Integer values going to max on RPM sensing circuit

Hi all,
I am just getting back into Arduino and coding after a few years away and have hit a roadblock. I am working on a small part of a larger equipment management project so I am only eating the elephant one bite at a time so to speak.

Anyways this part is for code for an rpm counter eventually using a hall effect sensor but I am using a button for more simplicity of testing on my desk. The error that I am running into some weird values with some of the integers and was hoping that I could get some help with figuring them out. Also as a side note I will eventually have 4 hall effect sensors and 2 temp sensors as part of this project so this is why I am trying to calculate RPM based off of timing between sensor activations as opposed to counting rotations in 1 second and multiplying by 60.

So when I try to print the interval and RPM the numbers being printed are near max value for an unsigned long integer but when I change the integers being printed out to startTime and endTime and hit the button then the numbers update like they should when the button is pressed.

I hope this and all my code comments make sense as I can't figure out why the integers are doing this.

Thanks,
Gearhulk34

#include "LiquidCrystal.h"
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

const int buttonPin = 8;     
unsigned long startTime;
unsigned long endTime;
unsigned long interval;
unsigned long seconds;
unsigned long RPM;
int buttonState = 0; 
int x = 0;     
   
void setup() {
  pinMode(buttonPin, INPUT);
  lcd.begin(16,2);
  Serial.begin(9600);
}

void loop() {
  
 if (buttonState == 0 && digitalRead(buttonPin)==HIGH) {  
     startTime = millis();//if button IS pushed start time
     x  = 1; //used to change button state only after button is released 
  } 
  
 if ( x == 1 && digitalRead(buttonPin) == LOW) { 
    buttonState = 1; // change button state so that it will register 2nd press after button is released 
  }
  
  if (buttonState == 1 && digitalRead(buttonPin)==HIGH){   
   endTime = millis(); //if button IS pushed stop time 
   x = 0; //used to change button state only after button is released 
  }
  
  if (x == 0 && digitalRead(buttonPin) == LOW) { 
    buttonState = 0; //to change state only after button is released 
  }
  
  interval = endTime - startTime; // determine value for interval from first press to second press *** this is where issues start giving numbers near max value for the interger 
  seconds = interval / 1000; 
  RPM = 60 / seconds;

  // all this below is for printing outputs to the 16x2 LCD screen I'm using
  lcd.setCursor(0, 0);
  lcd.print("interval"); //what value is being printed on 1/2 of screen
  lcd.setCursor(9,0);
  lcd.print("RPM"); //what value is being printed in 2/2 of screen
  lcd.setCursor(0,1);
  lcd.print(interval); //print value for start time or interval
  lcd.setCursor(9,1); //move cursor 1/2 way across screen
  lcd.print(RPM); // print value for end time or RPM
  }

At what RPM to the results go bad? IS that consistent? Are you de-bouncing the push button?

why do you calculate that for every loop ?

it only makes sense when you have secured the start time and associated end time.

Some buttons bounce many times in a fraction of a millisecond. There are button bounce libraries or code to help with that.

Post a diagram showing how the button is wired. You may in addition have a floating input.

Yes it is consistent and I do have a 10k ohm resistor on the button to help with bounce. As part of the debugging steps I went through I changed variables being printed to the LCD to the startTime and endTime and those values are as to be expected in the low thousands just after turning on the power. Also I have the button on a separate breadboard as the LCD and because I was having some signal issues in the wires that were giving phantom activations.

That doesn't help with bounce. How is that resistor wired?

The simplest approach to prevent floating inputs is to call pinMode(pin, INPUT_PULLUP) and wire the button from the input to GND.

1 Like

How does that help? The button still bounces the same number of times.

The truth is you are making a very complex solution to a very simple problem.

  1. create a volitile, global variable for an integer counter.
  2. use a pin that provides an interrupt for each rising or each descending voltage. Your choice.
  3. Set that pin to input pullup and attach your Hall sensor
  4. In the interrupt code for the pin, add 1 to the volatile counter. That is all that needs to be done.
  5. In setup(), attach the interrupt to your interrupt code.
  6. In the loop() code, set up a timer for 1 second. If less than 1 second has passed, do nothing.
  7. If 1 second has passed, turn off the interrupts, move the value in the interrupt counter to a local integer and zero to interrupt counter and enable interrupts.
  8. using the value in the working int which is the interrupt count or the revolution count, compute the RPM value which will be 60 times the one second value.
  9. display the RPM value and you are all done.

Use a hand held magnet to simulate the rotation of your shaft.

1 Like

not sure how this is rpm. rpm is some # of events / time.

The large values is because interval / 1000 will be zero if interval < 1000 because this is integer division. This can be avoided by doing

rpm = 60 * 1000 / interval;

look over the following where i corrected the math and added cnt to represent the # of events.

It also configured the button pin as INPUT_PULLUP to use the internal resistor with the button wired between the pin and ground and is adds a delay to debounce the switch.

ready
     msec      RPM
      599     1001
     msec      RPM
      710      845
#include "LiquidCrystal.h"
LiquidCrystal lcd (12, 11, 5, 4, 3, 2);

const int buttonPin = 8;
      int buttonState;

unsigned long startTime;
unsigned long cnts = 10;            // where does this come from

char s [90];

// -----------------------------------------------------------------------------
void loop ()
{
    unsigned long msec = millis ();
    
    byte but =  digitalRead (buttonPin);
    if (buttonState != but)  {
        buttonState  = but;
        if (LOW == but)  {          // pressed
            startTime = msec;
        }
        else  {                     // released
            unsigned long interval = msec - startTime;
            unsigned long rpm      = 60 * cnts * 1000 / interval;

            sprintf (s, " %8s %8s",  "msec", "RPM");
            Serial.println (s);
            lcd.setCursor (0, 0);
            lcd.print (s);

            sprintf (s, " %8lu %8lu", interval, rpm);
            Serial.println (s);
            lcd.setCursor (0, 1);
            lcd.print (s);
        }

        delay (30);                 // debounce
    }
}


// -----------------------------------------------------------------------------
void setup ()
{
    Serial.begin   (9600);
    Serial.println ("ready");

    lcd.begin    (16,2);
    pinMode      (buttonPin, INPUT_PULLUP);
    buttonState = digitalRead (buttonPin);
}

on a small AVR 60 x 1000 overflows. You might want to force unsigned long maths with at least one ul suffix in the first operation

rpm = 60ul * 1000 / interval;

if the end project is using hall effect sensors with no bounce why test the code using switch which does bounce - test with a hall effect switch

for timing multiple signals I would tend to use a 32bit microcontroller such as the ESP32, STM32, RP2040, etc, e.g. measure the phase difference between two pulses

in addition the ESP32 has a Pulse Counter (PCNT) module which may be useful

(post deleted by author)

I don't think its a bounce issue because when I was debugging I changed the printout to the startTime and endTime integers and those appear to be displaying correctly. Below is a sample of how I have the button wired. This is just an image I found on the web because I don't know how to draw the circuit properly. The resistor I am using is a 10K ohm and the button input in mine is pin 8.

I guess that's just where I put it for the testing. Now that you mention it I probably should have it in the 3rd if statement where I get the endTime variable set as that will make it easier to track when I start to add more sensors into the project.

It is on the ground side as a pullup/pull down resistor. For some reason I was having a blonde moment and thinking that it was for helping drown out the bounce. After sleeping on it and reading the comments I realize that was stupid of me. However I don't see how the bounce is the issue because if I was experiencing switch bounce that would be changing the values of the startTime and endTime integers but when I output those values to the LCD they seem to be as expected.

Having multiple sensors for RPM does not mean you can't count rotations in 1 second.

You can use either method with any (reasonably small) number of sensors.

It's not the preferred way. You don't need to use any resistor. Instead you can use an internal pull-up resistor (or maybe pull-down, depending on the model of Arduino).

Wiring 5V to the button is unnecessary, and can lead to dangerous short-circuits, if that wire comes loose or gets damaged.

Better to wire the button between the Arduino pin and ground, as previously suggested.

I was mistaken that the resistor was supposed to help with the noise... I was having a blonde moment lol. I pulled these 2 photos to show what is going on in the first I am printing the start and end time integers and in the second the only thing that I change is printing the interval integer instead of the end time integer. If I was expecting to be seeing a bounce in the switch then wouldn't the start and end times only be a few milliseconds apart?


I was wanting to calculate based off the time between sensor activations because it will give a more accurate RPM measurement as it is constantly monitoring each sensor as opposed to once 8-10 seconds when I have the system fully built. I probably should of mentioned this earlier but I am making a system to monitor the grain dryer on our farm and hope to have the Arduino connected to a raspberry pi so that we can monitor it remotely as well.

how are you planning to connect the Arduino to the RPi?
how much data how often?
what RPM values are expecting?
what will the RPi do apart from receiving data?
how will you monitor the RPi?