Accuracy of Readout on IR RPM Tachometer

Please see the code below. I’m beyond new to Arduino so I’m sure there are a multitude of errors and I’m simply hijacking code from a tutorial so I’m sure there is a better way. I’m trying to Serial.write the rpm of the device to my serial monitor and I’ll worry about LCD later as I’ve got that more or less figured out. The problem is, I’m getting the output RPM to show as 30.00 or 60.00 but it’s always based on multiples of 30. I’ve realized for low RPM my formula might have to take on longer sample times, but I don’t want the screen to only update every five seconds or so. Thoughts on how to achieve this? Thanks so much.

/*
 * Optical Tachometer
 * Calculates RPM of a 2 Blade Rotary Device (Low RPM 0-100)
 * Uses an IR LED and IR phototransistor to implement an optical tachometer.
 * The IR LED is connected to pin 13 and ran continually.
 * Pin 2 (interrupt 0) is connected across the IR detector.
 */

int ledPin = 13;                // IR LED connected to digital pin 13
volatile float rpmcount;
volatile float rpm;
unsigned long timeold;

// include the library code:
//#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
//LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

void rpm_fun()
 {
   //Each rotation, this interrupt function is run twice, so take that into consideration for 
   //calculating RPM
   //Update count
      rpmcount++;
 }

void setup()
 {
   Serial.begin(9600);  // intialise the LCD

   //Interrupt 0 is digital pin 2, so that is where the IR detector is connected
   //Triggers on FALLING (change from HIGH to LOW)
   attachInterrupt(0, rpm_fun, FALLING);

   //Turn on IR LED
   pinMode(ledPin, OUTPUT);
   digitalWrite(ledPin, HIGH);

   rpmcount = 0;
   rpm = 0;
   timeold = 0;
 }

 void loop()
 {
   //Update RPM every second
   delay(1000);
   //Don't process interrupts during calculations
   detachInterrupt(0);
   //Note that this would be 60*1000/(millis() - timeold)*rpmcount if the interrupt
   //happened once per revolution instead of twice. Other multiples could be used
   //for multi-bladed propellers or fans
   rpm = 30*1000/(millis() - timeold)*rpmcount;
   timeold = millis();
   rpmcount = 0;

   //Print out result to lcd

   Serial.print("RPM=");
   Serial.println(rpm);

   //Restart the interrupt processing
   attachInterrupt(0, rpm_fun, FALLING);
 }

siebiscuit: The problem is, I'm getting the output RPM to show as 30.00 or 60.00 but it's always based on multiples of 30. I've realized for low RPM my formula might have to take on longer sample times, but I don't want the screen to only update every five seconds or so. Thoughts on how to achieve this? Thanks so much.

Increase the number of pulses per revolution. If you have an optical sensor, for example, have more than one stripe on the wheel.

There isn't any other way. While there is no pulse, the RPM is indeterminate. You can only know the RPM from the last two pulses.

But the sketch has a very stupid way of calculating period. Instead of measuring the period between pulses and calculating the inverse, it counts pulses during a fixed period. You can fix that, and see a huge improvement.

for measuring RPM try many samples times over a period of time and then use a running average for the time. Hint: think ARRAYs.

Your problem of 30's has to do with your using integer division... truncation towards zero.

you are on the right track as far as using integers (avoiding using floating point division) to do your calculations. There are tons of examples on how to do what you want here on the forum.

Are you saying I should be using int instead of float? Or vice versa. I used float for my sample code.

Now I'm thinking rpm = 60000 / (millis() - timeold);

   rpm = (float)30*1000/(millis() - timeold)*rpmcount;

…but in case you didn’t catch on, the whole counting approach is useless. It is only useful for much higher RPM’s. Start from scratch. Measure individual measurement periods and then take the inverse to give RPM. Anything else will be either horribly inaccurate, or horribly slow to produce a reading.

So a good start would be an ISR that just captures the value of millis().