ECUs and Tuning Discuss Engine Management, Tuning, & Programming

Arduino as ECU?

Thread Tools
 
Search this Thread
 
Old 10-16-2010, 12:33 AM
  #121  
Boost Pope
iTrader: (8)
 
Joe Perez's Avatar
 
Join Date: Sep 2005
Location: Chicago. (The less-murder part.)
Posts: 33,038
Total Cats: 6,604
Default

I'm going to be generous tonight.

Originally Posted by josdavlar
could you clarify what the CAS is and what that black and white disk is?
CAS is shorthand for Cam Angle Sensor.

It tells the ECU the position of one of the camshafts. If you know what the position of the cam is, then you can determine what the position of the crank is. These two pieces of information are used to determine when to fire the spark plugs and the fuel injectors.

Do you know what the points and rotor were for on a distributor? Same basic idea, just with a computer in the middle.
Joe Perez is offline  
Old 10-16-2010, 12:43 AM
  #122  
Newb
 
josdavlar's Avatar
 
Join Date: Oct 2010
Posts: 6
Total Cats: 0
Default

sorry.

for anyone else CAS = crank angle sensor

i joined because bloodline is the only person on the interwebs i could find who's interested in using an arduino as an ECU/EFI controller. why not use Megasquirt? because i'm convinced there's a lower cost option for people like me who are interested in simply converting an older carb'ed vehicle to EFI. plus it's kinda fun.

i think an open source, straightforward EFI conversion could really help out everyone who's running a carb. it might also save some great old vehicles from the junkyard. clearly the hardware side of things (intake, injectors, O2 sensor, etc) complicates things, but perhaps it's less daunting when you're armed with the source code and a list of needed electronic components.

Megasquirt seems to have a really nice product, but they appear to be inclined pretty hard toward tuners. as such, their product line is extensive and frankly, difficult to wade through.

so, i'm sorry i'm using your forum this way, but i have good intentions...
josdavlar is offline  
Old 10-16-2010, 12:51 AM
  #123  
Newb
 
josdavlar's Avatar
 
Join Date: Oct 2010
Posts: 6
Total Cats: 0
Default

sorry, i defer to Joe Perez on the CAS definition.

a hall effect sensor interfaces very easily with an arduino and costs less than $1. actually, the miata's CAS is likely some breed of hall effect sensor if i had to guess.

mount a magnet on the flywheel or some other timed component, mount the sensor and adjust the code to suit. easy peasy.
josdavlar is offline  
Old 10-16-2010, 12:52 AM
  #124  
Boost Pope
iTrader: (8)
 
Joe Perez's Avatar
 
Join Date: Sep 2005
Location: Chicago. (The less-murder part.)
Posts: 33,038
Total Cats: 6,604
Default

Originally Posted by josdavlar
for anyone else CAS = crank angle sensor
That's also the common interpretation in the Miata community. It's wrong, because it doesn't sense the crank position, but it's a pretty meaningless differentiation. For the '90-'97 cars, the distinction is irrelevant. From '99 onwards, there were two separate sensors, one of which was mounted near the crank pulley, and the other still up on the cam.


so, i'm sorry i'm using your forum this way, but i have good intentions...
Don't sweat it. All these lonely homosexual deviants need someone to pick on.
Joe Perez is offline  
Old 10-16-2010, 12:56 AM
  #125  
Boost Pope
iTrader: (8)
 
Joe Perez's Avatar
 
Join Date: Sep 2005
Location: Chicago. (The less-murder part.)
Posts: 33,038
Total Cats: 6,604
Default

Originally Posted by josdavlar
a hall effect sensor interfaces very easily with an arduino and costs less than $1. actually, the miata's CAS is likely some breed of hall effect sensor if i had to guess.
Internally, there were two versions. One used phototransistors, the other VR sensors. But they both had open-collector outputs, so they are functionally equivalent to what you'd see on a hall-effect sensor.


mount a magnet on the flywheel or some other timed component, mount the sensor and adjust the code to suit. easy peasy.
Don't even need a magnet. The '99 and later cars all had a plate behind the crank pulley and a VR sensor (again, with open-collector output) pointed at it.

EDIT: the '96-'97 cars also had a crank sensor, however it was used only for OBD-II misfire detection. They still had the same two-channel cam-mounted sensor as the earlier cars. Also, this sensor didn't have a logic-level driver- it provided a raw AC output.
Joe Perez is offline  
Old 10-16-2010, 01:58 AM
  #126  
Junior Member
Thread Starter
iTrader: (3)
 
ctxspy's Avatar
 
Join Date: Jun 2008
Location: NJ
Posts: 428
Total Cats: 0
Default

Originally Posted by josdavlar
sorry.

for anyone else CAS = crank angle sensor

i joined because bloodline is the only person on the interwebs i could find who's interested in using an arduino as an ECU/EFI controller. why not use Megasquirt? because i'm convinced there's a lower cost option for people like me who are interested in simply converting an older carb'ed vehicle to EFI. plus it's kinda fun.

i think an open source, straightforward EFI conversion could really help out everyone who's running a carb. it might also save some great old vehicles from the junkyard. clearly the hardware side of things (intake, injectors, O2 sensor, etc) complicates things, but perhaps it's less daunting when you're armed with the source code and a list of needed electronic components.

Megasquirt seems to have a really nice product, but they appear to be inclined pretty hard toward tuners. as such, their product line is extensive and frankly, difficult to wade through.

so, i'm sorry i'm using your forum this way, but i have good intentions...
sniff sniff, he's not the only one.. i started this thread lol!

I agree with everything most of what you've stated above. To be fair, MS sourcecode is available, there's a pretty big following, and there are people who are willing to build your **** for you if you're like me and can't hack it!

To me the arduino was interesting because now the hardware could be more open, the software would be written in a higher level /more accessible language, and the arduino seems to have more in terms of add-ons which could really enhance the functionality and provide new features we haven't even thought of yet.

Maybe if bloodline is able to put something workable together, we could persuade Joe to spend a little time on this project. (I've seen some threads where he shares his dissatisfaction with some aspects of the MS world )
ctxspy is offline  
Old 10-16-2010, 06:58 AM
  #127  
Junior Member
 
bloodline's Avatar
 
Join Date: Sep 2010
Location: London, England
Posts: 91
Total Cats: 0
Default

I built my own "CAS" so I could test my timing data and just to make sure I had a good understanding of the mechanicals of this project. The last black and white disk I posted, was cut so that the white areas became holes in the disk. I then mounted two LEDs and two LDRs either side of the disk at the TDC position ( one lined up with the top set of holes the other lined up with the bottom set). When the disk is rotated (anticlockwise from the picture you can see), Then the LDRs send the same signals as a real Miata CAS.


@josdavlar
If you try and use my 0.4 code that I posted previously, you will probably find the injector times are too quick. I had a maximum duty cycle algorithm in the old code that didn't really work, I have since rebuilt that section, and the code can now hold the injectors open for almost an entire engine cycle (648 degrees of crank rotation (90%), which isn't very long at 8000RPM -EDIT-~12microseconds).

If you plan to convert an old Carbed engine to EFI, then I would suggest you use a single point injection, in place of the Carb... if you use my code, just use a single injector output from the Arduino, and adjust the duty cycle accordingly ( it will need to be 4 times what the Miata will use). I would be happy to adjust the code for you at a later time to make that work a bit better.

BTW, we still need someone to build a "Shield" to take the Engine sensor inputs and drive the Ignitor/Injector outputs.

Once I have finished getting my injector code to work properly, I will start work on it, but that maybe a while.

Last edited by bloodline; 10-16-2010 at 10:58 AM. Reason: added some data :)
bloodline is offline  
Old 10-16-2010, 10:24 AM
  #128  
Junior Member
 
bloodline's Avatar
 
Join Date: Sep 2010
Location: London, England
Posts: 91
Total Cats: 0
Default

Ok, v0.5 works now

Here is the code:
Code:
// MiataBrain1 ECU V0.5
// 
// Copyright (c)2010 Matt Parsons
//
// Mazda MX5 Engine Control Unit Software for the Arduino Microcontroller Board.
// 
// Use 8bit variables where ever possible to speed processing.
// The basic unit of time for the sequence is the Microsecond (unit written as "us"), but I have used
// a 128us timer for all time critical functions. This is due to a limitation in the AVRmega328 CPU
// used in the Arduino (what do you want from a $2 chip?). This is doesn't cause any real issues 
// but be sure to remember than events might occur 128us later than requested. :)  

// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

// We need includes for the 128us timer
#include <avr/interrupt.h>  
#include <avr/io.h>

unsigned long hRev; //number of half crank revolutions. x2 for engine life time crank revs.

// Cycle Time Variables
unsigned long MSPT;
unsigned long MSLastFallingEdge;
unsigned long MSLastRisingEdge;
byte Phase;
unsigned long segTime;

typedef struct{
byte PinCrank;
byte PinCam;
byte PinIgnA;
byte PinIgnB;
byte PinInj1;
byte PinInj2;
byte PinInj3;
byte PinInj4;
} ECUHW;

//Board pin assignment
/* Pins 1 and 2 are for the USB link, if you don't want/need that then you get two extra pins.

  Defaults here:  2=CKP input from Engine
                  3=CMP input from Engine
                  4=Ignitor1 and 4
                  5=Ignitor2 and 3
                  6=Injector Cylinder 1
                  7=Injector Cylinder 2
                  8=Injector Cylinder 3
                  9=Injector Cylinder 4

Adjust these below if your hardware needs them on different pins :)
*/

ECUHW board ={2,3,4,5,6,7,8,9};


//Iginition Variables, you can adjust the firing sequence here
byte IgNActive[]={board.PinIgnB,board.PinIgnA,board.PinIgnB,board.PinIgnA};

//Injector Variables, You can adjust the injector sequence here
byte InJActive[] = {board.PinInj4,board.PinInj2,board.PinInj1,board.PinInj3};
unsigned long InjFlowTime;

//Engine Sensor variables
int ThrottlePositionSensor;
int AirFlowSensor;
int AirTemp;
int CoolentTemp;

//Engine Control Variables
//Here we can control the various emission valves etc...
int throttleServoPosition; // in microseconds. on a regular MX5 this would be the ISC valve.

//User Variables, these will also be adjustable in real time via serial link. I think I will save these value in te EEPROM.
unsigned int User_DwellTime=2500;//2.5 milliseconds default (CANNOT BE NEGATIVE).
        long User_AdvanceRetard=0; //Advance retard accross the RPM band. ticks.
        long User_Mixture=0; // adjust the fuel mixture, rich/lean. +-ms
        long User_RevLimit=8000; //Rev limiter... not used at this time.

//Countdowns for the interrupt.
unsigned long dwellStartCountdown=-1;
unsigned long dwellEndCountdown=-1;
unsigned long InjectorCounter=0;

byte ignLive=0; //Just a little check flag to make sure we can't burn out the coils... just being a bit over cautious here.


unsigned long Inj1OnTime;  //Start time of inj1
unsigned long Inj1OffTime;
unsigned long Inj2OnTime;  //Start time of inj2
unsigned long Inj2OffTime;
unsigned long Inj3OnTime;  //Start time of inj3
unsigned long Inj3OffTime;
unsigned long Inj4OnTime;  //Start time of inj4
unsigned long Inj4OffTime;

//Timer2 overflow interrupt vector handler, called every 128us
ISR(TIMER2_OVF_vect) {
  dwellStartCountdown-=128;
  dwellEndCountdown-=128;
  
    InjectorCounter++;
  
  if(dwellStartCountdown<128){IgnitorOn();ignLive=1;}
  if(dwellEndCountdown<128){IgnitorOff();ignLive=0;}
 
 
  if(InjectorCounter==Inj1OnTime){digitalWrite(InJActive[2],HIGH);}
  if(InjectorCounter==Inj1OffTime){digitalWrite(InJActive[2],LOW);}  
  
  if(InjectorCounter==Inj2OnTime){digitalWrite(InJActive[1],HIGH);}
  if(InjectorCounter==Inj2OffTime){digitalWrite(InJActive[1],LOW);} 
  
  if(InjectorCounter==Inj3OnTime){digitalWrite(InJActive[3],HIGH);}
  if(InjectorCounter==Inj3OffTime){digitalWrite(InJActive[3],LOW);} 
  
  if(InjectorCounter==Inj4OnTime){digitalWrite(InJActive[0],HIGH);}
  if(InjectorCounter==Inj4OffTime){digitalWrite(InJActive[0],LOW);} 
  
};  

void setup(){

//Set up the 128us timer, this will run the injectors and start/end the coil dwell
  //Timer2 Settings: Timer Prescaler /128, WGM mode 0
  TCCR2A = 0;
  TCCR2B = 0<<CS22 | (1<<CS21) | (0<<CS20);
  //Timer2 Overflow Interrupt Enable  
  TIMSK2 = 1<<TOIE2;
  //reset timer
  TCNT2 = 0;
  
// set ADC prescale to 16, 21us per sample, without this the ADC will run too slowly
  sbi(ADCSRA,ADPS2) ;
  cbi(ADCSRA,ADPS1) ;
  cbi(ADCSRA,ADPS0) ;
  
  Serial.begin(115200);
  Serial.print("\n!!!!System Boot!!!!\nMiataBrain1 ECU v0.5\nCopyright (c)2010 Matt Parsons,\nAll rights reserved.\n\nMazda MX5 Engine Control Unit Software for\nthe Arduino Microcontroller Board.\nPlease see GPL for licence restrictions.\n!!!!!!!!!!!!!!!!!!!\n\nSynchronising with Engine!\n");
  
  
pinMode(board.PinCam,INPUT);  
pinMode(board.PinCrank,INPUT);  

pinMode(board.PinIgnA,OUTPUT);  
pinMode(board.PinIgnB,OUTPUT);
pinMode(board.PinInj1,OUTPUT);  
pinMode(board.PinInj2,OUTPUT);  
pinMode(board.PinInj3,OUTPUT);  
pinMode(board.PinInj4,OUTPUT);  

pinMode(13,OUTPUT); //Just the on board LED so I can see the CKP cycles, please remove if you need to use this pin

//The Engine needs to be turing over on the starter by this point.
//Time to Sync with the Engine with the Arduino...
//You might want to switch on the fuel injectors here to fill the manifold with fuel.
//

Phase=1; //default Phase state (if we leave the sync at half way through an engine cycle) 

Serial.println("Wait for the Cam");
while(digitalRead(board.PinCam)==LOW){}

Serial.println("Wait for the Crank");
while(digitalRead(board.PinCrank)==LOW){}
MSPT=micros(); //Save the current time somewhere... 

Serial.println("Wait for the Crank signal to go low");
while(digitalRead(board.PinCrank)==HIGH){}
MSLastFallingEdge=micros();
MSPT=(micros()-MSPT)/100;

Serial.println("We are now synchronised");
}

void loop(){
  //Serial.println("Falling edge of Crank signal");
    segTime=micros();
  
  //Advance the phase... and check the cam... if HIGH, then we are back to Cylinder 1 TDC
    Phase++;
    if(digitalRead(board.PinCam)==HIGH){Phase=0;hRev++;hRev=hRev>>0;}
  
  //Calculate RPM... Microseconds per 512th of a revolution in our case
    MSPT =(segTime - MSLastFallingEdge) >> 8;
    MSLastFallingEdge = segTime;
   
    //calculate dwellend time; this gets revised on the rising edge sparkTicks()
       dwellEndCountdown=(sparkTicks()+155)*MSPT;
        
    //calculate dwellstart time: (dwellend-User_DwellTime)
      dwellStartCountdown=dwellEndCountdown-User_DwellTime;
      
    //Set up the injector timers, this is a bit clumsy... but it works and is very quick, will probly move to a switch strucutre.  
    if(Phase==0){
     Inj3OffTime=InjectorCounter+((922*MSPT) >> 7);
     Inj3OnTime=(Inj3OffTime-(InjFlowTime >> 7));
   }
    if(Phase==1){
     Inj4OffTime=InjectorCounter+((922*MSPT) >> 7);
     Inj4OnTime=(Inj4OffTime-(InjFlowTime >> 7));
   }
    if(Phase==2){
     Inj2OffTime=InjectorCounter+((922*MSPT) >> 7);
     Inj2OnTime=(Inj2OffTime-(InjFlowTime >> 7));
   }
     if(Phase==3){
     Inj1OffTime=InjectorCounter+((922*MSPT) >> 7);
     Inj1OnTime=(Inj1OffTime-(InjFlowTime >> 7));
   }
      
    //calculate injector flow time, I might limit this once per engine cycle... but we need to test to see what works best.
      setInjFlowTime();
      
    //Aquire analog signals...  I might limit this once per engine cycle... but we need to test to see what works best.
      aquireAnalog();
      
    //Serial link - TODO: Need to set up ring buffer...
      dataLink();
    
  //Wait for rising edge of Crank signal
    while(digitalRead(board.PinCrank)==LOW){} 
////////////////////////////////////////////////////////////////////////
    segTime=micros();
    
    //Calculate RPM... Microseconds per 512th of a revolution in our case
    MSPT =(segTime - MSLastRisingEdge) >> 8;
    MSLastRisingEdge = segTime;
          
    //calculate dwellend time; this is a revised time based on the rising edge, 
    //if the engine has sped up or slowed down since the falling edge, this will adjust the spark.
    //accuracy here is VERY improtant!
    dwellEndCountdown=(sparkTicks())*MSPT;
      
                                              digitalWrite(13,HIGH); //just an LED signal for me please remove
 //Wait for Falling Edge of Crank signal... to start the next phase, Yay!! We made it!
  while(digitalRead(board.PinCrank)==HIGH){}   
 
  if(ignLive==1){Serial.print("MISFIRE");error();} // If this is true, then the spark has missed the deadline This IS the end of the world :(
                                              digitalWrite(13,LOW); //just an LED signal for me please remove
//////////////////////////////////////////////////////////////////////// 
}

  unsigned long sparkTicks(){
    return 92+User_AdvanceRetard; //92 is the number of ticks after the rising edge for 10Deg BTDC
  }
  
  void IgnitorOn(){
        digitalWrite(IgNActive[Phase],HIGH);
  }
    
    void IgnitorOff(){
        digitalWrite(IgNActive[Phase],LOW);
  }
  
      
  void setInjFlowTime(){
    //Here is where we work out how long the injector need to open
    //this will be based on Throttle, AirFlow, AirTemp, coolent temp etc...
    InjFlowTime=2000+User_Mixture; //2 milliseconds, as a default... run a real engine with closed throttle and adjust until you get the expected idle RPM.
  }
  
    void aquireAnalog(){
    //We read them, but don't use them yet
    ThrottlePositionSensor=analogRead(0);
    AirFlowSensor=analogRead(1);
    AirTemp=analogRead(2);
    CoolentTemp=analogRead(3);
  }
  
    void dataLink(){
    // Just a stub for now
    }
  
  void error(){ //I will need to put some proper error codes here
       Serial.print("Error!");
     //Switch Everything off
     digitalWrite(IgNActive[0],LOW);
     digitalWrite(IgNActive[1],LOW);
     digitalWrite(IgNActive[2],LOW);
     digitalWrite(IgNActive[3],LOW);
     digitalWrite(InJActive[0],LOW);
     digitalWrite(InJActive[1],LOW);
     digitalWrite(InJActive[2],LOW);
     digitalWrite(InJActive[3],LOW);
       
    while(true){} //Into a busy loop, wait for a reset.
  }
This code, should be able to hold an engine at idle. I need someone to test this code, to make sure my injector phases are correct!

Please note the variables:
Code:
unsigned int User_DwellTime=2500;//2.5 milliseconds default (CANNOT BE NEGATIVE).
        long User_AdvanceRetard=0; //Advance retard accross the RPM band. measured in rotation ticks (setting this to 6, will perform the 14degree advance mod ;) ).
        long User_Mixture=0; // adjust the fuel mixture, rich/lean. +- us
        long User_RevLimit=8000; //Rev limiter... not used at this time.
These apply gross adjustments to the engine settings, and will be the first user adjustable settings accessible via the serial interface, for adjustment while engine is running. I have the option to save them in the EEPROM if people would like that, this will save the settings so they will survive power off


Note: To convert degrees into Rotational Ticks use this formula: (degree/360)*512
So to advance the the standard timing from 10deg BTDC to 14deg BTDC, we work out how many Rotational Ticks there are in 4 degrees ( 4/360)*512 = 5.7 )... My code uses whole numbers so we round that up to 6 (or down to 5 to be safe). Thus the User_AdvanceRetard variable needs to be 6.


What I will do now is fit in the Injector time calculation+tables (also the spark advance table as well), then we should have something usable in a real engine.


Edit:

Below is my Arduino running LEDs in place of Ignitors/Injectors. The board next to it with the blue LEDs is my powerful ARM based "mbed" microcontroller, that is generating the CKP and CMP signals.

Last edited by bloodline; 10-16-2010 at 01:15 PM. Reason: Added picture of Arduino
bloodline is offline  
Old 10-16-2010, 01:47 PM
  #129  
Newb
iTrader: (1)
 
kakarot's Avatar
 
Join Date: Sep 2010
Posts: 27
Total Cats: 0
Default

if only this was a year ago. :( I had to make ecu out of arduino too.
http://polysae.poly.edu/
kakarot is offline  
Old 10-16-2010, 01:49 PM
  #130  
Senior Member
iTrader: (7)
 
Marc D's Avatar
 
Join Date: Jul 2007
Location: Milpitas, CA
Posts: 1,047
Total Cats: 1
Default

Interesting... Subscribed
Marc D is offline  
Old 10-16-2010, 03:40 PM
  #131  
Boost Pope
iTrader: (8)
 
Joe Perez's Avatar
 
Join Date: Sep 2005
Location: Chicago. (The less-murder part.)
Posts: 33,038
Total Cats: 6,604
Default

Originally Posted by josdavlar
why not use Megasquirt? because i'm convinced there's a lower cost option for people like me who are interested in simply converting an older carb'ed vehicle to EFI.
This is the part I don't understand.

Let's assume that you value your time at $0.

The 68HC908GP32 CPU used in the MS1 design costs $18 in qty 1. The Arduino Uno board seems to sell for around $30-$35. Granted, the Uno has an on-board power regulator ($0.60 for an LM7805, plus a few passives), an on-board clock crystal ($0.28) and an on-board serial interface ($1.16). So assume the lower Arduino price ($30), and the equivalent MS1 microcontroller package is $9.96 cheaper. All other costs (board fab, sensors, connectors, drivers, etc) should remain a constant.

Obviously, this assumes that you don't mind violating B&G's purported (and increasingly hazy) patent / copyright claims on the system. Personally, I'm half-tempted to buy one of their sacred new MS3Xs, reverse engineer it, and then commission some Chelsea artist to incorporate the resultant schematic into a postmodernist painting which purports to criticize the oppression of the 11th Panchen Lama Gedhun Choekyi Nyima by the Chinese communist government, and can thus subsequently be displayed for all the world to see on the grounds of Political Speech.

Not that I'd ever do anything even remotely resembling this, of course. That would be wrong, and it would cause harm to the Megasquirt project as a whole.

But I digress.


Don't get me wrong, I think this concept is very cool. I'm not a software guy, so I kind of admire the sort of ingenuity it takes to put together any kind of functioning realtime control system. I just don't buy the money argument.



Originally Posted by ctxspy
Maybe if bloodline is able to put something workable together, we could persuade Joe to spend a little time on this project. (I've seen some threads where he shares his dissatisfaction with some aspects of the MS world )
Dissatisfaction? I can't imagine what you'd be referring to!

In all seriousness, I genuinely respect all of the work that the many people within the MS community have done, most of it for zero or very little financial compensation, in both the software and hardware design not just of the MS1 and 2, but also the current MS3 and even the various oddball projects that have come and gone over the years. Without their work and dedication, the automotive aftermarket would still suffer under the brutal, closed-source oppression of the very few mainstream commercial manufacturers who have chosen to produce and sell programmable ECUs, generally at exorbitant prices and with little or no publicly available documentation as to the inner workings of their systems. These people have all done a great service to the world, and should be praised for their efforts.

I do have a bit of a specific grievance with the paranoid and ultimately futile attitude that Bruce Alan Bowling of 552 Franklin St., Havre de Grace, MD 21078 and Albert Carlos Grippo of 1213 Exeter Landing, Virginia Beach, VA 23464 have of late adopted with regard to the publication (or lack thereof) of the schematic diagrams for the MS3 processor and MS3X expansion module, prompted by their entirely reasonable and justifiable fear that the design of same will be cloned by third-party manufacturers, but this should in no way be construed as reflecting criticism of the design of any of the Megasquirt family of products.

(Feel free to mail a coconut to either Mr. Bowling or Mr. Grippo as a means of expressing your feelings on the aforementioned matter. You might choose to write the phrase "Free the MS3 Schematics!" on the outside of the coconut, provided of course that such a sentiment is reflective of your own personal opinion on the matter.)

I wholeheartedly support and endorse the legitimate use of properly licensed Megasquirt ECUs, distributed by authorized vendors, for any and all applications which involve the spark-initiated combustion of gasoline or ethanol-based fuels in an internal combustion engine.



That being said, I'd be happy to bang together some hardware so that this thing can actually talk to a car.

Last edited by Joe Perez; 12-20-2011 at 02:11 AM.
Joe Perez is offline  
Old 10-16-2010, 03:54 PM
  #132  
Junior Member
 
bloodline's Avatar
 
Join Date: Sep 2010
Location: London, England
Posts: 91
Total Cats: 0
Default

Originally Posted by Joe Perez
This is the part I don't understand.

Let's assume that you value your time at $0.

The 68HC908GP32 CPU used in the MS1 design costs $18 in qty 1. The Arduino Uno board seems to sell for around $30-$35. Granted, the Uno has an on-board power regulator ($0.60 for an LM7805, plus a few passives), an on-board clock crystal ($0.28) and an on-board serial interface ($1.16). So assume the lower Arduino price ($30), and the equivalent MS1 microcontroller package is $9.96 cheaper. All other costs (board fab, sensors, connectors, drivers, etc) should remain a constant.

...

Don't get me wrong, I think this concept is very cool. I'm not a software guy, so I kind of admire the sort of ingenuity it takes to put together any kind of functioning realtime control system. I just don't buy the money argument.
Well, the Arduino is also a prototyping board, a USB interface and an in circuit programmer... once the AVRMega328 ($2 chip) is programmed, you could pop it on your own custom board (with your own voltage regs and crystal), if you are good with hardware


That being said, I'd be happy to bang together some hardware so that this thing can actually talk to a car.
I would be happy to release a version of the code under whatever licence you require for you to use in a project. But I would prefer something closer to GPL so that any improvements are released back to the community.
bloodline is offline  
Old 10-16-2010, 05:30 PM
  #133  
Newb
iTrader: (1)
 
kakarot's Avatar
 
Join Date: Sep 2010
Posts: 27
Total Cats: 0
Default my ecu code

here is my code that I used on my ecu project. Its made for single cylinder, Air assisted direct injection. I had another code that could multitask and keep timing and injection going. Later on I rewrote the code to be single task, aka this code.
About this code:
eeprom was too small to keep my fuel injection table, so I put it into the flash memory. There is no maf/map, since I calibrated everything based on throttle position and rpm. The throttle response is much superior with this method over maf/map. The AFR in this model was between 19-16:1, yep super lean. in this particular program, whenever the cam sensor is triggered it injects air, followed by fuel. I put the trigger point such that it injects air at the low pressure of the intake stroke. I have another program that worked, that kept the timing and you could inject fuel or air at any time.

Theoretical rpm limit for this program was around 11K rpm, above that it requires to multitask instead of single task.

Problems with this kind of stuff is high noise that can easily freak out the arduino's interrupts. That's why i had to do hardware filtering of the noise. I put in low pass filter and added diode to prevent back emf. I had a spike in the signal, but with the filtering, it turned it into a square wave, just like simulations.

essentials:
Inputs: Cam position, throatle position.
Output: Fuel injector, Air injector, throttle.

Believe it or not, the code never made the engine backfire.

Personal approach:
I found it easier to have the fuel matrix over fuel equations. It much easier to schedule the events. So in the code, from inputs it would figure out when to inject, spark. That would be put into variables. The rest of the program, when not doing the calculations, can run the schedule. There is no need to wait to close the injector, or turn off the spark. All that can be put into schedule and the program would follow the whole thing. In my program, it does wait to finish, but there are gaps in time where it can do other junk.

The project that I made the ecu for:
http://polysae.poly.edu



Code:
#include <Servo.h>
#include <avr/pgmspace.h>

//Servo range closed 63-155 open
PROGMEM  prog_uint16_t fuelm[32][15]={
{100,4200,5200,5500,5500,5500,6000,6200,6500,6700,6900,7100,7300,7500,8000},
{100,4000,5000,5400,5400,5400,5800,6000,6300,6500,6700,6900,7100,7300,8000},
{100,4000,5000,5300,5300,5300,5600,5800,6100,6300,6500,6700,6900,7100,8000},
{100,4000,5000,5200,5200,5200,5600,5800,5900,6100,6300,6500,6700,6900,8000},
{100,4000,4800,5100,5100,5100,5500,5800,5900,6100,6300,6500,6700,6900,7500},
{100,4000,4800,5000,5000,5000,5500,5800,5900,6100,6300,6500,6700,6900,7500},
{100,4000,4800,4900,4900,5000,5500,5800,5900,6100,6300,6500,6700,6900,7500},
{100,3900,4600,4700,4800,5000,5500,5800,5900,6100,6300,6500,6700,6900,7500},
{100,3900,4600,4500,4700,5000,5500,5800,5800,6000,6200,6400,6600,6800,7500},
{100,3900,4600,4400,4700,4900,5400,5700,5800,5950,6100,6250,6400,6550,7500},
{100,3900,4600,4400,4700,4900,5400,5700,5800,5950,6100,6250,6400,6550,7500},
{100,3900,4600,4400,4600,4900,5400,5700,5800,5950,6100,6250,6400,6550,7500},
{100,3900,4400,4400,4600,4800,5400,5700,5500,5650,5800,5950,6100,6250,7000},
{6000,3800,4400,4400,4600,4800,5300,5500,5500,5650,5800,5950,6100,6250,7000},
{6000,3800,4400,4400,4500,4800,5300,5500,5500,5650,5800,5950,6100,6250,7000},
{6000,3800,4400,4400,4500,4800,5300,5500,5300,5450,5600,5750,5900,6050,7000},
{6000,3800,4200,4200,4500,4700,5300,5500,5300,5450,5600,5750,5900,6050,7000},
{6000,3800,4000,4200,4500,4700,5200,5400,5200,5350,5500,5650,5800,5950,7000},
{6000,3700,3900,4200,4400,4700,5150,5350,5250,5350,5450,5600,5650,5750,7000},
{6000,3700,3800,4200,4400,4700,5050,5250,5150,5250,5350,5550,5600,5700,7000},
{6000,3700,3800,4200,4400,4700,5000,5200,5100,5200,5300,5550,5550,5650,7000},
{6000,3700,3800,4000,4400,4600,4950,5150,5150,5250,5350,5650,5600,5700,7000},
{6000,3700,3700,4000,4300,4500,5000,5100,5200,5300,5400,5800,5700,5800,7000},
{6000,3600,3700,4000,4300,4500,5000,5100,5200,5300,5400,5800,5700,5800,6500},
{6000,3600,3700,4000,4300,4500,5000,5100,5100,5200,5300,5700,5600,5700,6000},
{6000,3600,3700,3900,4200,4400,4750,4850,4950,5050,5150,5450,5400,5500,5500},
{6000,3600,3500,3800,4100,4300,4500,4600,4700,4750,4800,5000,4950,5000,5000},
{6000,3600,3300,3800,4000,4200,4300,4400,4600,4650,4700,4900,4850,4900,5000},
{6000,3500,3100,3800,3900,4100,4050,4150,4450,4500,4550,4700,4700,4750,5000},
{5500,3400,2800,3400,3800,4000,3850,3950,4350,4400,4450,4600,4600,4650,5000},
{5000,3100,2600,3200,3800,3800,3650,3650,4150,4200,4250,4350,4350,4400,5001},
{4500,3000,2300,2600,2800,2300,2400,100,100,100,100,100,100,100,100}};


int rpmcount = 0;
int airopen=7400, fuel=2800;
byte temp=0,throatle=63,t1,t2,inj=0;
unsigned long time, dt,wheel,vel=0,trip=0,rpm=0;
boolean aire=0,fuele=0,spd=0;
const byte AirValvePin = 6;
const byte FuelValvePin = 7;
const byte thrin=1;
Servo thr;

void setup()
{
  Serial.begin(9600);
  thr.attach(9);
  pinMode(AirValvePin, OUTPUT);
  pinMode(FuelValvePin, OUTPUT);

  digitalWrite(FuelValvePin, LOW);
  digitalWrite(AirValvePin, LOW);
  clearLCD();
  Serial.print("Alive");
}

void loop()
{
  if (aire){    
    digitalWrite(AirValvePin, HIGH);
    delayMicroseconds(airopen);
    digitalWrite(AirValvePin, LOW);
    aire=false;
  }

  if (!aire && fuele){    
    digitalWrite(FuelValvePin, HIGH);
    delayMicroseconds(fuel-100);
    digitalWrite(FuelValvePin, LOW);
    fuele=false;
    //LCD print out    
    clearLCD();
    /*Serial.print(t1, DEC);
    Serial.print("_");
    Serial.print(t2,DEC);
    Serial.print("F:");
    Serial.print(fuel-100,DEC);
    //selectLineTwo();
    Serial.print(rpm,DEC);
    Serial.print(" SPD");*/
    Serial.println((vel/10));    
  }

  if (!aire && !fuele) {
    temp=throatle;
    throatle=constrain(map(analogRead(thrin),0,600,64,155),64,155);
    if (temp!=throatle)
      thr.write(throatle);

    //cam sensor
    if (inj>0)
      if (analogRead(0)<400)
        inj=0;

    if (inj==0) {
      if (analogRead(0)>400) 
        inj=1;
      rpmcount = rpmcount+inj;
    }


    // Speed sensor
    if (spd)
      if (analogRead(4)>250)
        spd=false;

    if (!spd)
      if (analogRead(4)<250) {
        vel=32950/(millis()-wheel);  //mph x10
        wheel=millis();
        spd=true;
        trip++;
      }
  }

  if (rpmcount!=3) 
  {
    dt=micros() - time;
    rpmcount = (rpmcount > 3)+2*(rpmcount != 0);
    time = micros();

    if ((dt>20000) && (dt<1200000) && (rpmcount==3)) {  //Operational Condition test 100 to 6000 rpm range
      aire=true;
      fuele=aire;
      rpm=120000000/dt;
      t1=constrain((157-throatle)/3,0,31);
      t2=(constrain((rpm-1100)/350,0,14))*(rpm>1101);
      fuel=pgm_read_word(&fuelm[t1][t2]); 
    }
  }

}
void contactbreaker_fun() //Each two rotation, this interrupt function is run once
{
  rpmcount = rpmcount+1;
}

void selectLineTwo(){  //puts the cursor at line 0 char 0.
  Serial.print(0xFE, BYTE);   //command flag
  Serial.print(192, BYTE);    //position
}
void clearLCD(){
  Serial.print(0xFE, BYTE);   //command flag
  Serial.print(0x01, BYTE);   //clear command.
}
kakarot is offline  
Old 10-18-2010, 05:23 PM
  #134  
Junior Member
 
bloodline's Avatar
 
Join Date: Sep 2010
Location: London, England
Posts: 91
Total Cats: 0
Default

Ok ctxspy, this one is for you

I have built my first go at the serial data link. This is based on an old protocol that I used on another project.

This whole thing is open to discussion, and no doubt we will need to adjust it after some real world trials

First things first, the ECU is "speak when spoken to".
All communication is initiated by the computer to the ECU.
Also so keep the buffers small, once the computer has sent a command, the computer must wait for an acknowledgement from the ECU before sending any more commands.

There are two levels to the protocol, the high level protocol is for adjusting the settings and reading data from the ECU. The low level protocol is to get the error/status code of the ECU, this runs in the interrupt so will respond no matter what the ECU is doing

Ok...

Lowlevel:

If the computer sends a single byte "05", the ECU will respond with a single byte error code. I have 3 defined codes right now, we will add more as we go on:

0 = Everything Ok.
254 = Engine waiting Sync... Awaiting starter.
255 = Default starting condition... ECU not ready for engine.


Now the high level protocol:

All commands start with the byte 33, and must end with the byte 04. Once a command has been sent to the ECU, the computer much wait for a single byte with the value of 06 before sending any more commands. Any commands sent before receiving the 06, may be ignored (in reality they might be picked up, but who knows).

Commands:

Byte sequence: 33 63 65 04 - This is the request for all ECU settings, it returns a large 64 byte data structure (will explain more later) containing all ECU data, and terminated with a 06.

Byte sequence: 33 83 65 xx xx xx xx 04 - Sets the advance retard value, the "xx xx xx xx" represents the 4byte (32bit long signed) advance retard in microseconds against the factory setting.

Byte sequence: 33 83 77 xx xx xx xx 04 - Sets the mixture value, the "xx xx xx xx" represents the 4byte (32bit long signed) injector open time in microseconds against the factory setting.

Byte sequence: 33 83 68 xx xx xx xx 04 - Sets the dwell time, the "xx xx xx xx" represents the 4byte (32bit long unsigned) coil dwell time in microseconds. Cannot be a negative value.

Byte sequence: 33 83 76 xx xx xx xx 04 - Sets the rev limit, the "xx xx xx xx" represents the 4byte (32bit long unsigned) rev limit value. I have no idea if we ever need this

This is where I'm at so far...
bloodline is offline  
Old 10-19-2010, 12:13 PM
  #135  
Newb
iTrader: (1)
 
kakarot's Avatar
 
Join Date: Sep 2010
Posts: 27
Total Cats: 0
Default

make sure to disconnect Arduino's reset from the usb to serial converter. When ever you connect usb, it automatically resets arduino. has to be done physically.
kakarot is offline  
Old 10-19-2010, 07:06 PM
  #136  
Junior Member
 
bloodline's Avatar
 
Join Date: Sep 2010
Location: London, England
Posts: 91
Total Cats: 0
Default

Originally Posted by kakarot
make sure to disconnect Arduino's reset from the usb to serial converter. When ever you connect usb, it automatically resets arduino. has to be done physically.
Lol, yes, thought that is easy... IIRC you just cut the marked trace on the board.

And a little note for Joe, that you can buy Arduino clone boards on eBay for ~$10... perhaps you would give it a whirl?
bloodline is offline  
Old 10-19-2010, 11:25 PM
  #137  
Junior Member
Thread Starter
iTrader: (3)
 
ctxspy's Avatar
 
Join Date: Jun 2008
Location: NJ
Posts: 428
Total Cats: 0
Default

I saw your post regarding serial communications.

have you given any thought to datalogging? will you provide a mode that automatically dumps all the data at a regular rate, or will it be pull only?

For peace of mind, you may want to also send back the same message sent to the ECU, prefixed by the number of bytes to expect..

e.g. if i send you a 4 byte instruction, you would send back <06><04><[4 byte instruction as received by ECU]>.. kind of like a poor man's error check.
ctxspy is offline  
Old 10-20-2010, 12:09 AM
  #138  
Elite Member
iTrader: (12)
 
neogenesis2004's Avatar
 
Join Date: Aug 2006
Posts: 4,413
Total Cats: 20
Default

If you do a quick google search you can find a good amount of data on checksums and hashing for low power mcus. Nothing uber like a 128bit MD5 but better than nothing, and better than just returning the number 4.
neogenesis2004 is offline  
Old 10-20-2010, 03:40 AM
  #139  
Junior Member
 
bloodline's Avatar
 
Join Date: Sep 2010
Location: London, England
Posts: 91
Total Cats: 0
Default

Originally Posted by ctxspy
I saw your post regarding serial communications.

have you given any thought to datalogging? will you provide a mode that automatically dumps all the data at a regular rate, or will it be pull only?
With the limited amount of CPU cycles I can devote to serial communication, pull mode is the most efficient and reliable way to ensure that the send and receive buffers don't get full, they are only 128bytes on the arduino. The engine gets priority over everything else.

However, I agree that there does need to be a "datalogging" mode, which I think we could implement in the low-level protocol. I would prefer it to still use the pull method, but we could have a "keep transmitting until told to stop" mode... Not sure if I am totally comfortable with that though

For peace of mind, you may want to also send back the same message sent to the ECU, prefixed by the number of bytes to expect..
I agree the protocol isn't very robust, but (at least for now) I think the simple acknowledgement will be sufficient. Once I get real feedback as to how well the code performs in a real engine, then I'll know where I can optimise the engine control portion of the code... And free up CPU cycles for coms

e.g. if i send you a 4 byte instruction, you would send back <06><04><[4 byte instruction as received by ECU]>.. kind of like a poor man's error check.
I do like that, it's simple and doesn't add much over head at all... I'll add code to do that, but won't enable it on v0.1 of the protocol
bloodline is offline  
Old 10-20-2010, 03:45 AM
  #140  
Junior Member
 
bloodline's Avatar
 
Join Date: Sep 2010
Location: London, England
Posts: 91
Total Cats: 0
Default

Originally Posted by neogenesis2004
If you do a quick google search you can find a good amount of data on checksums and hashing for low power mcus. Nothing uber like a 128bit MD5 but better than nothing, and better than just returning the number 4.
A simple hash would be nice, I have thought of a few, but at the moment I'm really pushed for CPU time as I've had to be over cautious about the timings in the engine... You can see a few busy loops in the main loop, these hold the code until known points, once I get to try the code out I will then move the code that waits on the busy loops into some interrupts and that will free up a lot of time for communications.
bloodline is offline  


Quick Reply: Arduino as ECU?



All times are GMT -4. The time now is 08:33 AM.