3144 Hall Switch Tachometer Code: Getting RPM in smaller intervals than 60?

For my Senior Physics project due June 7th, I am making a tachometer using Arduino, a 3144 Hall-effect switch, and a 16x02 i2C LCD.

I have the wiring as,

5v ---> VCC
GND ---> GND
D2 ---> Vout

a resister of 10K Ohms is placed on my breadboard between VCC and Vout.
and for the LCD

5v ---> VCC
GND ---> GND
SDA ---> A4
SCL ---> A5

Here is the code I am using

`/*
  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,8,9,10,11,12
*/

// include the library code:

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
// read the hall effect sensor on pin 2
const int hallPin=2;
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(0x27, 16, 2);
  // Print a message to the LCD.
  lcd.print("initializing...");
  delay(1000);
  lcd.clear();
}

void loop() 
{
  delay(1000);
  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 want to get my measurements accurately and in smaller intervals than 60. I am an Arduino noob.
Any help is appreciated

Thanks

EDIT: If anyone knows how to adjust the code to fix my issue, or knows how to change the code in order to get RPM by using the time interval instead of the conversion.

spinnerrpm_v2_2_ino.ino (1.6 KB)

File for the code

Edited your first post for you to include CODE TAGS ( </> )

1 Like

Assuming your code works, it's not really an Arduino question - just math. Try increasing your sample time.

Yes while I am aware of this, being so unfamilar makes looking at it quite daunting, so I seeked assistance from those who are familar. Thought I was supposed to be decreasing the sample time, I'll give this a try now.

changing my sample time only makes it more inaccurate. If I change sample time from 1000 to 2000, my RPM simply halves from 60 to 30. I need more frequent counts I think?

What is your hall sensor connected to? How many RPM are you expecting?

1 Like

It's connected to my Arduino, that's it. I'm only experimenting with a toy car wheel, only getting intervals of 60, should definitely be less than 60RPM.

At sixty RPM or less, with your sample time of one second, you're going to get at most one tick from the Hall. The only way to get better accuracy is to increase the time substantially.

What time must be increased substantially? The time it is measuring for or the sample time? I am not seeing how increasing the sample time does not just make it innacurate. If i double it, my RPM halves, like you said it's just math.

The problem is the sample time. You're spinning the wheel so slowly that the Hall provides one or no pulses in a second. It sounds like even two seconds isn't enough. Try ten.

Is there a better more accurate way to do get this measurement just by using the time interval between each hall effect sensor output? I am not seeing how adjusting sample time from 1 to 10 seconds does anything except turn any value I get for 60 into 6, and only update every 10 seconds.

Did you try ten?

You would get better accuracy using a second of sample time if you had higher revs. The minimum you'll see in most cars is 800, which would give you ~13 counts per second, giving you either 780rpm or 840 depending on how the pulses fell in the interval. So even there, you would likely want a slightly larger sample time to get closer to an accurate number. In your case, with so few pulses per second, the accuracy, as you observe, is catastrophically bad.

Try your alternative method of counting time between pulses (or several) and see if it helps. I doubt it'll make it worse at least.

I tried ten and like I said I got 6RPM instead of 60 and it updates once every 10 seconds. It's not different it's just on a smaller scale.

I have no idea how I would code this alternative method unfortunately as I have just started using Arduino.

I think there's a bug then - can you print out the value of kount?

I don't know how to print the value of kount or what "kount" really is even. Assuming its a term thats been defined to mean each time the magnet passes the sensor.

I am simply not getting what changing the sample value would even potentially fix-- but I am a complete and total noob.

But what seems apparent to me is that since the way the code determines my RPM is by using the 60000/sampletime, which was set at 1000. If i am to change the sample time (which I have) the only thing it truly seems to achieve is "scaling down" my values due to dividing 60000 by a larger number. Doesn’t changing the sampletime therfore "void" the accuracy of the RPM measurement? also, 10 seconds seems very long to wait between each time the code makes a calculation.

my intuitive idea was that decreasing the time between when these calculatiosn are made would result in RPM that can be shown more specifically and accurately than just 60, 120, 180, so on. Changing sampletime to 10000 only makes me get 6, 12, 18, etc and only at a rate of 10 seconds does it "refresh".

Part of the issue seems to be the use of DELAY.

Thinking that MILLIS may be a better alternative.
Using them you should be able to count more accurately without the blocking effect of the DELAY.

There are also lots of RPM counter sketches out there that may be of more use to you.

Or much much better record the elapsed times in microseconds between successive pulses, and calculate frequency from period.

Mark,

This is what I would love to have accomplished. How can I do this today? it’s due tomorrow.

Thank you

This is the code I have modified from diyhacking.com

It was working with serial print, but can't get it to show on LCD. It only prints RPM if it's greater than 20, and after 20 I presume it will only change if it gets to 40 or greater, and so on. This is another problem but less of a big deal than the LCD, which I am assuming is due to a noob error. Any pointers?

/*
Arduino Hall Effect Sensor Project
by Arvind Sanjeev
Please check out  http://diyhacking.com for the tutorial of this project.
DIY Hacking
*/
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <LCD.h>

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

 volatile byte half_revolutions;
 unsigned int rpm;
 unsigned long timeold;
 void setup()
 {
   Serial.begin(9600);
   lcd.begin(0x27, 16,2);
   attachInterrupt(0, magnet_detect, RISING);//Initialize the intterrupt pin (Arduino digital pin 2)
   half_revolutions = 0;
   rpm = 0;
   timeold = 0;
   lcd.print("setting up");
   delay(1000);
   lcd.clear();
 }
 void loop()//Measure RPM
 {
   if (half_revolutions >= 20) { 
     rpm = 30*1000/(millis() - timeold)*half_revolutions;
     timeold = millis();
     half_revolutions = 0;
     lcd.setCursor(15,1);
     lcd.print(rpm,DEC);
   }
 }
 void magnet_detect()//This function is called whenever a magnet/interrupt is detected by the arduino
 {
   half_revolutions++;
   lcd.setCursor(0,0);
   lcd.print("detect");
 }