get crank angle

Hello guys this is my first project with uno and its going very well.
I am trying to make a parallel ecu for my small 150 cc efi bike that i will use to tune it my self
so far i can read all the sensors, read rpm, calculate the injector pulse width but i do miss one part
to make my bike run with my fuel map.

i want to know how to get the angle of the crankshaft so i can time the injector correctly, i already read the crankshaft sensor using interrupts by using this code

|
int ReadCrankTimes(){
   int maxAngle = 360, math = 0,angle = 0;
   
   noInterrupts();
   Serial.println(timeoldHigh);
   Serial.println(timeoldLow);

   math = timeoldHigh + timeoldLow ; //360
   interrupts();

}

void InteruptPulse(){
  unsigned long TimeNow = micros();
  state = digitalRead(CrankInputPin);

  //divide by 2 later on GetRpm
  ++crankRevolutions;

  if(state == LOW){
    timeoldLow= TimeNow - lastTime;
  }else{
    timeoldHigh = TimeNow - lastTime;
  }

  lastTime = TimeNow;
}

my idea was to calculate how many angles is the missing tooth from the total 360 rotation and start from there
by using timeoldLow / (timeoldHigh + timeoldLow) * 360 but i dont think this is the correct way to do it

so timeoldHigh + timeoldLow its a 360 turn
4 strokes are 2 360 crankshaft turns

here you can use some data from my logs if you want

Tps Voltage : 0.75 Map kPa : 58.22 BARO kPa: 82.95 Iat Celcius : 23.20 RPM : 1180 CFM : 0.64 Fuel ml/s: 0.04 Injector Pulse Width µS: 2614
############################################################
2596 timeoldHigh
928 timeoldLow
Tps Voltage : 1.14 Map kPa : 82.24 BARO kPa: 82.95 Iat Celcius : 23.09 RPM : 2040 CFM : 1.68 Fuel ml/s: 0.09 Injector Pulse Width µS: 3635
############################################################
1068 timeoldHigh
584 timeoldLow
Tps Voltage : 1.50 Map kPa : 83.05 BARO kPa: 82.95 Iat Celcius : 22.92 RPM : 2940 CFM : 3.16 Fuel ml/s: 0.16 Injector Pulse Width µS: 4853
############################################################
696 timeoldHigh
500 timeoldLow
   noInterrupts();

Serial.println(timeoldHigh);
  Serial.println(timeoldLow);

Don't do that! Serial printing uses interrupts. Grab "safe" copies while interrupts are disabled, turn the interrupts back on, THEN do your calculation or printing.

The Arduino is not the ideal platform to make an ECU. There are specialized processors that basically run the entire program synchronized with the crank rotation. However it can be done for simple control of small numbers of cylinders.

Once you have a complete revolution, then you can estimate that the next one will take the same amount of time. So a fraction of a revolution is a fraction of that time.

thnx for the info about print!

if the rpm change ? for 1200 rpm to 2000 for example

Then the time you measured for a rotation will change.

How many pulses per crank revolution? You talk about a "missing tooth" which usually means many pulses per revolution but you also talk about having only two pulses per revolution. Which is it?

my wheel got 12 teeth with 1 missing so its 11 actual teeth

360 / 12 = 30deg

missing tooth = 60deg and rest teeth = 300deg

lets say rpm = 1200
i think the pulses are 1200 / 60 = 20 revs per second so 20 * 11 = 220 pulses per second

If you measure the time between tooth pulses the pulse that arrives after a much longer interval will be the tooth after the missing tooth. And if you know where, relative to the crankshaft, the missing tooth is you can identify the crank position.

Keep in mind that you will need to know the angle much more accurately than +/- 30 degrees so you must use time. You can use successive pulses to update the timing as the speed varies.

...R

so i can use something like this to get the long pulse.

void InteruptPulse(){
  unsigned long TimeNow = micros(),nowPulse = 0;
  currentPulse = TimeNow - lastTime;

  if( (currentPulse / 2) > lastTime){ 
    LongPulse = nowPulse;
    
  }else{
    /*
    */
  }
  lastTime = currentPulse;
}

does the long pulse mean the crank has rotated for a 360 turn ?i am a bit confused at this point

motoGuyDIY:
my wheel got 12 teeth with 1 missing so its 11 actual teeth
360 / 12 = 30deg

Is the long pulse a HIGH pulse or a LOW pulse? I'm going to guess that RISING edges signal the signal the start of a tooth and FALLING edges signal the end of a tooth, so the long pulse would be LOW. You don't need both edges so I would look only at RISING edges.
If you keep an average of the last few tooth times (RISING to RISING) you should be able to easily detect the RISING edge of the First Tooth (the one after the gap). It will have a tooth considerably longer than the average. I would try 1.5 times the average as a threshold to detect the long gap.
The average tooth time, times 12, will give you an approximation of the time per rotation. You can use the rotation rate, last edge number, and last edge time to calculate the degrees since the First Tooth. If the First Tooth is not a TDC then you would add an offset to get degrees from TDC.

I think this is how I would do it

void myPulseIsr() {
    timeOfPulse = micros();
    prevPulseInterval = pulseInterval; // save the preceding value
    pulseInterval = timeOfPulse - timeOfPrevPulse;
    if (pulseInterval > (prevPulseInterval + prevPulseInterval >> 1) { // compare to prev * 1.5 
        longPulse = true;
        longPulseTime = timeOfPulse;
    }
    newPulse = true;
    timeOfPrevPulse = timeOfPulse;
}

NEVER use division where speed matters as it is very slow >>1 is the same as /2 but very much faste

...R

I like that ISR. I would add a count of which pulse last passed so the time-based calculation is only within a 30° or 60° interval:

volatile unsigned long PulseInterval;  // Microseconds between the last two pulses
volatile unsigned long PulseTime;  // Time (micros()) of the rising edge of the latest pulse
volatile byte PulseNumber = 0;  // Which pulse was the latest pulse
volatile boolean PulseIsNew = false;   // Flag set by the ISR


void myPulseIsr()
{
  static unsigned long prevPulseTime;  // Needed for detecting the new pulse interval
  static unsigned long prevPulseInterval;  // Needed for detecting the long pulse


  PulseTime = micros();
  prevPulseInterval = PulseInterval;  // save the preceding value
  PulseInterval = PulseTime - prevPulseTime;
  prevPulseTime = PulseTime;
  PulseNumber++;
  // A pulse longer than 1.5 times the previous pulse indicates the index pulse (#0).
  if (PulseInterval > (prevPulseInterval + (prevPulseInterval >> 1)))   // compare to prev * 1.5
  {
    PulseNumber = 0;
  }
  PulseIsNew = true;  // Let loop() know that new data is available
}


void setup()
{
  attachInterrupt(digitalPinToInterrupt(2), myPulseIsr, RISING);
}


void loop()
{
  static unsigned long localPulseTime = 0;
  static byte localPulseNumber = 0;
  static unsigned long microsecondsPerRevolution = 999;


  if (PulseIsNew)
  {
    // Grab the volatile variables.
    noInterrupts();
    unsigned long localPulseInterval = PulseInterval; // Should work down to 152 RPM
    localPulseTime = PulseTime;
    byte localPulseNumber = PulseNumber;
    PulseIsNew = false;
    interrupts();


    microsecondsPerRevolution = localPulseInterval * 12;
    if (localPulseNumber == 0)
    {
      microsecondsPerRevolution = localPulseInterval * 6;  // Long Pulse
    }
  }


  unsigned long microsecondsSincePulse = micros() - localPulseTime;
  long int degreesSinceIndex = 30 * localPulseNumber;  // First 11 pulses are at 30° intervals
  // Equivalent to (microsecondsPerRevolution / microsecondsSincePulse) * 360 but less subject to truncation
  degreesSinceIndex += (microsecondsPerRevolution * 360UL) / microsecondsSincePulse;


  //   THIS IS WHERE YOU ADJUST 'degreesSinceIndex' FOR OFFSET FROM TDC


  // This is where you use the new crank position
}

thnx a lot for your code i will study it right now. btw i went to my bike and used a socket to turn the crank
while i was turn the crank there was a spot where the fuel pump worked so that was the trigger point for ecu
then i turn the crank counter clockwise and the pump worked again so i said it must be the ecu count the teeth .so i turned the key off and then on and tried the same thing fuel pump worked so it must be a static trigger

i need to remove the stator case to see at what point the efi gets triggered

hey guys i got a small update i cant read the sensor correct yet but i do know that from teeth number 1
till the sensor location at TDC its 4.5 teeth that means 135deg so the triger point is 360 - 135 = 225 deg

sensor got 2 cables can it possible be an inductive sensor instead of hall sensor ?if so how you read an inductive sensor because i only get only 2 pulses per rotation

motoGuyDIY:
sensor got 2 cables can it possible be an inductive sensor instead of hall sensor

How many were you expecting?

Can you post a photo of the sensor?

...R

as far i know a hall effect sensor got 3 cables.I attached my sensor btw i read somewhere that if sensor is inductive i need a converter from analog to digital to read it.if so is there any fast converter? what do you recommend for this job

Image from Reply #14 so we don't have to download it. See this Simple Image Guide

...R

Why don't you write a short program that does nothing except try to detect a pulse from the sensor. That way you can easily try different circuit configurations.

...R

i will try tomorrow with an ADC converter what do you recommend i want something fast for this job

motoGuyDIY:
i will try tomorrow with an ADC converter what do you recommend i want something fast for this job

At this stage I don't even know that an ADC is needed. That's why I suggested a simple test program. If it was my project I would be hoping to be able to detect it with a digital HIGH or LOW.

...R

yea i know i tried everything but its not working. take a look at my readings with analogRead conerted to voltage. this looks like an analog signal so i need to convert it to understand it

0.27
0.25
0.01
0.00
0.00
0.11
2.10
0.00
0.56
0.00
0.00
3.34
0.00
0.00
1.59
0.00
0.00
0.56
0.00
0.00
0.65
0.04
0.00
0.61
0.44
0.00
0.00
2.21
0.00
0.00