How to achieve accurate low RPM readings

Hey folks.
I'm struggling to figure out how to display an accurate RPM reading with a hall effect sensor, for a project I've got going.
All of the examples I've seen, show the concept works, but readings tend to be all over the place (+/- 100 RPM) which is unusable to me.

I started by trying to modify this example code, which appears to be for RPM's up to 10,000, using one magnet.

/*
  RPM meter code (tested on device rotating at 10,000 rpm)
  rev.001
  * using a hall effect sensor
  http://www.adafruit.com/index.php?main_page=product_info&cPath=35&products_id=158
  * using a parallel LCD with
  LiquidCrystal Library
  http://www.ladyada.net/learn/lcd/charlcd.html
  http://www.arduino.cc/en/Tutorial/LiquidCrystal
  with pins 7,6,5,4,3,2
*/

// include the library code:
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7,6,5,4,3,2);
// read the hall effect sensor on pin 2
const int hallPin=8;
const unsigned long sampleTime=1000;
const int maxRPM = 10200;

void setup()
{
  pinMode(hallPin,INPUT);
  Serial.begin(9600);
  // set up the LCD's number of rows and columns:
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("initializing");
  delay(1000);
  lcd.clear();
}

void loop()
{
  int rpm=getRPM();
  lcd.clear();
  displayRPM(rpm);
  displayBar(rpm);
}
 
int getRPM()
{
  // sample for sampleTime in millisecs
  int kount=0;
  boolean kflag=LOW;
  unsigned long currentTime=0;
  unsigned long startTime=millis();
  while (currentTime<=sampleTime)
  {
    if (digitalRead(hallPin)==HIGH)
    {
      kflag=HIGH;
    }
    if (digitalRead(hallPin)==LOW && kflag==HIGH)
    {
      kount++;
      kflag=LOW;
    }
    currentTime=millis()-startTime;
  }
  int kount2rpm = int(60000./float(sampleTime))*kount;
  return kount2rpm;
}
   
void displayRPM(int rpm)
{
  lcd.clear();
  // set the cursor to column 0, line 1
  lcd.setCursor(0, 0);
  // print the number of seconds since reset:
  lcd.print(rpm,DEC);
  lcd.setCursor(7,0);
  lcd.print("RPM");
}

void displayBar(int rpm)
{
  int numOfBars=map(rpm,0,maxRPM,0,15);
  lcd.setCursor(0,1);
  if (rpm!=0)
  {
  for (int i=0; i<=numOfBars; i++)
   {
        lcd.setCursor(i,1);
        lcd.write(1023);
      }
  }
}

I've added 50 magnets, to a larger surface area (for a little more time between interrupts) and am trying to get accurate readings starting from around 400 RPM, to calculate the time it takes for a flywheel to free-spin down to around 50 RPM.

Am I on the right track with more interrupts per revolution?
Is float math the right way to go about this?
Would an optical sensor, with alternating white/black lines on the axle, be more accurate?

I haven't made much progress on this in the last couple weeks, and it's a little frustrating.

Could anyone point me in the right direction?
.

Hi!
I fased the same question ages ago, 30 years..
My project was to measure the RPM of the 4 cylinder engine in my car picking up the ignitions.
One question is how frequently You want to now the RPM.
Counting pulses at high speed ought to work well, giving the answer in a rather short time.
For low speed, measuring the time between pulses might work better.
In my project i wanted the RPM measured twice per second. I think I used a high speed clock triggering a hardware counter that was red by the program.
Sit down and calculate the number of pulses coming during Your measuring time, for the low speed and for the high speed and see what You can expect.
Many magnets and pulse counting or few magnets and time measuring.
Good luck,
Railroader.

Sorry. Reading Your question again I see You want low speed measurements.
Use a low number of magnets and a loop with a short delay that has a loop count and is counting the time from one magnet to the other. My suggestion...
//Railroader

Steel disc you have the magnets mounted on may affect what you expect from the magnets.

To accurately measure RPM, you need to accurately measure one shaft revolution, and accurately measure the time for one shaft resolution. Those are completely different measurements.

Which measurement is the least accurate in your case?

Adding "more magnets" does not help, interrupts introduce unpredictable timing errors (latency) and Hall effect sensors are noisy and unpredictable, especially when used with several different magnets.

For these and other reasons, most people use optointerrupters and slotted disks to accurately measure RPM.