**HELP** Engine RPM reading fluctuation...

Here is a code which I am using to read the rpm of an engine.. The problem is that there is always a fluctuation in the reading ... The RPM of the engine which I am measuring normally idles at 700 RPM, But there is always a bad fluctuation on the last two digits of the reading...

How do I make it little stable..??

Here is my code below..

const int ignitionPin = 2;
const int ignitionInterrupt = 0;
const unsigned int pulsesPerRev = 1;

unsigned long lastPulseTime = 0;
unsigned long rpm = 0;

void ignitionIsr()
{
  unsigned long now = micros();
  unsigned long interval = now - lastPulseTime;
  if (interval > 2000)
  {
     rpm = 60000000UL/(interval * pulsesPerRev);
     lastPulseTime = now;
  }  
}

void setup()
{
  lcd.begin(16,2);
  pinMode(ignitionPin, INPUT);
  attachInterrupt(ignitionInterrupt, &ignitionIsr, FALLING);
}

void loop(){
  lcd.setCursor(0, 0);
  lcd.print("RPM- ");
  lcd.print(rpm);
  delay(200);

}

Rotational speed is calculated by number of rotations divided by some length of time, scaled to the time desired (one minute, one second, one week, whatever).

The number of rotations is the number of pulses received divided by the number of pulses per revolution.

You are not counting pulses, so there is no way you are going to get anything like a reasonable approximation of RPM.

I got your point, but with this code I get RPM readings which is close to one of the manufacturer's RPM meter.. the only thing is that my reading fluctuates by two digit...

The readings are :-

712 689 698 724 686 787 600 621

Are you thinking this is an Arduino problem? Your numbers are probably accurate. For less jitter in the displayed value, you could round to the nearest multiple of 25 RPM. :grin:

yes I was thinking something of that kind..

pick up few readings (3 readings) and average it and display it...

How do I do it your way...?? DO it something like

If((RPM % 25) == 0)
   {
     lcd.print (RPM);
   }

else
   {
   // what will I write here..?? or dont use an else part..?
   }

...???

Displaying a running average would probably help to improve display stability, but it might be more practical to just display (RPM + 50) / 100 and provide an indication that the display is showing hundreds of RPMs. You could add a feature so that while a button was pressed a finer resolution would be displayed, but I can't imagine that it would be much used after the first week or so...

Back in the early 70's I built a digital speedometer for rally driving that displayed speed to hundredths of miles per hour - and was (initially) surprised that I couldn't read any of the fractional digits and that the units digit was so jittery as to be practically useless. It was a good learning experience. :)

Perhaps you set up an array - every time you process an interrupt you shift the values down 1 position and insert the new one at the top

Add the values in the array and divide by the number of values.

kf2qd:
Perhaps you set up an array - every time you process an interrupt you shift the values down 1 position and insert the new one at the top

Add the values in the array and divide by the number of values.

according to you how many readings should I take into the array…??

I think 5 is fine… you…??

PaulS: Rotational speed is calculated by number of rotations divided by some length of time, scaled to the time desired (one minute, one second, one week, whatever)...

That's one method. The other method, which he is using, is to measure the elapsed time between pulses.

robmaples:

PaulS: Rotational speed is calculated by number of rotations divided by some length of time, scaled to the time desired (one minute, one second, one week, whatever)...

That's one method. The other method, which he is using, is to measure the elapsed time between pulses.

Yes ..! You are right... what I am trying to count is the time between the last pulse and the present pulse...

Here are a couple of ways to take the average of several readings-

Method 1: Just wait x number of pulses before taking a reading and then divide the elapsed time by x. For example, to average 5 pulses:

void ignitionIsr()
{
  unsigned long now = micros();
  unsigned long interval;

  pulseCount++;
  if (pulseCount==5)
  {
     pulseCount=0;
     interval = (now - lastPulseTime)/5;
     if (interval > 2000)
     {
        rpm = 60000000UL/(interval * pulsesPerRev);
        lastPulseTime = now;
    } 
   } 
}

This will slow down your updates by the same ratio as the number of pulses you average over, but if all you're doing is displaying the RPM, this should be adequate.

Method 2: If you need a higher update speed (I did a system where we recorded the RPM 100 times a second, so we needed to update it every pulse) then you need to use an array like kf2qd suggested. You do not need to add up the values however. Instead of storing the intervals in the array, you store the value of "now" for each pulse. For each call of the interrupt routine, you shift the arrray values down one, and then store the value of "now" in the 1st position. You then calculate the elapsed time using the first and last value in the array and divide by one less than the number of elements in the array. I can write an example routine for this method as well, if you would like for me to.

To display the RPM to the nearest 25:

RPM += 12;
RPM /= 25;
RPM *= 25;

Since RPM is a long int, when you divide by 25 it drops the fractional part, so divide by 25, then multiply by 25, rounds down to the next 25. By adding 12 first, you round to the nearest 25.

Generally, to display the RPM to the nearest x:

RPM += (x/2);
RPM /= x;
RPM *= x;

@ robmaples

Thank you for your valuable suggestions… :slight_smile:

It would be kind of you if you show me an example or I would not mind if you modify mi code… :slight_smile:

Concerning the number of points to average: Since this is a one pulse per revolution engine, it's not critical. With multiple pulses per revolution, you ideally want a multiple of the number of pulses per revolution. For example, an eight cylinder four stroke engine would have four pulses per revolution, so you would want to average over four, eight, twelve, etc. pulses. This will make sure you are calculating off of full revolutions and will help smooth out fluctuations caused by horsepower disparities between cylinders.

If the speed of the calculations is critical, then it is a good idea to use a power of two for the number points to average over(2, 4, 8, 16, ...). The reason being instead of dividing to get the average you can bit shift, which is much faster. For example, if you want to average over eight points, instead of dividing by eight you can shift the number three bits to the right.

Joy: @ robmaples

Thank you for your valuable suggestions.. :)

It would be kind of you if you show me an example or I would not mind if you modify mi code... :)

Will do.

robmaples: To display the RPM to the nearest 25:

RPM += 12;
RPM /= 25;
RPM *= 25;

If you are doing this way. then why dot do it just this way...

if ( RPM % 25 == 0)
   {
   lcd.print(RPM);
   }

else
{
// I dont know what to write here.. :P
}

Joy:

robmaples: To display the RPM to the nearest 25:

RPM += 12;
RPM /= 25;
RPM *= 25;

If you are doing this way. then why dot do it just this way...

if ( RPM % 25 == 0)
   {
   lcd.print(RPM);
   }

else { // I dont know what to write here.. :P }

The method you showed will only print the RPM if it is a multiple of 25. You want to print the RPM every pass, but round it to the nearest 25 before printing.

Here is your code modified to calculate the rpm over eight pulses, update it on each pulse, and to round the result to the nearest 25 before displaying it. I also noticed when doing this that you were passing rpm, which gets changed by the interrupt routine, to lcd.print. This can cause problems if rpm gets changed in the middle of lcd.print. I added a variable displayRPM to copy rpm to for passing it to lcd.print.

const int ignitionPin = 2;
const int ignitionInterrupt = 0;
const unsigned int pulsesPerRev = 1;

unsigned long lastPulseTime = 0;
unsigned long rpm = 0;

unsigned long pulseArray[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
unsigned long displayRPM = 0;

void ignitionIsr()
{
  unsigned long now = micros();
  unsigned long interval;

  // shift array values down 1 position
  for (int i=7; i>0; i--)
  {
    pulseArray[i+1] = pulseArray[i]; 
  }
  pulseArray[0] = now;  

  
  interval = pulseArray[0]- pulseArray[8];
  interval >>= 3;  // divide by 8 by shifting result right 3 bits

  if (interval > 2000)
  {
     rpm = 60000000UL/(interval * pulsesPerRev);
     lastPulseTime = now;
  }  
}

void setup()
{
  lcd.begin(16,2);
  pinMode(ignitionPin, INPUT);
  attachInterrupt(ignitionInterrupt, &ignitionIsr, FALLING);
}

void loop(){

  // capture value of rpm
  noInterrupts();
  displayRPM = rpm;
  interrupts();

  // the next 3 lines would round displayRPM to the nearest 25
  displayRPM +=12;
  displayRPM /= 25;
  displayRPM *= 25;

  lcd.setCursor(0, 0);
  lcd.print("RPM- ");
  lcd.print(rpm);
  delay(200);

}