KY-024 Hall effect sensor for RPM's under 400

Hey folks, I’m trying to use a KY-024 hall effect sensor, to accurately measure the RPM’s of a flywheel project I’m working on.

I found some code, but it doesn’t seem to be too reliable. I tried testing with a drill at a constant speed, and the readings were all over the place.

I tried messing with the sample time, but no luck.

I thought maybe adding two magnets, on opposite ends, but I’m not sure how to adjust the code.

Was hoping for a hand over this little roadblock.

Thanks in advance for any assistance!

/*
  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() 
{
  delay(10);
  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);
      }
  }
}

This

void loop() 
{
  delay(10);

guarantees you are going to miss some readings.
Get rid of delay(10);
Consider only writing to the display only when things change.

Thanks CrossRoads, I removed the delay. It did help some, but I'm still getting erratic readings.

I was wondering if adding more magnets around the axle, and adjusting the code, would provide a more stable reading.

Also one more thought.
I have 6 more hall effect sensors. Would adding another sensor or two benefit me with a more accurate updated RPM value?

Eventually my goal is to be able to plot accurate graphs of Time versus RPM, so RPM accuracy is important.

I am open to any suggestions to help my project along the way.

Thanks!

If I could provide any other information, I'd be happy to.

To recap, I'm simply looking for a way to use a hall effect sensor to accurately measure the RPM's of an axle under 400 RPM, and display on a 16x2 LCD.

I've tried looking for an answer, but am coming up short.

Just hoping someone can guide me towards a working solution, and I can try to take it from there.

Thanks again!

I tried testing with a drill at a constant speed, and the readings were all over the place.

I question your test method and signal quality more than the code.

Here’s some code which tests your code with a pulse generated with the TimerOne library.
https://www.pjrc.com/teensy/td_libs_TimerOne.html

It’s set up for 480 rpm with a pulse which is approximately 600 microseconds in width. The display results are pretty stable.

Do you have an oscilloscope to verify the quality of the pulse coming from your hall sensor?

/*
  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; 

#include <TimerOne.h>
const byte pulsePin = 9;//jumper 9 to 8

void setup() 
{
  pinMode(hallPin,INPUT);
  pinMode(pulsePin,OUTPUT);
  Timer1.initialize(125000);//pulse every 125 ms = 8Hz = 480rpm
  Timer1.pwm(pulsePin, 5);//short pulse 5/1024 x 125  approx 600 microseconds high
  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() 
{
  //delay(10);
  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;
  Serial.println(kount2rpm);
  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);
      }
  }
}

You are counting pulses for 1 second (1000ms) - at less than 400rpm that will be an integer of 7 or below, and vary by ±1.

When you convert this to r.p.m. (effectively multiplying by 60) it will vary considerably as you have found.

You are using code that was meant for reading up to 10,000rpm (according to the comment on the first line) to measure ˂400rpm.

If you want to measure the speed to the nearest rpm, then you need to sample for a whole minute (which may be impractical) or count many pulses per revolution.

cattledog:
I question your test method and signal quality more than the code.

Here’s some code which tests your code with a pulse generated with the TimerOne library.
TimerOne & TimerThree Arduino Libraries

It’s set up for 480 rpm with a pulse which is approximately 600 microseconds in width. The display results are pretty stable.

Do you have an oscilloscope to verify the quality of the pulse coming from your hall sensor?

Thanks cattledog, I appreciate the link, and though I’m still dealing with a sharp learning curve with programming, I’m trying my best to understand how to use the code.
I do have access to an oscilloscope, and will try to get some help confirming the signal is clean.

JohnLincoln:
If you want to measure the speed to the nearest rpm, then you need to sample for a whole minute (which may be impractical) or count many pulses per revolution.

I’ve tried increasing the pulses, up to 24 magnets, though I’m not sure how to adjust the code to correspond. I’ve tried dividing the 60,000 by 24 and replaced it with a value of 2500 in this line;

  int kount2rpm = int(60000./float(sampleTime))*kount;

But honestly I’m just taking a stab in the dark. Trying to do my research online before coming to ya guys, but I’m having troubles finding what I need