Electronic ignition system EIS feedback

AndersStenhammar:
Hi Dwight. Thank you for helping :slight_smile:
I guess I just do whatever that comes to my mind and if it works, I go for it :slight_smile:
In my head I am using rpm to calculate time it takes to turn the crankshaft one degree.
I am sorry but I don`t quite understand what you are telling me about Figuring advance as a percentage and to leave the value as time and not the inverse of time. Could you explain some more detailed? I am a rookie at this, an dmy english is not the best.

I like to use numbers i feel confortable with when I am learning new stuff. Are you an engine engineer?

I'm not looking at your code so I don't know what degrees you have but I'll use some arbitrary
numbers to demonstrate.

Say the engine is to have an advance of 10 degrees at 2000 RPM.
Say you have a sensor on the crank that pulse twice per rotation rotation at 20 degrees
before top dead center of one of the banks of pistons.
( I don't recall how you are getting cam position but that isn't relevant at this time. ).
For one sensor, at 2000 RPM, the pulses will be .015 seconds apart or
15000 us as calculated by reading delta micros().
15000 / 180 is the time for 1 degree.
15000 * ( 20 - 10 ) / 180 is the time in microseconds to delay before firing
the plug.
As I recall, the VW had a odd shaped ignition cam. You'd need two
sensors at what ever goofy angle the pistons were on a VW or calculate
based on the one sensor.
Also, how are you figuring cam time relative to crank?
I'll go back and read how you're doing sensors later.
I'm sure you figured that OK.
It has been some time since I'd fiddle with a VW bug.
As you can see, other than some per-calculations, I never converted anything
in the computer to RPM.
All the computer needs is time. RPM is rotation per minutes, a wasted calculation.
Once you have RPM, you have to convert back to time, that you already have.
Doing real time control is using the controller the least. You are just adding
busy work for it that may introduce rounding errors and other unneeded
noise.
Make your code as simple as possible and have it do the least number
of operations.
RPM is for dash board meters and people.
Dwight
PS
Crazy spark timing. I had to look it up, the pre-1971 engines had the #3
cylinder retarded by 4 degrees to make it run cooler.
The problem was the location of the oil cooler. From 1971 on, they mounted
the oil cooler separate ( called the "dog-house" oil cooler ) and then all
of the timing was 90 degrees for each cylinder.
So, which engine do you have?
Dwight

Ok Dwight, I am greatfull for your words. I am having a little hard time understanding the math, but I will try. I feel that it will be difficult for me to change my code that much. I have been working on this code for 6 month now and I would love to get it function. Could you study my code and see if it could be modified in some "easy" way?

You can see my advance calculations in the attached excell file (the numbers don`t match the code just now, but I will uppdate the code).

The engine is an experimental aircraft VW type1 engine or an airboat VW engine. The aircraft engine is operated in a slow and easy manner. When flying, you push the throttle slowly and cruse at 3000rpm, and land at 1000 rpm. The engine is cooled ba the propeller and ram air baffles. Most don`t use oil cooler in this engines (1835cc, 65hp @ 3300rpm)

I dont have any cam sensor, and I dont need it. There are two magnets and one sensor and two coils (Wasted spark coil pack). The magnets are positioned at 3 degrees ATDC and the advance timing is set at 3 degrees ATDC so that it will be possible to hand prop the engine.

EI10A_Electronic_Ignition_System.pdf (954 KB)

Attached is an similar system that I have gotten inspiration from, the Leburg system.
http://www.sky-craft.co.uk/acatalog/Skycraft_Leburg_Ignition.html

EI10A_Electronic_Ignition_System.pdf (954 KB)

Yes, I see, a dual double ended coils. No need to see the cam.
You only need to see 90/270 degrees different than 0/180.
I'll need to look at the code some but I think it is mostly just rip
out the unneeded code and use a modified table.
I'll give it a look in the next few days.
Dwight

Hi Anders
I went through your code and other than I think
you are wasting time converting to RPM, just to convert it back to time.
It also introduces another layer of rounding errors that are not needed.
Just for your own interest, you might plot RPM on a graph as a linear
scale and plot advance time also on a linear scale.
You might see that things are more easily fitted to curves if you stay
in the time domain, rather than switching to the frequency domain
and then back to the time domain.
One other thing, I noticed that you have both plus and minus swings
of detecting the magnets. That is fine and makes sense to detect
which of the two coils to fire.
Do be careful not to take the input voltages below ground. Over time
this can stress the input protection circuitry, causing premature failure.
One last thing, I believe the dwell time is suppose to be the time that
the coil is being charged before the flyback.
Also make sure you driving transistors are rated for at lest 1Kv when
they turn off. On the bench, the voltage to fire a sparkplug is quite
a bit lower than what is need to fire the compressed gas of an engine.
Most points cares that I've worked with see primary voltages hit
400-600v. CDIs tend to be in the order of 350-400V.
Dwight

Hello.
I have not hade the time enought to work on my code a lot, but today I did. I have tried to get rid of the unnecessary calculations and I made an attempt to use crankshaft time instead of rpm. I did make an curve in excell for the cranktime also to get a better understanding. Please have a look at my code and excell sheet.
I have some questions.
First, I have read that calculation with global variables take a lot of memory. I have to use them in my interrupt service routines and interrupts, but in my loop I could move that value to an local variable and use that in my calculations in the loop. Is that a good idea?

The ignition advance time could be coded with about 10 calculation instructions. It could also be coded without calculations, but a lot of if instructions instead (one for every 100rpm for example). What way is the best? What takes most time, and what is most safe/realiable?

Here is one version:

if (halfrev>=  100000 )                     { cranktime = 0 ;    } //  300
if ((halfrev>=  85714 ) & (halfrev< 100000)){ cranktime = 85476 ;} //  350
if ((halfrev>=  80000 ) & (halfrev< 85714 )){ cranktime = 78667 ;} //  375
if ((halfrev>=  77922 ) & (halfrev< 80000 )){ cranktime = 74025 ;} //  385
if ((halfrev>=  76923 ) & (halfrev< 77922 )){ cranktime = 72222 ;} //  390
if ((halfrev>=  75000 ) & (halfrev< 76923 )){ cranktime = 70000 ;} //  400
if ((halfrev>=  70588 ) & (halfrev< 75000 )){ cranktime = 65921 ;} //  425
if ((halfrev>=  66667 ) & (halfrev< 70588 )){ cranktime = 62333 ;} //  450
if ((halfrev>=  60000 ) & (halfrev< 66667 )){ cranktime = 56267 ;} //  500
if ((halfrev>=  50000 ) & (halfrev< 60000 )){ cranktime = 47222 ;} //  600
if ((halfrev>=  42857 ) & (halfrev< 50000 )){ cranktime = 40476 ;} //  700
if ((halfrev>=  37500 ) & (halfrev< 42857 )){ cranktime = 35417 ;} //  800
if ((halfrev>=  33333 ) & (halfrev< 37500 )){ cranktime = 31481 ;} //  900
if ((halfrev>=  30000 ) & (halfrev< 33333 )){ cranktime = 28333 ;} //  1000
if ((halfrev>=  28571 ) & (halfrev< 30000 )){ cranktime = 26904 ;} //  1050
if ((halfrev>=  27272 ) & (halfrev< 28577 )){ cranktime = 25454 ;} //  1100
if ((halfrev>=  25000 ) & (halfrev< 27272 )){ cranktime = 23152 ;} //  1200
if ((halfrev>=  23076 ) & (halfrev< 25000 )){ cranktime = 21217 ;} //  1300
if ((halfrev>=  21428 ) & (halfrev< 23076 )){ cranktime = 19523 ;} //  1400
if ((halfrev>=  20000 ) & (halfrev< 21428 )){ cranktime = 18056 ;} //  1500
if ((halfrev>=  18750 ) & (halfrev< 20000 )){ cranktime = 16770 ;} //  1600
if ((halfrev>=  17645 ) & (halfrev< 18750 )){ cranktime = 15686 ;} //  1700
if ((halfrev>=  16666 ) & (halfrev< 17647 )){ cranktime = 14759 ;} //  1800
if ((halfrev>=  15789 ) & (halfrev< 16666 )){ cranktime = 13912 ;} //  1900
if ((halfrev>=  15000 ) & (halfrev< 15789 )){ cranktime = 13166 ;} //  2000
if ((halfrev>=  14285 ) & (halfrev< 15000 )){ cranktime = 12460 ;} //  2100
if ((halfrev>=  13636 ) & (halfrev< 14285 )){ cranktime = 11818 ;} //  2200
if ((halfrev>=  13043 ) & (halfrev< 13636 )){ cranktime = 11231 ;} //  2300
if ((halfrev>=  12500 ) & (halfrev< 13043 )){ cranktime = 10715 ;} //  2400
if ((halfrev>=  12000 ) & (halfrev< 12500 )){ cranktime = 10233 ;} //  2500
if ((halfrev>=  11538 ) & (halfrev< 12000 )){ cranktime = 9807  ;} //  2600
if ((halfrev>=  11111 ) & (halfrev< 11538 )){ cranktime = 9382  ;} //  2700
if ((halfrev>=  10909 ) & (halfrev< 11111 )){ cranktime = 9151  ;} //  2750
if ((halfrev>=  10344 ) & (halfrev< 10909 )){ cranktime = 8597  ;} //  2900
if ((halfrev>=  10000 ) & (halfrev< 10344 )){ cranktime = 8288  ;} //  3000
if ((halfrev>=  9677  ) & (halfrev< 10000 )){ cranktime = 8005  ;} //  3100
if ((halfrev>=  9375  ) & (halfrev< 9675  )){ cranktime = 7750  ;} //  3200
if ((halfrev>=  9090  ) & (halfrev< 9375  )){ cranktime = 7510  ;} //  3300
if ((halfrev>=  8823  ) & (halfrev< 9090  )){ cranktime = 7284  ;} //  3400
if ((halfrev>=  8571  ) & (halfrev< 8823  )){ cranktime = 7071  ;} //  3500
if ((halfrev>=  8333  ) & (halfrev< 8571  )){ cranktime = 6870  ;} //  3600
if ((halfrev>=  8108  ) & (halfrev< 8333  )){ cranktime = 6680  ;} //  3700
if ((halfrev>=  7894  ) & (halfrev< 8108  )){ cranktime = 6500  ;} //  3800
if ((halfrev>=  7692  ) & (halfrev< 7894  )){ cranktime = 6329  ;} //  3900
if ((halfrev>=  7500  ) & (halfrev< 7692  )){ cranktime = 6166  ;} //  4000
if ((halfrev>=  7317  ) & (halfrev< 7500  )){ cranktime = 7195  ;} //  4100

And here is an other wersion (the one I use for the moment):

//Following calculations are based on a excell sheet with the advance curve attached.

 halfrev= halfrevGlobal; //Transfer global variable value to a lokal for less memory usage ?.

if ((halfrev<= 100000)& (halfrev>= 75000 )){ //Advance from -3 to 9 @ 300-399 rpm 
cranktime=(halfrev+600/1000)-dwellTime;}

if ((halfrev<=  75000 ) & (halfrev> 50000 )){ ///Advance from 9 to 7 @  400-599 rpm
 cranktime=(halfrev+456/1000)-dwellTime;}

if ((halfrev<=  50000 ) & (halfrev> 42857 )){ ///Advance from 7 to 7 @  600-699 rpm
 cranktime=(halfrev+472/1000)-dwellTime;}

if ((halfrev<=  42857 ) & (halfrev> 27273 )){ ///Advance from 7 to 9 @  700-1199 rpm
 cranktime=(halfrev+482/1000)-dwellTime;}

if ((halfrev<=  27273 ) & (halfrev> 17647 )){ ///Advance from 9 to 17 @  1100-1699 rpm
 cranktime=(halfrev+507/1000)-dwellTime;}

if ((halfrev<=  17647 ) & (halfrev> 10909 )){ ///Advance from 17 to 26 @  1700-2749 rpm
 cranktime=(halfrev+485/1000)-dwellTime;}

if ((halfrev<=  10909 ) & (halfrev> 9091 )){ ///Advance from 26 to 28 @  2750-3299 rpm
 cranktime=(halfrev+447/1000)-dwellTime;}

if ((halfrev<=  9091 ) & (halfrev> 7500 )){ ///Advance from 28 to 29 @  3300-3999 rpm
 cranktime=(halfrev+427/1000)-dwellTime;}

if ((halfrev<=  7500 ) & (halfrev> 7317 )){ ///Advance from 29 to 0 @  4000-4100 rpm (rev limitation)
 cranktime=(halfrev-281/1000)-dwellTime;}

Wich way is the best for my application?

I have attached the new code and

Do be careful not to take the input voltages below ground. Over time
this can stress the input protection circuitry, causing premature failure.

IDo you have any advice how to address this? Using a 330ohm resistor maybe?

One last thing, I believe the dwell time is suppose to be the time that
the coil is being charged before the flyback.

Yes, that I know and I have that in my calculations and code.

Also make sure you driving transistors are rated for at lest 1Kv when
they turn off.

I am using an Bosch VAG coilpack whit built in transistor logic (igniter). I hope that will turn out fine because MegaJolt uses them also.

Have a nice day!

Arduino_EIS_160131.ino (8.12 KB)

Here is the advance curves. Excell file was to big.

Calculations take the same amount of memory wherever you store the variables. Sometimes you may need extra storage like an SD card to hold lots and lots of data while calculating but where it's stored doesn't change the size.

You should think about variable storage in your original design. If a variable only needs to be accessed inside the loop() function and no other function, then declare it inside loop(). If it also needs to be updated by an ISR, then it needs to be global. Then think about the persistence - does it need to remember its value when loop() isn't running? Add the static keyword. Then think about the size of the values you are storing - do you need byte, int or float? Then give it a good name which describes the contents of the variable. (Actually, you should probably name it first.)

Usually engine computers have a very tight deadline on when they must provide the next spark. To ensure that the main processing loop always runs at a consistent speed, the designers use a "map" to convert input to output. Even in integer maths, the number of clock cycles to perform some operations is dependent on the input values. A map always takes the same amount of time because it's (usually) only one memory-access instruction.

Maps are also a convenient way of thinking about the engine parameters. If you need to tweak the advance a little at 1800rpm, then you can do that directly, without trying to work out the coefficients of an equation that does this.

One thing I wanted you to see was that the amount in micro seconds
advance was almost a constant except at the ends. At the high end
it rolls off to help keep you from over revving.
At the low end, it is to ensure that it doesn't kick back and damage
the starter motor.
The burn time of the fuel air mixture is almost constant but
optimal timing does change some with both RPM and volume.
For the application you are using, just using RPM is OK since
there is little time when you'd be going down hill with high manifold vacuum.
As for the coil, you just have to make sure there is no voltage higher than
the its logic voltage of the adruino. Measure it with the arduino disconnected
to make sure.
A resistor is not enough for negative voltages. You need to have some form of
clamping or isolating circuit.
Dwight

Thank you Morgan and Dwight!! This helps a lot :slight_smile:
I was also wondering if it would be possible to use two parallel Arduinos running the same code and uses a hall sensor each and firing the same coil att the same time? That way I would have an backup controller just in case.
Do I need to but diodes between them so that they don't get damaged by each others output?
And do you think an optocoupler could be used between the arduinos and coil ignitor?

Backups are hard. Really hard. There's so many more failure modes to consider: what if one locks on hard? what if one randomly fires? what if both have the same software fault? It is usually best to put that engineering effort into making one work reliably.

Optocouplers are a good idea except an optocoupler is usually a low-current and low-voltage device. You need some proper transistors on the output side to actually operate the thing you're trying to operate. Coils in particular have extremely nasty ringing voltages that will destroy a transistor of insufficient beefyness.

Hello.
Some uppdate on the project now. I have made an engine crank signal simulator (2 signals per revolution). I did then with a lot of testing realize that my advance calculation was not right, so I changed the code and it seems to be working out. But I am pretty sure that the timing is a little late because of the arduino processing time. There is an other fellow who has made this correction and I will look into that.

The uppdated EIS code is in attachments.

Here is a short video of the setup:

Here is my Engine crank simulator:

//Use Serial monitor to send rpm setting to the simulator.
//Default RPM is 250

// To be able to manipulate the AVR processor timers:
#include <Avr/interrupt.h>
#include <Avr/io.h>
const int HallPin1 = 13;  // Hall latch sensor output (PORTB,0)
const int HallPin2 = 12;  // Hall latch sensor output (PORTB,0)
int TrimPotPin = A0;    // select the input pin for the potentiometer
unsigned int rpm;
long int timeold;
unsigned int sensorValue;
volatile long int microseconds;          // The microsecondcounter value.
volatile long int half_revolution_time;  // The time it takes for the crank to turn 1/2 revolution (The value of Trimpot.
volatile long half_revolutionStart;
volatile int half_revolutions;      // Hall sensor trigger counter

void setup() {

 Serial.begin(9600);
 pinMode(HallPin1, OUTPUT);    
 digitalWrite(HallPin1, LOW);  

rpm=250;
  microseconds = 0;         // Preset the us counter variable.
 
   /********** Setup timer2*************/
  noInterrupts();
  TCCR2A = 0;               // Turn off Control register for waveform generation
  TCCR2B = 0;               // Turn off noise cancelling, turn off edge select, waveform gen mode 0, 
  TCCR2A |= (1 << WGM21);   // Turn on CTC mode (so it will start again) automatically
  TIMSK2 |= (1 << OCIE2A);  // Set interrupt on compare match.
  OCR2A = 8;                // Prescaler of 64 gives 4uS per tick, 4uS * 8 = 32uS (32uS = 1 degree at ~5100rpm).
  TCNT2  = 0;               // Reset timer counter to 0
  TCCR2B |= (1 << CS22);    // Load 64 prescaler, and this starts the timer2! 
  interrupts();
 
}
 //The Interrupt Service Routine for Timer2 that will be executed each time the timer reach the compare match register (32uS)*/
 //______________________________________________________________________________________________________________________________
 ISR(TIMER2_COMPA_vect) {
  
  microseconds=microseconds+32;  // Increases the variable "microseconds" by 32 every time the ISR is executed).
   
  if (microseconds <= half_revolution_time){  
     digitalWrite(HallPin1, HIGH); // North magnet is passing the Hall sensor    
     }
     if (microseconds > half_revolution_time){  
     digitalWrite(HallPin1, LOW); // South magnet is passing the Hall sensor 
     }
     if (microseconds > (half_revolution_time*2)){   
     half_revolutions=half_revolutions+2;       
     microseconds = 0;         // Reset the us counter variable.  
     TCNT2  = 0;               // Reset timer counter to 0  
     }
 }

void loop() {
  half_revolution_time=30000000/rpm; 
   
 if (Serial.available() > 0) { // send data only when you receive data:
                   
  rpm = Serial.parseInt(); //read int or parseFloat for ..float...

  // say what you got:
  Serial.print("rpm: ");
  Serial.println(rpm, DEC);
  }
}

Arduino_EIS_160211_HeltOK.ino (10.1 KB)

Anders,
MorganS mentioned a beefy transistor for your ignition.

We used to use an MJ10012 in our standard ignition both on 6v and 12v.

The above ran from a hall effect and then through a buffer BC640 to the output transistor.

They were essentially designed for ignition switching I believe.

Not too expensive if you shop around. I think I initially bought about 20 in one go and bought them for about Aus $4.00-$6.00 each if I remember correctly.

I can email the circuits over to you if you like.

Should be able to mod the circuit to run on a micro without too much trouble.

Hello. Yes I would realy like to see the wiring diagram. You could attach it here or send it to anders.stenhammar84@gmail.com

I am using a coil pack with built in ignitor (transistors), so i amm not quit sure if it is neccesary to have an extra transistor. Do you think this wiring would be ok?

Powering the opto transistor from the Arduino in my mind defeats the purpose of the isolator.

Similar problems came up in the pic 1840 open source ignition a while back.

Not sure just where it was and many pages to look through so I'll do a look back when I get a chance.

http://www.rcgroups.com/forums/showthread.php?s=0dfb00bdfc046f866207a45a5a5e5ba7&t=1781959&page=91

Not only that but the 5V output on Arduino would not handle any loading such as this I would imagine.

Ok,,,,,didn't know about the inclusion of transistors in your coil packs...... I'll send over the 2 circuits anyhow.
Never know, may come in helpful one day.

That is a good point! I think I will power the arduino and the ignitor with this power supply wiring (attached).
I will look into that PIC system.

Reason for the seperate supply arrangement i.e. one for the micro and one for the ignitor is that ignition systems are very noisy and can cause many headaches.

By isolating the 2 systems can sometimes be an advantage.

Don't know if I like the "high" side switching arrangement you have with the npn opto either.

Others may care to comment but in my opinion, as the micro starts to turn the opto transistor on, the emitter voltage starts to rise due to it's load circuit, turning the transistor off again.

Might be better to look for a logic level mosfet perhaps driven by a slightly different circuit around the opto isolator.

As I say, others more qualified may care to comment there.

Ok so here is something for you to get your electro-engineering-teeth in (maybe only a Swedish of saying?)
I have tried to learn from all of you and have put a wiring diagram togheter (attached). What do you think of it?

So is there not anyone who want to comment on this TinyCad circuit?

5V regulator will get hot stepping 12-14V down to 5V.
5V into Vin will not work well - Vin needs ~6.5V for the Nano regulator to work. Feed the 5V into the 5V pins instead.
Don't know enough about the other 2 chips to comment. Got links to datasheets?