Miata Turbo Forum - Boost cars, acquire cats.

Miata Turbo Forum - Boost cars, acquire cats. (https://www.miataturbo.net/)
-   ECUs and Tuning (https://www.miataturbo.net/ecus-tuning-54/)
-   -   Arduino as ECU? (https://www.miataturbo.net/ecus-tuning-54/arduino-ecu-50695/)

Joe Perez 10-16-2010 12:33 AM

I'm going to be generous tonight.


Originally Posted by josdavlar (Post 643927)
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.

josdavlar 10-16-2010 12:43 AM

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 10-16-2010 12:51 AM

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.

Joe Perez 10-16-2010 12:52 AM


Originally Posted by josdavlar (Post 643966)
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. :D

Joe Perez 10-16-2010 12:56 AM


Originally Posted by josdavlar (Post 643967)
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.

ctxspy 10-16-2010 01:58 AM


Originally Posted by josdavlar (Post 643966)
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 shit 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 :giggle: )

bloodline 10-16-2010 06:58 AM

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.

bloodline 10-16-2010 10:24 AM

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.
http://www.cutiemish.com/setup.JPG

kakarot 10-16-2010 01:47 PM

if only this was a year ago. :( I had to make ecu out of arduino too.
http://polysae.poly.edu/

Marc D 10-16-2010 01:49 PM

Interesting... Subscribed

Joe Perez 10-16-2010 03:40 PM


Originally Posted by josdavlar (Post 643966)
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. :D




Originally Posted by ctxspy (Post 643983)
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 :giggle: )

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.

bloodline 10-16-2010 03:54 PM


Originally Posted by Joe Perez (Post 644156)
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. :D

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. :)

kakarot 10-16-2010 05:30 PM

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

http://polysae.poly.edu/wp-content/g...251%20copy.jpg

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.
}


bloodline 10-18-2010 05:23 PM

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...

kakarot 10-19-2010 12:13 PM

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.

bloodline 10-19-2010 07:06 PM


Originally Posted by kakarot (Post 645386)
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? ;)

ctxspy 10-19-2010 11:25 PM

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.

neogenesis2004 10-20-2010 12:09 AM

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.

bloodline 10-20-2010 03:40 AM


Originally Posted by ctxspy (Post 645642)
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 10-20-2010 03:45 AM


Originally Posted by neogenesis2004 (Post 645655)
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.

ctxspy 10-20-2010 11:42 PM


Originally Posted by bloodline (Post 645078)
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.

I'm confused by this one.. what do you mean by injector open time? are you using the 2D array for lookup of fuel value, or are you still using the 1 value as a test for now?

I started putting together a program in C#.. once it does something i'll compile it and send it over to you for testing.

Tomaj

bloodline 10-21-2010 03:32 AM


Originally Posted by ctxspy (Post 646078)
I'm confused by this one.. what do you mean by injector open time? are you using the 2D array for lookup of fuel value, or are you still using the 1 value as a test for now?

Like most of the spec at this stage, this command might change :)

But regardless of the the fuel calculation (I will explain what I'm using as a test shortly), the end result is that the amount of fuel in the engine is determined by how long the injectors are open. Now the ECU will try to open the injectors for just the right amount of time to get the right amount of fuel in the engine for complete combustion (too little fuel and we start forming nitrous oxide, too much fuel and unburnt hydrocarbons get in to the exhaust). This command simply adds or subtracts the requested amount of microseconds from whatever the ECU has worked out that the injectors need to be open for... If you write a value of 1000, the injectors will be open for an extra millisecond (on top of what the ECU thinks should be) so the mixture will be rich, if you write a value of -2500, the injectors will be opened for 2.5milliseconds less than the ECU has worked out and the mixture will be lean. This is a gross adjustment and is basically like the choke on a carburettor :)


My fueling code at the moment assumes no loading on the engine, since it still hasn't been tested in a real engine, it is safe to say it's not going to be put in a car and driven around. This means the injector open time is a linear function of the AFM open amount (I.e. The amount of air introduced into the manifold).
So if the AFM is reading 0 volts, the injectors are open for 0 microseconds, if the AFM is reading 5volts (maximum air intake), the injectors are open for 12000microseconds (90% duty cycle at 8000RPM). It is a very simple way to do it, but should be sufficient for testing.

I started putting together a program in C#.. once it does something i'll compile it and send it over to you for testing.

Tomaj
That would be brilliant! I'll have to find a Windows machine to test it with :) (actually I think I have XP installed on bootcamp on my MacBook).

ctxspy 10-21-2010 07:09 PM

there's monomac... since this is a simple program porting may be easy. I also may look into doing this in java (But i hate programming in java, i can't get around to liking eclipse. any better simple IDEs?)

about the fueling "delta", i dont mean to be harsh but i don't see the value in that, may as well just leave that off until we can get the full 2D lookup working.

Also regarding AFM, i think most people around here are going to be using a MAP sensor. With the MS the MAP sensor is built into the unit, but for the arduino i guess we'll need an external one. Either way, the concept is the same (i think), higher voltage = higher pressure / more air.

ctxspy 10-21-2010 08:26 PM

1 Attachment(s)
OK the 1st version of "Arduiciu" is ready, it basically lists the serial ports available on the computer, lists common port speeds, allows you to open the serial port, has several non functioning buttons, and one button the "Get ECU status", which hypothetically will work.

I've uploaded it in a zip file, let me know if it works.

bloodline 10-22-2010 07:24 PM


Originally Posted by ctxspy (Post 646441)
there's monomac... since this is a simple program porting may be easy. I also may look into doing this in java (But i hate programming in java, i can't get around to liking eclipse. any better simple IDEs?)

I also dislike Java, I grew up on 68k Asm and C, used C++ for a few years and then after a lot of effort finally figured out Objective-C... And I tend to stick with Obj-C now... Though for low-level work C++ is better suited :)


about the fueling "delta", i dont mean to be harsh but i don't see the value in that, may as well just leave that off until we can get the full 2D lookup working.
For development we do need to be able to adjust all the settings, I agree that a working system should have no use for this parameter.


Also regarding AFM, i think most people around here are going to be using a MAP sensor. With the MS the MAP sensor is built into the unit, but for the arduino i guess we'll need an external one. Either way, the concept is the same (i think), higher voltage = higher pressure / more air.
As I said before, I don't want to get carried away... First goal is to make a plugin replacement for the factory ECU. Once it works, then changing the code to support the MAP sensor over the AFM, should be as simple as wiring the MAP in place of the AFM and the loading the correct pressure/fuel map in :)

bloodline 10-22-2010 07:26 PM


Originally Posted by ctxspy (Post 646459)
OK the 1st version of "Arduiciu" is ready, it basically lists the serial ports available on the computer, lists common port speeds, allows you to open the serial port, has several non functioning buttons, and one button the "Get ECU status", which hypothetically will work.

I've uploaded it in a zip file, let me know if it works.

Brilliant! I'll give this a go this weekend :)

Really excited!!!

ctxspy 10-22-2010 10:30 PM

1 Attachment(s)
I've updated the code, it now implements all the messages defined thus far.

I've also zipped up the source code as well. The executable is in the bin/debug directory.

bloodline 10-23-2010 07:39 AM


Originally Posted by ctxspy (Post 647070)
I've updated the code, it now implements all the messages defined thus far.

I've also zipped up the source code as well. The executable is in the bin/debug directory.

Awesome stuff, I'm away from the keyboard today (taking the girlfriend shopping)... So I can't test anything, but I will need to define and implement the data feed. This your program will request the data, and the ECU will respond with a data structure containing all the current system setting and information, this way your app will be able to send out periodic requests (say every 50milliseconds) and be able to update that freedback to the user (perhaps with some nice gauges and dials, as I'm
Planning for the macos version)

bloodline 10-25-2010 02:48 PM


Originally Posted by ctxspy (Post 647070)
I've updated the code, it now implements all the messages defined thus far.

I've also zipped up the source code as well. The executable is in the bin/debug directory.

Seems to work! Connected to COM3 at 115200 :) Hopefully I'l get more time to work on my end of this soon!

ctxspy 10-25-2010 06:51 PM

excellent. Now give me a datafeed and i can start playing around!

I'll need to get an arduino i think so i can really play w/the ECU source too.. How hard is it to get that side working?

bloodline 10-26-2010 03:17 AM


Originally Posted by ctxspy (Post 648143)
excellent. Now give me a datafeed and i can start playing around!

Coming up ASAP! :)


I'll need to get an arduino i think so i can really play w/the ECU source too.. How hard is it to get that side working?
As the thread starter, I'm actually surprised you don't have one already ;)

Very simple to get going. Buy the Arduino Uno, download the Arduino software (you can do this first if you prefer), copy and paste my ECU code into te Arduino IDE, plug Uno into your computer, hit upload... Done :)

This has to be the simplest way to get into Microcontroller development.

A word of warning, my code does require a CAS signal to run, otherwise it will just wait at the sync section of code. If you have a real CAS you can connect up then that would be perfect... Or you could use a second microcontroller to generate the CAS signal as I currently do.

ctxspy 10-27-2010 11:16 PM

I found this (among others).. Apparently by searching for "arduino piggyback" and other terms i was able to find other interesting projects.

http://thedeltaecho.wordpress.com/20...ublic-release/

It seems there are a few people out there already experimenting with arduinos as ECUs in various forms.

I've e-mailed the guy working on this project to see how he's doing and whether he thinks there's any possibility of making a more generic system.

Tomaj

bloodline 10-28-2010 07:36 AM


Originally Posted by ctxspy (Post 649138)
I found this (among others).. Apparently by searching for "arduino piggyback" and other terms i was able to find other interesting projects.

http://thedeltaecho.wordpress.com/20...ublic-release/

It seems there are a few people out there already experimenting with arduinos as ECUs in various forms.

I've e-mailed the guy working on this project to see how he's doing and whether he thinks there's any possibility of making a more generic system.

Tomaj


Wow, I never managed to find any arduino ECU projects, my google skills are weak :)

This guy seems to be quite far ahead already, I will make sure his licence is compatible with mine and then read over his code.

I've already started my v0.6 codebase (only plotted on paper at the moment) with yet another significant change, moving away from the "busy wait" that I used to sync in the v0.5 code, to using interrupts. This will make my serial code work better, at the moment is isn't really working very well at all... It seems to be crashing your app now :(

-Edit- If you want to get into Arduino devlopment, I notice that you can get cheap Arduino clones on EBay, the "Seeduino" seems to get good reviews too (make sure you get one with the AVRMega328 CPU and running at 16Mhz, or my code won't work)

Mitch_1979 10-28-2010 09:45 AM

Hello Miata enthusiasts...

I'm the author of the 2JZduino project and theDeltaEcho wordpress site linked to above.
ctxspy send me an e-mail to bring my attention here.

In brief, I've had preliminary success running an Arduino-based project as a piggyback on a 2JZ-GE (the 3L inline 6 used in the Lexus IS300, Toyota Altezza, Toyota Supra [non turbo]). It currently intercepts and delays crank sensor pulses (delays ignition timing), intercepts and scales injector signals (fuel mixture), and simulates narrowband O2 sensor output based on wideband O2 sensor input (programmable closed-loop AFR).

For more info I'll defer to my blog for now.

Anyway, if it turns out there is a project here to develop a more advanced Arduino-ECU I'd be happy to be involved in any way that's helpful.

I'll admit I haven't had time to read this thread in detail (just quickly perused it) but I will do that later today.

-Mitch

[Edit: P.S. I did see some early discussion in this thread about whether an Arduino was fast enough to handle engine control... this analysis and simulation was an early part of my work for 2JZDuino. See here...
http://thedeltaecho.wordpress.com/20...or-capability/ ]

bloodline 10-30-2010 08:29 AM


Originally Posted by Mitch_1979 (Post 649262)
Hello Miata enthusiasts...

I'm the author of the 2JZduino project and theDeltaEcho wordpress site linked to above.
ctxspy send me an e-mail to bring my attention here.

In brief, I've had preliminary success running an Arduino-based project as a piggyback on a 2JZ-GE (the 3L inline 6 used in the Lexus IS300, Toyota Altezza, Toyota Supra [non turbo]). It currently intercepts and delays crank sensor pulses (delays ignition timing), intercepts and scales injector signals (fuel mixture), and simulates narrowband O2 sensor output based on wideband O2 sensor input (programmable closed-loop AFR).

For more info I'll defer to my blog for now.

Anyway, if it turns out there is a project here to develop a more advanced Arduino-ECU I'd be happy to be involved in any way that's helpful.

I'll admit I haven't had time to read this thread in detail (just quickly perused it) but I will do that later today.

-Mitch

[Edit: P.S. I did see some early discussion in this thread about whether an Arduino was fast enough to handle engine control... this analysis and simulation was an early part of my work for 2JZDuino. See here...
http://thedeltaecho.wordpress.com/20...or-capability/ ]

Hi Mitch,

I have been working on this Arduino ECU project for a while, the this thread is getting a bit large and unfriendly. But I have got quite far now, you can find my latest code at sourceforge, under the project title of MiataBrain :)

I had a few obstacles to overcome using the Arduino, but I think I have solved all the major issues with the time critical and structural components of the code.

All that needs to be added now is the fuel calculation/table, I'm using a linear advance (that follows the factory settings) on the spark timing for now... but that can easily be replaced.

The two functions that need to be completed are (and really need to be tested in a real engine to get right):

sparkTime() // which returns the advance based on the engine state.
setInjFlowTime() // which returns the time the injector needs to be open based on the engine state.

I know that you are not working on a Mazda MX5, but you might be able to adapt the code, though it is hardcoded to the MX5 CAS...

bloodline 10-30-2010 09:54 AM


Originally Posted by ctxspy (Post 649138)
I found this (among others).. Apparently by searching for "arduino piggyback" and other terms i was able to find other interesting projects.

http://thedeltaecho.wordpress.com/20...ublic-release/

It seems there are a few people out there already experimenting with arduinos as ECUs in various forms.

I've e-mailed the guy working on this project to see how he's doing and whether he thinks there's any possibility of making a more generic system.

Tomaj

Apologies, I have adjusted the serial spec again :)

The command: 33 63 65 04 to request all data has been shortened to 63 65 04... the 33 command identifier byte was unnecessary and complicated/slowed the receive code on the arduino...

The ardunio will now respond to that request with 13 byte response (this will change as we need it to).

The first 4 bytes are a long unsigned value, the number of microseconds per half revolution of the crank... from this it is trivial to establish the RPM of the engine.

The second 4 bytes are the injector flow time, that is to say the duty cycle of the injectors in microseconds.

The third 4 bytes are the number of rotation ticks (512 per revolution) advance of the spark. At the moment I have it programmed that the spark always tries to advance the spark so that the spark fires about 1600 microseconds before TDC. The packet is terminated with a 06 as required by the spec.

Not sure what else we need at this time, but I'm sure we will think of something.

Make sure you flush you serial receive buffer before sending any commands or requests. The Arduino sends some random garbage during startup (and possibly other times, if any debug code is left in), so if you are not expecting any data from the arduino, reject anything as rubbish.



I have moved my code to version 0.6 now (available at https://sourceforge.net/projects/mia...ataBrain_v0.6/ ), which is fully interrupt based, timing is now accurate to 16 microseconds (or about half a degree of revolution at 8000RPM).

Mitch_1979 11-03-2010 10:52 AM

Hi bloodline,

I just finished looking through your code. There are some nice simplicities in how you've built structs and variables... makes it rather nice to read compared to my project.

A couple of thoughts that came to mind as I went through it. They are timing related though and I haven't worked through how much more leniant the Miata engine is on timing (the 2ZJ has a 34 tooth crank wheel requiring Arduino to respond every 5 degrees). Anyway...

From "MiataBrain_v0_62.pde":

setup(), line 265: Is it possible for a Crank signal to occur during Serial.println("") and that it gets missed or the synchronization doesn't happen as expected?

aquireAnalog(), line 358: I'm sure you know analogRead() will suspend program flow until the ADC completes. The 4 sequential reads I think should probably consume about 1-2 ms. At redline this is on the order 90deg. I don't think you can afford to miss fuel injection by that much. The approach I took for ADC was to use an interrupt handler for ADC.complete, which processes the result, sets the next channel, kicks off the next ADC, and returns. This also affords a much larger ADC prescaler which seemed necessary because of the signal impedances I was metering.

datalink(), line 291: similar comments to above, running dataLink serially to your injector control logic I'd be wary of missing critical injector events by a substantial margin... but maybe that's what you intend to handle with the ring buffer TODO.


Maybe these thoughts will help. Cool project though, and I appreciate the cleanliness of the code. I'm actually considering now that a standalone might be more feasible on an Arduino than a piggyback for ECU (less logic load in a standalone).

-Mitch

bloodline 11-03-2010 01:12 PM


Originally Posted by Mitch_1979 (Post 652185)
Hi bloodline,

I just finished looking through your code. There are some nice simplicities in how you've built structs and variables... makes it rather nice to read compared to my project.

I really appreciate the code review, working alone means that I often get stuck in odd little traps that might not be the best or most efficient way (which explains why I'm already on v0.62 hahahah)...


A couple of thoughts that came to mind as I went through it. They are timing related though and I haven't worked through how much more leniant the Miata engine is on timing (the 2ZJ has a 34 tooth crank wheel requiring Arduino to respond every 5 degrees). Anyway...
The Miata is quite a crude (in a good way) machine, I've seen these engines take a real beating and a lot of abuse and still perform like new.

If you trawl back through this thread you can see the timing diagrams I built with the help of the members here, from that I was able to make the code. Basically, the Miata engine only has two timing pulses per crank revolution (actually I did toy with a more elaborate timing system that used the cam pulse as well, but it didn't improve timings in my tests).



From "MiataBrain_v0_62.pde":

setup(), line 265: Is it possible for a Crank signal to occur during Serial.println("") and that it gets missed or the synchronization doesn't happen as expected?
The cam pulse is so long, and the starting revolution speed is so slow missing the sync is never an issue... I have even had my simulator running at 8000rpm and it still syncs fine. I should note that even if the sync is lost it will be restored after 1 cam revolution, as the code resyncs every time.


aquireAnalog(), line 358: I'm sure you know analogRead() will suspend program flow until the ADC completes. The 4 sequential reads I think should probably consume about 1-2 ms. At redline this is on the order 90deg. I don't think you can afford to miss fuel injection by that much. The approach I took for ADC was to use an interrupt handler for ADC.complete, which processes the result, sets the next channel, kicks off the next ADC, and returns. This also affords a much larger ADC prescaler which seemed necessary because of the signal impedances I was metering.
I really wanted the code to be, 100% standard Ardunio... so that it could be ported to another arduino compatible board based on a different CPU architecture (I note here the wonderful, Maple Leaf Ardunio).

But in tests I found the Analog read function to block (as you point out) for upto 200 microseconds. My solution (which went against my original goal), was to set the prescaler at 1Mhz (21 microseconds per acquisition)... You are quire right that in a real engine, I might need a slower capture time... my solution would be to amplify and condition the signal with external hardware (a simple transistor and low pass filter should do, or even an op-amp...).

I have an interrupt based analog read function... but I fear that it might make the code a little too hard for beginners/less advanced programmers to read. Part of my design goal is for this code to be simple for anyone to have a go at using :)

I think you are probably right about this, and we will have to see how a real engine handles it...


datalink(), line 291: similar comments to above, running dataLink serially to your injector control logic I'd be wary of missing critical injector events by a substantial margin... but maybe that's what you intend to handle with the ring buffer TODO.
Yup, my serial code is a bit complex, but the result is that it only costs 2 microseconds per main loop cycle to send... one character is sent per main loop cycle. The communication protocol has been designed to ensure that there is minimal load on the system.


Maybe these thoughts will help. Cool project though, and I appreciate the cleanliness of the code. I'm actually considering now that a standalone might be more feasible on an Arduino than a piggyback for ECU (less logic load in a standalone).

-Mitch
Your thought are very helpful. I really welcome any constructive criticism.

I have though about your timing system, with the 34 tooth wheel... I think actually it would be rather easy to modify my code to use that... also with that resolution, timing would be much tighter :)

The required modification would be in the crankPulseInterrupt() function, where all I need to do is check for a high or low signal... for your timing wheel, that would need to be a bit more complex, and count the number of pulses (and call the correct function accordingly) and also the RPM calculation could be moved there (which would be nice)... everything thing else would remain the same.

ctxspy 11-03-2010 08:52 PM

Hey Mitch, good to see you posting here.

Sorry i haven't gotten back to your e-mail, been preoccupied with getting my car running on my MS2.

I'll probably be getting an arduino as a christmas gift, so then i'll be more active in this project.

Mitch, bloodline, now that you've each seen the other's code, any ideas on how to proceed? Is there an opportunity to create a cooperative team project here?

bloodline 11-04-2010 05:04 AM


Originally Posted by ctxspy (Post 652406)
Hey Mitch, good to see you posting here.

Sorry i haven't gotten back to your e-mail, been preoccupied with getting my car running on my MS2.

I'll probably be getting an arduino as a christmas gift, so then i'll be more active in this project.

This will be really great! If you need any assistance setting stuff up, let me know. I'm not a very good teacher, but I will try my best and you already have experience with software development so I think everything will be fine!

Mitch, bloodline, now that you've each seen the other's code, any ideas on how to proceed? Is there an opportunity to create a cooperative team project here?
I think Mitch would be most welcome if he could fin any use for the code for his own engine!

I also think he is spot on about the need for asynchronous analogue reads... And would be delighted if he could build a new analogue section :)
I have an osciliscope hooked up to the injector signals and it is clear that the duty cycle is larger than it should be :(


If either of you want to join the sourceforge project, please send me a request and I will add you.

For my part, I want to optimize the code a bit more (and probably move it to an OOP design, as my earlier mbed port uses, to make the code cleaner).

Mitch_1979 11-04-2010 10:24 AM

I do have some ideas regarding ways to better generalize my project and roll pieces of it into this one... maybe this could evolve into an Arduino shield providing standalone or piggyback ECU in the same package (to be honest, my interest will be limited if this is standalone only).

Some early decisions though: bloodline you said you wanted the code to be 100% standard Arduino. Is this firm? I'm not sure this will be possible in the long-haul, and I'll suggest you've already strayed from it by tweaking the Timer2 registers. Consider maybe what could be built is an unreadable complicated core [ :) ], but then a user-configurable header simple enough to allow this to easily port to other vehicles.

2nd pt.; ECU selection. I went right to the Arduino Mega for the additional features. Joe P listed out earlier in this thread all the potential inputs necessary. I haven't gone through a design but I'm skeptical that an Arduino 168 has enough stuff.

3rd pt.; JasonC are you onboard at least as electrical consultant? I managed to come up with circuits that work but I'm not an electrical engineer. What I built works for the 2JZ but I know there are problems I solved in s/w that would be better solved with a better schematic (specifically my lack of signal conditioning on the crank sensor). I think the next challenge will be breadboarding a circuit to hookup with a Miata engine.

...

Shift in topic; you're earlier questions about fuel injectors. Were they answered?
I *think* the Miata has a Peak & Hold injector, which are about 1-2 Ohm. The alternate is a saturated-type (12 Ohm). There are different requirements for the driver circuit for each type... maybe there is a design that works with both (???).

-Mitch

bloodline 11-04-2010 10:51 AM


Originally Posted by Mitch_1979 (Post 652624)
I do have some ideas regarding ways to better generalize my project and roll pieces of it into this one... maybe this could evolve into an Arduino shield providing standalone or piggyback ECU in the same package (to be honest, my interest will be limited if this is standalone only).

The software needs to be capable of standalone operation, and as piggy-backing simply a subset of the total functionality I see no conflict of interest between the two :)

I am quite keen to add A code path for your 34 tooth CAS, with a simple
Compile time switch.


Some early decisions though: bloodline you said you wanted the code to be 100% standard Arduino. Is this firm? I'm not sure this will be possible in the long-haul, and I'll suggest you've already strayed from it by tweaking the Timer2 registers. Consider maybe what could be built is an unreadable complicated core [ :) ], but then a user-configurable header simple enough to allow this to easily port to other vehicles.
Yes, the need to modify the ADC and the timer has blown away that goal... I suggest we simply wrap any non standard functionality into a set of clearly documented functions, they can be adapted if need be. It is really the only option, the arduino library does not provide enough realtime functionality.




2nd pt.; ECU selection. I went right to the Arduino Mega for the additional features. Joe P listed out earlier in this thread all the potential inputs necessary. I haven't gone through a design but I'm skeptical that an Arduino 168 has enough stuff.
My code is already at 8k complied, so the 328 is the smallest chip I would recommend (all official arduino boards use at lease that chip so we are good)... The UNO board which I use does have a limited amount of IO, but it dies just about have enough to function (writing a multiplexer Is not difficult if I need more IO), my code will of course work on the mega, that is te advantage to using the standard library over custom functions :)


3rd pt.; JasonC are you onboard at least as electrical consultant? I managed to come up with circuits that work but I'm not an electrical engineer. What I built works for the 2JZ but I know there are problems I solved in s/w that would be better solved with a better schematic (specifically my lack of signal conditioning on the crank sensor). I think the next challenge will be breadboarding a circuit to hookup with a Miata engine.
I'm a software developer by trade, and hardware is just a hobby... We do need a hardware guy :)


...

Shift in topic; you're earlier questions about fuel injectors. Were they answered?
I *think* the Miata has a Peak & Hold injector, which are about 1-2 Ohm. The alternate is a saturated-type (12 Ohm). There are different requirements for the driver circuit for each type... maybe there is a design that works with both (???).

-Mitch
I think the miata is saturated, and is high impedance so won't burn up if you hold it open for long periods :) I think Joe posted about this earlier in this thread.

Mitch_1979 12-10-2010 08:12 PM

Long-time since any progress has been reported on this thread. Any updates worth posting? I've thought a few times on working out a function to read analog data but hesitated because I figured I'd be working with a fairly obsolete .pde.

ctxspy 12-10-2010 09:17 PM

Hey Mitch, I've been busy with work & family lately, no progress from me :) I'm planning on getting an arduino sometime early next year.

bloodline 12-11-2010 06:59 AM


Originally Posted by Mitch_1979 (Post 667472)
Long-time since any progress has been reported on this thread. Any updates worth posting? I've thought a few times on working out a function to read analog data but hesitated because I figured I'd be working with a fairly obsolete .pde.

Hey there,

The project is still alive, but I've had to focus on my real life job for a bit as the economy here in the UK has gone belly up. I am having to get everything ready to look for a new job etc... Anybody want to employ me full time on this project? ;) hahahahah

On a brighter note, the last release does work from a theoretical point of view. That is to say that it should hold an engine @1000 RPM, and while it would be lumpy it should handle up to 7000 RPM.

In order to push this project on any further, really we need to interface with a real engine. I have compared my output on an O-scope with that of the engine and it "seems" to be ok.

I'm looking forward to ctxspy getting an Arduino so we can share ideas a bit more, and should allow us to develop the ECU<->Laptop interface to something very useful!

I also hope we can start to build an Arduino/Engine interface... but we can worry about that later ;)

sketchman 12-20-2010 10:10 AM

Subscribed.
I just thought of this a couple days ago. Glad to see it's already happening.

JustinHoMi 12-20-2010 06:24 PM

I wish I had time to help with this. When you get to the point where you need baseline fuel and spark maps (afm volts vs rpm), let me know.

rb26dett 12-23-2010 10:09 AM

OK, guys, bloodline, and mitch 1979, please for the love of god, get these code bases into version control, and get them licensed appropriately! GPL or MIT or Apache or BSD or something good.

www.github.com - post link when done, thanks!

Also, move this into the public domain and out of this purely miata forum, potentially gaining more assistance! Post it on:

http://forum.diyefi.org

no ads, rare moderation, correct target audience, that forum is exactly for projects just like this one.

Here is an example of some aduino based stuff on github:

https://github.com/quan-time/dynofirmware

Fred.

sketchman 12-23-2010 10:21 AM

^X2
This thing needs to go places.

flyrdm 01-05-2011 05:15 PM

This serious enough for ya? Anybody want to take this h/w design further?
 
https://github.com/flyrdm/EngineControlShield

Made for Arduino Mega in shield/stack format. I have two prototypes built
and will send to people if convinced they might do something with it. I put a lot of time and expense into it so far. Sensor reading works. Crank/cam decoding works. Not much else tested or developed. I put this out there to hedge my bets with Fred@FreeEMS and to keep the opensource fires lit. ;)
Cheers,
flyrdm

rb26dett 01-05-2011 05:19 PM


I put this out there to hedge my bets with Fred@FreeEMS
Ye of little faith! ;-)

That repo isn't much good to anyone but you without a license declaration of some sort in it, you might want to fix that.

Fred.

ctxspy 01-05-2011 07:45 PM


Originally Posted by flyrdm (Post 675964)
https://github.com/flyrdm/EngineControlShield

Made for Arduino Mega in shield/stack format. I have two prototypes built
and will send to people if convinced they might do something with it. I put a lot of time and expense into it so far. Sensor reading works. Crank/cam decoding works. Not much else tested or developed. I put this out there to hedge my bets with Fred@FreeEMS and to keep the opensource fires lit. ;)
Cheers,
flyrdm

for the lay-people -- what exactly does it do? offloads some of the lower level stuff into a shield so the main arduino can concentrate on calculations related to fuel, spark, etc?

tomaj

ctxspy 01-05-2011 07:47 PM

I'm still interested in the project, but my vote is to keep it miata specific for now.. I've seen many projects languish and die in the early stages because people want it to be everything for everyone.

IMHO if you get it to a decent stage running on a miata, people will become excited and intrigued and make whatever changes they feel are necessary to run on THEIR car.. enough of that happens, then you can come together and abstract the differences so that it becomes interchangeable based on some settings vs being hardcoded..

Some people prefer the top-down approach but without a real motivator ($$) and proper staffing it rarely gets out of the planning stages.

Tomaj

flyrdm 01-05-2011 07:54 PM

Arduino Mega ECU hardware
 
The piggyback boards do all the conditioning and driving.
For the the injectors and coils it uses dual (MC33810s?). These are just smart drive chips that can sense over-current, over-dwell, etc. The
NCVXXX is a VR conditioner (cam/crank). I also have a need to sense 6 EGTS so they are in there. Signal conditioning for the 0-5V analog inputs is also included (IAT, MAP, TPS, Coolant, etc). It has the capability to drive 8 coils and 8 injectors or if using less cylinders, you can use as higher current aux digital outs. I need to look at the external DB connector pinouts and make a sheet that describes capability. I haven't played with this in months so I am a little rusty.

ctxspy 01-05-2011 09:56 PM

When using this shield, what features would the main arduino implement?

In other words, does the shield expose an API and things like fuel & ignition are triggered from a main loop on the arduino, or does it run the execution loop on the shield itself but parameters can be tweaked via API?

flyrdm 01-05-2011 10:01 PM

The arduino implements everything. Arduino digital and analog I/Os are used to interface with the shields which in turn interface with the outside world. Think buffering/conditioning layer when you think of the shields.

FieldEffectDave 02-21-2011 12:18 PM

Is it possible to build fast accurate hardware timers in a CPLD device?

http://hackaday.com/2008/12/11/how-t...-devices-cpld/

I am wondering would it be possible if you built something like the above and have the CPLD do the time critical tasks of ignition and injection timing and leave the arduino to read A2Ds perform the calculations for injection time and ignition advance and transfer this to the CPLD. (Maybe with a parallel bus?)

Where I am getting at is you abstract all the timing critical tasks out of the Arduino and leave the "fun" stuff for a larger spectrum of enthusiasts to play with, within the Arduino development environment?

ctxspy 02-21-2011 12:36 PM

based on the prior pages it seems like maybe the arduino is not the ideal platform for ECU development due to the limited CPU power available.

I've read about the 'netduino' which has a more powerful CPU, more memory, onboard ethernet and SD card. Supports .NET micro framework, event based programming etc, costs $60.

Do you think .NET is too high level to be reliable as a real-time system? I'd be much more likely to contribute to a .net based system as i'm proficient in C#.

JustinHoMi 02-21-2011 01:28 PM

I think you'd lose developers if you switched to C#. Check out this arm-based arduino clone:

http://leaflabs.com/

kakarot 02-22-2011 01:20 AM


Originally Posted by FieldEffectDave (Post 692584)
Is it possible to build fast accurate hardware timers in a CPLD device?

http://hackaday.com/2008/12/11/how-t...-devices-cpld/

I am wondering would it be possible if you built something like the above and have the CPLD do the time critical tasks of ignition and injection timing and leave the arduino to read A2Ds perform the calculations for injection time and ignition advance and transfer this to the CPLD. (Maybe with a parallel bus?)

Where I am getting at is you abstract all the timing critical tasks out of the Arduino and leave the "fun" stuff for a larger spectrum of enthusiasts to play with, within the Arduino development environment?

Its not a bad idea to split the task, but to calculate the injection/ignition takes a bit of processing. Arduino has plenty of HP to take on engine management. My Audi has 8Mhz cpu and fraction of memory that arduino.

It may be a good idea to copy the ArduPilot style of coding. http://diydrones.com/

They do it by splitting the task onto fast, medium and slow. So crank position and ignition is tracked in the fast loop, injection tracked in the medium loop, ignition advance, injector pulse width, MAF/MAP, etc in slow loop.

many tricks can be used to reduce cpu loading, like parallel injection (all at once), combining ignition coils, fire 1&4 and 3&2 for 4 cyl for example.

Also, a friend of mine is doing multitasking on arduino platform, so that is possible as well.

PS, going to read the thread from start ones again.


All times are GMT -4. The time now is 04:22 AM.


© 2024 MH Sub I, LLC dba Internet Brands