Arduino Tachometer Problem

Cattledog, Thank you for your help man!
Turns out the tachometer acts very different on the engine vs a perfect Arduino spark emulator.

Here's what I've tried and learned:

  1. I tried every capacitor I have and they all had no positive affect on filtering even when paired with a resistor for a proper RC filter. The rpm would be very erratic.

  2. Only resistor combinations between the input and ground helped provide a stable rpm reading.

  3. I tried 10k, 1k, 820, 390, 330, 270, 220 180, 150, 100, 82, 68, 51 and 33 ohm combinations at idle and wide open throttle to test low and high end. I have notes on how each combination responded.

  4. The best resistance is actually 82 ohm with a 10 ohm in series (92 ohm). It works flawlessly on the equipment, showing actual rpm at all ranges. This is confirmed with two known working tachometers.

  5. I added the ICNC and tested it this morning and everything still works perfectly.

I want to add the tachometer as a function in a bigger program so my next test is to add it in and see if it works.

Thanks again,
Laggy

Ok so I am trying to avoid using delay with the ICR but using the millis method isn't working for some reason. Is there something I'm doing wrong? I've tried several methods of doing it. Here's the code:

#include <Arduino.h>
#include <avr/interrupt.h>
#include <LiquidCrystal.h>
void setup();
void loop();
unsigned long timer;
byte counter;

LiquidCrystal lcd (5,6,9,10,11,12);
const int led = 7;
const int Backlight = 4;
const int inputCapturePin = 8;
const int prescale = 64;
const byte prescaleBits = B011;
const long precision = (1000000/(F_CPU/1000.0)) * prescale;
const int numberOfEntries = 6;		
const int gateSamplePeriod = 1000;
int timerTest = millis();
volatile byte index = 0;
volatile byte gate = 0;
volatile unsigned int results[numberOfEntries];
unsigned long rpm;


/* ICR INTERRUPT VECTOR */
ISR(TIMER1_CAPT_vect)
{
	TCNT1 = 0;
	if(gate)	{
		if(index != 0 || bitRead(TCCR1B,ICES1) == true){
			if(index < numberOfEntries){
				results[index] = ICR1;
				index++;
			}
		}
	}
	TCCR1B ^= _BV(ICES1);
}

void setup(){
	lcd.begin(16,2);
	pinMode(led,OUTPUT);
	pinMode(Backlight,OUTPUT);
	pinMode(inputCapturePin,INPUT_PULLUP);
	digitalWrite(Backlight,HIGH);
	digitalWrite(inputCapturePin,HIGH);
	
	lcd.home();
	lcd.print("Tachometer13:");
	
	TCCR1A = 0;
	TCCR1B = prescaleBits;
	TCCR1C = 0;
	bitSet(TCCR1B,ICES1);
	bitSet(TCCR1B,ICNC1);		//activate the input capture noise cancellation
	bitSet(TIFR1,ICF1);
	bitSet(TIMSK1,ICIE1);
}

void loop(){
	index = 0;
	gate = 1;
	//delay(gateSamplePeriod);				//Don't want to use delay
	if (millis() - timer >= 1000){
		timer = millis();		
	gate = 0;
	if (index > 0)
	{
		for(byte i=0; i < index; i++)
		{
			long duration;			
			duration = results[i] * precision;
			if(duration > 0){
				float actualDuration = duration/1000000.0;		
				float rpm = (1000/actualDuration*60);
	
				lcd.setCursor(0,1);
				lcd.print(rpm*2,0);
				lcd.print(" RPM      ");
				
				results[i] = 0;
			}
		}
		index = 0;
	}
	}
}

C isn't my strongest language and I've only started writing
code in it about 2 months ago.
Still, I think that:

float actualDuration = duration/1000000.0;

Should be written:

float actualDuration = float(duration)/1000000.0;

You have a number of calculations where it isn't clear what the
variables are, floating or integers.
Also, you ISR is too complicated for an interrupt. I'm not saying it won't work,
even though I don't fully understand what it is doing, just that it is not what
an interrupt should be doing. Way too much work.
Dwight

Hi,
Exactly how have you got the wire to the base resistor connected to the spark plug?

The voltage off the spark plug will not be a pulse, but a firing spike followed by a couple of smaller but in the case of the arduino significant oscillations.
The 4.7V zener will not protect the transistor or the arduino as only more than 0.7V is required to damage the B-E junction.

Tom... :slight_smile:

dwightthinker:
Still, I think that:

float actualDuration = duration/1000000.0;

Should be written:

float actualDuration = float(duration)/1000000.0;

Why?

Hi, again.

Try this circuit to get your spark signal.
It uses a toroidal ring, any type will do as long as you can feed the sparkplug lead through it and wind 5 or more turns of insulated wire on it.
The number of turns may have to be adjusted for your application.
Using a ring provides isolation between the HI VOLTAGE spark circuit and the LOW VOLTAGE arduino circuit.
It also works by responding to current in the lead, which only flows when the spark plug fires.
If the pair of wires from the ring to the circuit are long, ie > 20cm, then twist them together to counteract any other noise.

Tom.... :slight_smile:

You have a number of calculations where it isn't clear what the
variables are, floating or integers.

-Can you point these out for me so I can fix them?

Also, you ISR is too complicated for an interrupt. I'm not saying it won't work,
even though I don't fully understand what it is doing, just that it is not what
an interrupt should be doing. Way too much work.

-This is exactly how the datasheet says to use an interrupt for fast and accurate pulse measurement. Can you provide an example that may suit this application better?

Exactly how have  you got the wire to the base resistor connected to the spark plug?

-See attachment. Sparkpad is a wire with a clamp that clamps onto the spark plug wire. It used to be wrapped but using a clamp provided more consistent results from different engines. SPKT is input to arduino.

Try this circuit to get your spark signal.
  • Thanks for the suggestion, I'll save that. For right now I already have an idea that I want to work that uses a clamp around the spark plug instead of wrapping a wire around. It produced better results across different (big vs small) engines.

Thanks for the replies! Now, how do I get this to work without using delay?

Connectinos.JPG

aarg:
Why?

Like I said, I'm new at C. I just thought it would take out any confusion
as to what to cast the numbers to before doing the calculation.
I don't fully understand how it deals with type conversion.

Dwight

 TCCR1B ^= _BV(ICES1);

The edge detection toggle must be elimintated from your code. We went through this a few post ago. That line should be commented out., or else you will be reading the high and low periods of the signal. You want to be determining the time between the same edge of a repeating pulse. It's also important because the logic within the ISR relays on ICES1 being set when the toggle is gone.

The reason why the version without the delay is not working is that you set index=0 at the start of every loop, and then your calculation follows a conditional test of if (index > 0)

I think you can modify the code as by just commenting the line which begins the loop. Index is then reset to 0 after the calculation.

#include <Arduino.h>
#include <avr/interrupt.h>
#include <LiquidCrystal.h>
void setup();
void loop();
unsigned long timer;
byte counter;

LiquidCrystal lcd (5, 6, 9, 10, 11, 12);
const int led = 7;
const int Backlight = 4;
const int inputCapturePin = 8;
const int prescale = 64;
const byte prescaleBits = B011;
const long precision = (1000000 / (F_CPU / 1000.0)) * prescale;
const int numberOfEntries = 6;
const int gateSamplePeriod = 1000;
int timerTest = millis();
volatile byte index = 0;
volatile byte gate = 0;
volatile unsigned int results[numberOfEntries];
unsigned long rpm;


/* ICR INTERRUPT VECTOR */
ISR(TIMER1_CAPT_vect)
{
  TCNT1 = 0;
  if (gate)	{
    if (index != 0 || bitRead(TCCR1B, ICES1) == true) {
      if (index < numberOfEntries) {
        results[index] = ICR1;
        index++;
      }
    }
  }
  //TCCR1B ^= _BV(ICES1);
}

void setup() {
  lcd.begin(16, 2);
  pinMode(led, OUTPUT);
  pinMode(Backlight, OUTPUT);
  pinMode(inputCapturePin, INPUT_PULLUP);
  digitalWrite(Backlight, HIGH);
  digitalWrite(inputCapturePin, HIGH);

  lcd.home();
  lcd.print("Tachometer13:");

  TCCR1A = 0;
  TCCR1B = prescaleBits;
  TCCR1C = 0;
  bitSet(TCCR1B, ICES1);
  bitSet(TCCR1B, ICNC1);//activate the input capture noise cancellation
  bitSet(TIFR1, ICF1);
  bitSet(TIMSK1, ICIE1);
}

void loop() {
  //index = 0;
  gate = 1;
  //delay(gateSamplePeriod);//Don't want to use delay
  if (millis() - timer >= 1000) {
    timer = millis();
    gate = 0;
    if (index > 0)
    {
      for (byte i = 0; i < index; i++)
      {
        long duration;
        duration = results[i] * precision;
        if (duration > 0) {
          float actualDuration = duration / 1000000.0;
          float rpm = (1000 / actualDuration * 60);

          lcd.setCursor(0, 1);
          lcd.print(rpm * 2, 0);
          lcd.print(" RPM      ");

          results[i] = 0;
        }
      }
      index = 0;
    }
  }
}

There is a structural problem with the code which can be fixed when you have worked out the delay issue. The code is currently creating an array of results[numberOfEntries] which will hold 6 values. I think you are displaying each value, instead of averaging them.

Essentially, the code reads six values from the timer, either during the gated delay period or while the millis() timer interval is running. At 1200 rpm you have 20 pulses/sec and the six pulses are read in 300 ms.

Given how the code has been modified from what you picked up when you went to the input capture scheme there are several things which can be cleaned up relating to the gate, the index, and some conditional tests.

^ If this is the case, then as I mentioned a while ago you should use a moving average. It looks like you are resetting the index variable then running through the array again and again.

Instead you move all values in the array down one index, dropping the earliest value, and place your newly acquired value in the open spot. FIFO arrangement, or a queue. Then each time you acquire a new value you average the whole array and spit out that number.

Other wise, again you will just be estimating revolutions-per-minute based off of a much smaller interval, which creates wild errors.

Without averaging or other filtering techniques you are linearly relating rev/sec & rev/minute which is incorrect.

Mr_Laggy:

You have a number of calculations where it isn't clear what the

variables are, floating or integers.



-Can you point these out for me so I can fix them?

They may not be errors. I am new to C myself. I looked at the reference
and see that if one of the values is a float, the calculation is done in floating point.
You have such a case where you have the value 8 but have it as an integer.
The compiler turns it into a floating point to do the operation. Since you only use
it in a calculation that converts it to a float, there is not likely any space save
declaring it as a int. The operation becomes a complier operation that doesn't
look like it executed in run time anyway.
Dwight

Mr_Laggy

Here is a simplified version of your code.

I have made it less generic and cleaned up the unnecessary variables and renamed others for clarity. I have added an averaging function. I have used some multiplication and division to eliminate floating point calculations.

The code uses an integrated test pulse generator to show of what the code is doing. Jumper pin 11 to pin 8.

It uses serial printing for demonstration purposes, and you should be able to convert it back to your lcd fairly easily.

Hopefully it will be easier for you to see the program logic than with what you inherited from the internet.

const int inputCapturePin = 8;
const int prescale = 64;//gives 4us per timer tick @ 16Mhz
const byte prescaleBits = B011;//sets prescale 64 on timer 1
const byte microsPrecision = (prescale * (1000 * .0625) / 1000);//4;
const byte numberOfEntries = 6;

volatile byte index = 0;
volatile unsigned int timerCount[numberOfEntries];

long rpm;
long sumRPM;

unsigned long lastRead;
unsigned long interval = 1000;

ISR(TIMER1_CAPT_vect)//stores the timer count from rising edge to rising edge in an array
{
  TCNT1 = 0;
  if (index < numberOfEntries) {
    timerCount[index] = ICR1;
    index++;
  }
}

void setup() {
  Serial.begin(115200);

  pinMode(inputCapturePin, INPUT_PULLUP);

  Serial.print("Tachometer13:");
  Serial.println(microsPrecision);

  //Setup Timer 1
  TCCR1A = 0;//initialize register
  TCCR1B = 0;//initialize register

  TCCR1B = prescaleBits;
  bitSet(TCCR1B, ICES1);//input capture edge select RISING
  bitSet(TCCR1B, ICNC1);//activate the input capture noise cancellation
  //bitSet(TIFR1, ICF1);//input capture flag  flag is automatically set
  bitSet(TIMSK1, ICIE1);//input capture interrupt enable

  //Setup Timer 2 to provide test pulse on pin 11.  Jumper D11 to D8 (icp pin)
  //Provide 30.625Hz pwm signal on pin 11 
  //Default frequency 490/16 = 30.625 HZ gives 1837.5 rpm
  TCCR2B = B111;//sets prescaler to 1024 from default 64 gives 64 us/tick
  pinMode(11, OUTPUT);//pwm on pin D11
  analogWrite(11, 2); // .128ms pulse width every 32.6ms
}

void loop() {
  if (millis() - lastRead >= interval) {
    lastRead += interval;
    if (index > 0)
    {
      for (byte i = 0; i < index; i++)
      {
        long duration;
        duration = timerCount[i] * microsPrecision ;
        if (duration > 0) {
          //Serial.println(duration);//microseconds
          //multiply by 100 to produce 4 digit rpm with integer math
          rpm = (100 * 1000000L / duration) * 60;
          rpm = rpm / 100;

          sumRPM += rpm;

          Serial.print(rpm);
          Serial.print("  RPM");
          Serial.println(i);//0 thru 5 index numbers

          timerCount[i] = 0;
        }
      }
      Serial.print("Average rpm =  ");
      Serial.println(sumRPM / numberOfEntries);
      index = 0;
      sumRPM = 0;
    }
  }
}

Edit: 9:30 pm--Revised variable names and arrangement of millis() timer for reading every second

Here is a simplified version of your code.

Thanks for the code, it works better than what I was using before. What I am noticing now is that when the engine is at idle (~1900 RPM) every random amount of seconds the tachometer will read 2500 or 2700ish RPM for a split second. Likewise when the engine is at Wide Open Throttle (~3600 RPM) every now and then the tachometer reading will spike (but the engine wont) up to 4600 RPM. Why is that? I've tried every combination of capacitors and resistors to create a filter and the combination that works best is an 82 ohm resistor with a 10 ohm resistor in series (~92 ohm). Any higher or lower in resistance and there is a dramatic increase in errors. Note that these spikes are not shown on the other two tachometers that I am using for reference.

Improvements I would like to learn how to make:

  1. Eliminate the random spikes
  2. Round the rpm up or down to the nearest 10th (ie 2343 RPM = 2340 RPM & 2347 RPM = 2350 RPM)

Thanks,
Laggy

Mr_Laggy

Here is some code with a five period moving average and rounding to the nearest 10 count.

There is probably some additional software filtering you can do by disallowing readings which vary from their previous reading by some threshold.

But first, evaluate what the simple moving average and rounding look like on the machine. You can certainly tune the values, by averaging more or less if its too sluggish or too responsive.

const int inputCapturePin = 8;
const int prescale = 64;//gives 4us per timer tick @ 16Mhz
const byte prescaleBits = B011;//sets prescale 64 on timer 1
const byte microsPrecision = (prescale * (1000 * .0625) / 1000);//4;
const byte numberOfEntries = 6;

volatile byte index = 0;
volatile unsigned int timerCount[numberOfEntries];

long rpm;
long sumRPM;



unsigned long timer;
unsigned long interval = 1000;

ISR(TIMER1_CAPT_vect)
{
  TCNT1 = 0;
  if (index < numberOfEntries) {
    timerCount[index] = ICR1;
    index++;
  }
}

void setup() {
  Serial.begin(115200);

  pinMode(inputCapturePin, INPUT_PULLUP);

  Serial.print("Tachometer13:");
  Serial.println(microsPrecision);

  //Setup Timer 1
  TCCR1A = 0;
  TCCR1B = 0;
  TCCR1B = prescaleBits;
  bitSet(TCCR1B, ICES1);//input capture edge select RISING
  bitSet(TCCR1B, ICNC1);//activate the input capture noise cancellation
  //bitSet(TIFR1, ICF1);//input capture flag  flag is automatically set
  bitSet(TIMSK1, ICIE1);//input capture interrupt enable

  //Setup Timer 2 to provide test pulse on pin 11
  //Provide 30.625Hz pwm signal on pin 11 Jumper 11 to icp pin 8
  //Default frequency 490/16 = 30.625 HZ gives 1837.5 rpm
  TCCR2B = B111;//sets prescaler to 1024 from default 64 gives 64 us/tick
  pinMode(11, OUTPUT);//pwm on pin D11
  analogWrite(11, 2); // .128ms pulse width every 32.6ms
}

void loop() {
  if (millis() - timer >= interval) {
    timer += interval;
    if (index > 0)
    {
      for (byte i = 0; i < index; i++)
      {
        long duration;
        duration = timerCount[i] * microsPrecision ;
        if (duration > 0) {
          //Serial.println(duration);//microseconds
          //multiply by 100 to produce 4 digit rpm with integer math
          rpm = (100 * 1000000L / duration) * 60;
          rpm = rpm / 100;

          sumRPM += rpm;
   
          Serial.print(rpm);
          Serial.print("  RPM");
          Serial.println(i);//0 thru 5 index numbers

          timerCount[i] = 0;
        }
      }
      Serial.print("Period Average rpm =  ");
      Serial.println(sumRPM / numberOfEntries);
      index = 0;
      
      //now make a moving average and round to nearest 10 count
      static long movingAveSumRpm = sumRPM / numberOfEntries;//initilize with first value
      movingAveSumRpm = (movingAveSumRpm*4 + (sumRPM/numberOfEntries))/5;//update
      sumRPM = 0;//reset value for next period
      
      int roundedRpm = (movingAveSumRpm +5)/10 * 10;//integer math to round to nearest 10
      
      Serial.print("Five Period Moving Average rpm =  ");
      Serial.println(movingAveSumRpm);
      Serial.print("Five Period Moving Average rpm rounded to nearest 10  ");
      Serial.println(roundedRpm);
    }
  }
}

Hi,

  1. Eliminate the random spikes

Try the circuit in post #45.
It responds to spark current, not voltage that the capacitive pickup that you have will be doing.

Tom.... :slight_smile:

There is probably some additional software filtering you can do by disallowing readings which vary from their previous reading by some threshold.

  • I think I've finally reached that point. I have the tachometer working really well and responsive when just printing rpm. Printing MovingAveSumRpm or RoundingRpm are a bit too slow and I couldn't seem to improve the responsiveness very much so I've removed them for now. I still need to round the numbers but I couldn't get that part working properly again. Ugh

Every now and then the rpm will skip to something like 179223452 but the engine only operates between 1800 and 3600 so I've made a simple if statement to clear the lcd when an rpm above 4000 occurs. Now I've realized that this causes the lcd to stay blank for a second or so every now and then and doesn't look good so I want to make it display something like this:

if rpm >= say4000
display last rpm value

Here's what I have so far:

#include <LiquidCrystal.h>
LiquidCrystal lcd (5,6,9,10,11,12);

const int led = 7;
const int Backlight = 4;
const int inputCapturePin = 8;
const int prescale = 64;//gives 4us per timer tick @ 16Mhz
const byte prescaleBits = B011;//sets prescale 64 on timer 1
const byte microsPrecision = (prescale * (1000 * .0625) / 1000);//4;
const byte numberOfEntries = 6;

volatile byte index = 0;
volatile unsigned int timerCount[numberOfEntries];
long rpm;
long sumRPM;
unsigned long timer;
unsigned long interval = 1000;

ISR(TIMER1_CAPT_vect)
{
  TCNT1 = 0;
  if (index < numberOfEntries) {
    timerCount[index] = ICR1;
    index++;
  }
}

void setup() {
	lcd.begin(16,2);
	pinMode(led,OUTPUT);
	pinMode(Backlight,OUTPUT);
	pinMode(inputCapturePin, INPUT_PULLUP);
	
	digitalWrite(led,LOW);
	digitalWrite(Backlight,HIGH);
	
    pinMode(inputCapturePin, INPUT_PULLUP);
	
  //Setup Timer 1
  TCCR1A = 0;
  TCCR1B = 0;
  TCCR1B = prescaleBits;
  bitSet(TCCR1B, ICES1);//input capture edge select RISING
  bitSet(TCCR1B, ICNC1);//activate the input capture noise cancellation
  bitSet(TIMSK1, ICIE1);//input capture interrupt enable

  //Setup Timer 2 to provide test pulse on pin 11
  //Provide 30.625Hz pwm signal on pin 11 Jumper 11 to icp pin 8
  //Default frequency 490/16 = 30.625 HZ gives 1837.5 rpm
  TCCR2B = B111;//sets prescaler to 1024 from default 64 gives 64 us/tick
}

void loop() {
	lcd.setCursor(0,0);
	lcd.print("Tachometer:");
  if (millis() - timer >= interval) {
    timer += interval;
    if (index > 0)
    {
      for (byte i = 0; i < index; i++)
      {
        long duration;
        duration = timerCount[i] * microsPrecision ;
        if (duration > 0) {
         
          //multiply by 100 to produce 4 digit rpm with integer math
          rpm = (100 * 1000000L / duration) * 60;
          rpm = rpm / 100;
		  
		  lcd.setCursor(0,1);
		  lcd.print(rpm*2);					
		  lcd.print(" RPM         ");
          timerCount[i] = 0;
		  
		  if (rpm >= 4000){	//Causes the screen to go blank too long sometimes
			  lcd.clear();		  
		  }
		  
        }
      }
      index = 0;
    }
  }
}

I really do not think you should remove the average of the 6 readings in a second. I have modified the program to round the six reading average to the nearest 10, and only print values less than 4000. If there is a defective value over 4000, the lcd will continue to display the previous seconds value. I have also moved unchanging aspects of the display into setup for better lcd management.

Why are you multiplying rpm by 2? I'm confused by that, and you may need to modify the math on what to display.

You really should consider the signal pickup change proposed by Tom George.

#include <LiquidCrystal.h>
LiquidCrystal lcd (5, 6, 9, 10, 11, 12);

const int led = 7;
const int Backlight = 4;
const int inputCapturePin = 8;
const int prescale = 64;//gives 4us per timer tick @ 16Mhz
const byte prescaleBits = B011;//sets prescale 64 on timer 1
const byte microsPrecision = (prescale * (1000 * .0625) / 1000);//4;
const byte numberOfEntries = 6;

volatile byte index = 0;
volatile unsigned int timerCount[numberOfEntries];
long rpm;//individual reading
long sumRPM; //numberofEntries readings in a second
long periodAverageRPM; //average of the last second
unsigned long timer;
unsigned long interval = 1000;

ISR(TIMER1_CAPT_vect)
{
  TCNT1 = 0;
  if (index < numberOfEntries) {
    timerCount[index] = ICR1;
    index++;
  }
}

void setup() {
  lcd.begin(16, 2);
  pinMode(led, OUTPUT);
  pinMode(Backlight, OUTPUT);
  pinMode(inputCapturePin, INPUT_PULLUP);

  digitalWrite(led, LOW);
  digitalWrite(Backlight, HIGH);

  pinMode(inputCapturePin, INPUT_PULLUP);

  //Setup Timer 1
  TCCR1A = 0;
  TCCR1B = 0;
  TCCR1B = prescaleBits;
  bitSet(TCCR1B, ICES1);//input capture edge select RISING
  bitSet(TCCR1B, ICNC1);//activate the input capture noise cancellation
  bitSet(TIMSK1, ICIE1);//input capture interrupt enable

  //Setup Timer 2 to provide test pulse on pin 11
  //Provide 30.625Hz pwm signal on pin 11 Jumper 11 to icp pin 8
  //Default frequency 490/16 = 30.625 HZ gives 1837.5 rpm
  //TCCR2B = B111;//sets prescaler to 1024 from default 64 gives 64 us/tick
  //pinMode(11, OUTPUT);//pwm on pin D11
  //analogWrite(11, 2); // .128ms pulse width every 32.6ms


  //place unchanging parts of display in setup
  lcd.setCursor(0, 0);
  lcd.print("Tachometer:");

}

void loop() {

  if (millis() - timer >= interval) {
    timer += interval;
    if (index > 0)
    {
      for (byte i = 0; i < index; i++)
      {
        long duration;
        duration = timerCount[i] * microsPrecision ;
        if (duration > 0) {

          //multiply by 100 to produce 4 digit rpm with integer math
          rpm = (100 * 1000000L / duration) * 60;
          rpm = rpm / 100;

          sumRPM += rpm;
          timerCount[i] = 0;

        }
      }

      periodAverageRPM = sumRPM / numberOfEntries; //one second average

      index = 0; //reset values for next period
      sumRPM = 0;//reset values for next period

      periodAverageRPM = (periodAverageRPM + 5) / 10 * 10; //integer math to round to nearest 10

      if (periodAverageRPM < 4000)
      {
        lcd.setCursor(0, 1);
        lcd.print(periodAverageRPM * 2);
        lcd.print(" RPM         ");

      }

      periodAverageRPM = 0;

    }
  }
}

Why are you multiplying rpm by 2?

  • Because I noticed that when I use a certain filter (capacitor/resistor combo) the rpm is halved, but with other combinations it actually displays properly. I am finally using it without X2 now that I have the correct filter.

The code you posted is perfect once I adjusted the filter. This is exactly what I was looking to do when I set out with this post. Only modification was uncommenting "TCCR2B = B111;".

For any who need it, my final filter combo was a 473M capacitor and an Orange, White, Brown (390ohm) resistor. These are typically placed in parallel between the spark input and ground but I found a better outcome from using them in series between the two.

I found other successful filter combinations using a variation of 100uf, 104M, 473M, and .033Z capacitors with resistance between 82 and 510 ohms.

Thank you Cattledog!

I'm glad all is well.

Only modification was uncommenting "TCCR2B = B111;".

I don't understand this. Timer2 was only used to generate a test signal to debug the code. In your actual application on the machine, it should not be needed.