Revolutions Per Minute Calculating Error

jraskell:

//convert milliseconds to seconds

RPS=(float)timeSincePass*1000;



You convert milliseconds to seconds by dividing by 1000, not multiplying by 1000.

It is also a good practice to make your constants match the kind of math you are doing. "1000" is an integer constant. "1000.0" is a float constant.

I also don't see any of the logic that I would expect in this code.

There are 2 ways to do this:

  1. Count how long it takes for one revolution (sec/rev) and then take the reciprocal to get rev/sec. You will probably count ms so a conversion will be required.
  2. Count the number of 'clicks' in a second (or partial second) and then use the count directly.

What was your intention here?

My intention was to measure the speed of a bike wheel as it turns in revolutions per second or minute and then calculate that to the actual speed using the wheel size. Also I tried using a debounce to make sure I wasn't getting the magnetic reed giving me 2 readings and I still have the same thing not the right reading. Any suggestions?

I sort of made it into a reciprocal when I divided 1000 by the milliseconds which would be the same as dividing 1 by the seconds. Ex: 5 seconds = 5/1 1 divided by 5 = 1/5 opposite of 5/1. I think the math is right as far as i can tell. Constructive criticism would be appreciated to help me find the right solution.

Show us your new code.

Here's my updated code to include a debounce:

//these are just some references for it to work properly
#include <StopWatch.h>
#include <LiquidCrystal.h>
//these are the microcontroller ports that an lcd screen is connected to, a status led, and the switch.
LiquidCrystal lcd(12, 11, 7, 6, 5, 4);
#define LED 13 //pin for the LED indicator (green)
#define Reed 3 //pin for the switch

//create a timer
StopWatch MySW;
//state of sensor 1 or 0
byte reedState=0;
//time between pass of magnet
float timeSincePass=0;
//revolutions per second
float RPS=0;
//revolutions per minute
float RPM=0;
//last state of reed switch on or off
byte lastReedState=LOW;
long lastDebounceTime = 0;
long debounceDelay = 50;

//just some setup stuff
void setup()
{
  pinMode(LED,OUTPUT);
  pinMode(Reed,INPUT);
  Serial.begin(9600);
  lcd.begin(16, 4);
  lcd.print("Your Revs/Sec");
}


void loop()
{
  
  byte reading = digitalRead(Reed);  
  //get the state of the sensor;
  //time since pass of magnet
  timeSincePass= MySW.elapsed();
  //set the lcd cursor to write on the second line
  lcd.setCursor(0, 1); 

  if (reading != lastReedState) 
  {
    // reset the debouncing timer
    lastDebounceTime = millis();
  } 
  
    if ((millis() - lastDebounceTime) > debounceDelay) 
  {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:
    reedState = reading;
  }

//if the magnet engages the switch
  if (reedState==HIGH)
  {
    //turn on status led for visual purposes
    digitalWrite(LED, HIGH);
    //convert milliseconds to seconds
    RPS=(float)timeSincePass*1000;
    //reset the timer storage int    
    timeSincePass=0;
    //reset the timer and start it again
    MySW.reset();
    MySW.start();
  }

  else
  {
    //turn off led otherwise
    digitalWrite(LED, LOW); 
  }
  //convert seconds to minutes
  RPM=(float)RPS*60; 
  
  //print the results to a screen 
  lcd.print(RPS);
  lcd.print("  ");
  lcd.print(timeSincePass);
  lastReedState = reading;
}

mykiscool:
I sort of made it into a reciprocal when I divided 1000 by the milliseconds which would be the same as dividing 1 by the seconds. Ex: 5 seconds = 5/1 1 divided by 5 = 1/5 opposite of 5/1. I think the math is right as far as i can tell. Constructive criticism would be appreciated to help me find the right solution.

I don't see any divisions anywhere.

Ooops sorry I thought I made that division change that he suggested Ill change it and get back to you.

Ok now I'm getting somewhat more realistic numbers like 200 and 250 revs per second, but still those are the only 2 readings I'm getting.

//these are just some references for it to work properly
#include <StopWatch.h>
#include <LiquidCrystal.h>
//these are the microcontroller ports that an lcd screen is connected to, a status led, and the switch.
LiquidCrystal lcd(12, 11, 7, 6, 5, 4);
#define LED 13 //pin for the LED indicator (green)
#define Reed 3 //pin for the switch

//create a timer
StopWatch MySW;
//state of sensor 1 or 0
byte reedState=0;
//time between pass of magnet
float timeSincePass=0;
//revolutions per second
float RPS=0;
//revolutions per minute
float RPM=0;
//last state of reed switch on or off
byte lastReedState=LOW;
long lastDebounceTime = 0;
long debounceDelay = 50;

//just some setup stuff
void setup()
{
  pinMode(LED,OUTPUT);
  pinMode(Reed,INPUT);
  Serial.begin(9600);
  lcd.begin(16, 4);
  lcd.print("Your Revs/Sec");
}


void loop()
{
  
  byte reading = digitalRead(Reed);  
  //get the state of the sensor;
  //time since pass of magnet
  timeSincePass= MySW.elapsed();
  //set the lcd cursor to write on the second line
  lcd.setCursor(0, 1); 

  if (reading != lastReedState) 
  {
    // reset the debouncing timer
    lastDebounceTime = millis();
  } 
  
    if ((millis() - lastDebounceTime) > debounceDelay) 
  {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:
    reedState = reading;
  }

//if the magnet engages the switch
  if (reedState==HIGH)
  {
    //turn on status led for visual purposes
    digitalWrite(LED, HIGH);
    //convert milliseconds to seconds
    RPS=(float)1000/timeSincePass;
    //reset the timer storage int    
    timeSincePass=0;
    //reset the timer and start it again
    MySW.reset();
    MySW.start();
  }

  else
  {
    //turn off led otherwise
    digitalWrite(LED, LOW); 
  }
  //convert seconds to minutes
  RPM=(float)RPS*60; 
  
  //print the results to a screen 
  lcd.print(RPS);
  lcd.print("  ");
  lcd.print(timeSincePass);
  lastReedState = reading;
}

mykiscool:
My intention was to measure the speed of a bike wheel as it turns in revolutions per second or minute and then calculate that to the actual speed using the wheel size. Also I tried using a debounce to make sure I wasn't getting the magnetic reed giving me 2 readings and I still have the same thing not the right reading. Any suggestions?

Just be aware that "wheel size" depends on tire pressure, tire width and load on the wheel as well as the nominal diameter. At 75 psi it will be less than at 90 or 110.

IMO you should make a small solid wheel that the bicycle wheel turns and find the rpms of that.

Ok now I'm getting somewhat more realistic numbers like 200 and 250 revs per second, but still those are the only 2 readings I'm getting.

It's a resolution issue, in this case the resolution of your timer.

milliseconds returns whole millisecond results, so:
1000/4 = 250.
1000/5 = 200.

You'll never get any values between 4 and 5, so you'll never get any results between 250 and 200.

One easy option here is to switch to a higher resolution timer, ie use micros() instead of millis. You'll need to adjust your calculations appropriately.

You got a bicycle wheel that will hold together at 5 ms per rev? Reality check time!

27" rim with 1"-1.25" tire, that's about 7' per rev x 200 revs/sec (@ 5ms) = 1400 ft/sec where 60 mph is 88'/sec.

Maybe change the reed switch for a Hall Effect sensor?

Oh ok thanks I'll try that.

I wasn't using my bike right away for the test I was just testing the concept by doing a simulation of moving the magnet in front with my hand. As for the hall effect, I'll look into that. My reed switch does however seem to be working because the light goes on when I move it in front of it. Also here's my new code, strangely it keeps getting 257 then a decimal like 257.45 and 257.56 rps even when I wait a long time.

//these are just some references for it to work properly
#include <StopWatch.h>
#include <LiquidCrystal.h>
//these are the microcontroller ports that an lcd screen is connected to, a status led, and the switch.
LiquidCrystal lcd(12, 11, 7, 6, 5, 4);
#define LED 13 //pin for the LED indicator (green)
#define Reed 3 //pin for the switch

//create a timer
StopWatch MySW (StopWatch::MICROS);
//state of sensor 1 or 0
byte reedState=0;
//time between pass of magnet
float timeSincePass=0;
//revolutions per second
float RPS=0;
//revolutions per minute
float RPM=0;
//last state of reed switch on or off
byte lastReedState=LOW;
long lastDebounceTime = 0;
long debounceDelay = 50;

//just some setup stuff
void setup()
{
  pinMode(LED,OUTPUT);
  pinMode(Reed,INPUT);
  Serial.begin(9600);
  lcd.begin(16, 4);
  lcd.print("Your Revs/Sec");
}


void loop()
{
  
  byte reading = digitalRead(Reed);  
  //get the state of the sensor;
  //time since pass of magnet
  timeSincePass= MySW.elapsed();
  //set the lcd cursor to write on the second line
  lcd.setCursor(0, 1); 

  if (reading != lastReedState) 
  {
    // reset the debouncing timer
    lastDebounceTime = millis();
  } 
  
    if ((millis() - lastDebounceTime) > debounceDelay) 
  {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:
    reedState = reading;
  }

//if the magnet engages the switch
  if (reedState==HIGH)
  {
    //turn on status led for visual purposes
    digitalWrite(LED, HIGH);
    //convert milliseconds to seconds
    RPS=(float)1000000.0/timeSincePass;
    //reset the timer storage int    
    timeSincePass=0;
    //reset the timer and start it again
    MySW.reset();
    MySW.start();
  }

  else
  {
    //turn off led otherwise
    digitalWrite(LED, LOW); 
  }
  //convert seconds to minutes
  RPM=(float)RPS*60; 
  
  //print the results to a screen 
  lcd.print(RPS);
  lcd.print("  ");
  lcd.print(timeSincePass);
  lastReedState = reading;
}

I see you have read the sensor (reed switch) then if it changes from last read to start the debounce delay and if the delay is over to actually set the switch state to what is read which is not bad though 50 ms may be a bit long if you race down long hills.

But then if the state is HIGH you compute and show data... and the state may stay HIGH many times through loop() before debouncing back to low, at least 50 ms worth. You only want it to detect and calculate once per pass, otherwise your timing and resultant calculations will be wrong.

I used to have a generator light. The brightness varied with my speed until the bulb burned out, usually on the way downhill. It also dragged, that was when bike lights were incandescents. But a lightweight DC motor of the right type should generate low voltage (depending on how big a wheel is on the shaft) when driven and might give an analog reading of speed though probably not linear analog (twice the volts not being twice the speed kind of thing, probably response being a curve). However you'd have an idea of speed well before the bicycle wheel turned once.
Because face it, if the wheel has to turn 1 or 1/2 revolution to generate a reading then you will have noticeable lag in calculation and display until you really get moving. 1 rev/sec is about 5 mph with a 27" rim wheel.

mykiscool:
Can someone please help me with this code. It is supposed to detect the revolutions per second on a bicycle wheel and then convert it to revolutions per minute. The magnetic sensor code works fine, the timer resets itself when it passes by, but no matter what speed or increment of time i wait to pass the magnet by again, it only comes up with 2 readings 5,000 and 6,000, which are not goods revolution per second readings. I expect to get some number like 230.14 or something random like that. My code is below. Any help is appreciated.

//these are just some references for it to work properly

#include <StopWatch.h>
#include <LiquidCrystal.h>
//these are the microcontroller ports that an lcd screen is connected to, a status led, and the switch.
LiquidCrystal lcd(12, 11, 7, 6, 5, 4);
#define LED 13 //pin for the LED indicator (green)
#define Reed 3 //pin for the switch

//create a timer
StopWatch MySW;
//state of sensor 1 or 0
byte reedState=0;
//time between pass of magnet
float timeSincePass=0;
//revolutions per second
float RPS=0;
//revolutions per minute
float RPM=0;

//just some setup stuff
void setup()
{
 pinMode(LED,OUTPUT);
 pinMode(Reed,INPUT);
 Serial.begin(9600);
 lcd.begin(16, 4);
 lcd.print("Your Revs/Sec");
}

void loop()
{
 //get the state of the sensor
 reedState=digitalRead(Reed);
 //time since pass of magnet
 timeSincePass= MySW.elapsed();
 //set the lcd cursor to write on the second line
 lcd.setCursor(0, 1);

//if the magnet engages the switch
 if (reedState==HIGH)
 {
   //turn on status led for visual purposes
   digitalWrite(LED, HIGH);
   //convert milliseconds to seconds
   RPS=(float)timeSincePass*1000;
   //reset the timer storage int    
   timeSincePass=0;
   //reset the timer and start it again
   MySW.reset();
   MySW.start();
 }

else
 {
   //turn off led otherwise
   digitalWrite(LED, LOW);
 }
 //convert seconds to minutes
 RPM=(float)RPS*60;
 
 //print the results to a screen
 lcd.print(RPS);
 lcd.print("  ");
 lcd.print(timeSincePass);
}

There was a lively conversation about this on another thread recently. If you really need error correction, you might want to look into rotary encoding and Gray codes.

Thanks for all your help, I fixed it.

 //these are just some references for it to work properly
#include <StopWatch.h>
#include <LiquidCrystal.h>
//these are the microcontroller ports that an lcd screen is connected to, a status led, and the switch.
LiquidCrystal lcd(12, 11, 7, 6, 5, 4);
#define LED 13
#define Reed 3 //pin for the switch

//create a timer
StopWatch MySW (StopWatch::MICROS);
//state of sensor 1 or 0
byte reedState=0;
//time between pass of magnet
unsigned long timeSincePass=0.0;
//revolutions per second
float RPS=0.0;
//revolutions per minute
float RPM=0.0;
//last state
byte lastreedState=0;

//just some setup stuff
void setup()
{
  pinMode(LED,OUTPUT);
  pinMode(Reed,INPUT);
  Serial.begin(9600);
  lcd.begin(16, 4);
  lcd.print("RPM");
}


void loop()
{  
  reedState=digitalRead(3);
  //get the state of the sensor;
  //time since pass of magnet
  timeSincePass= MySW.elapsed();
  //set the lcd cursor to write on the second line
  

  //if the magnet engages the switch
  if (reedState != lastreedState && reedState==HIGH)
  {
    //turn on status led for visual purposes
    //convert milliseconds to seconds
    RPS=(float)1000000.0/timeSincePass;
    //reset the timer storage int    
    timeSincePass=0;
    //reset the timer and start it again
    MySW.reset();
    MySW.start();

  }

  if (reedState==HIGH)
  {
    digitalWrite(LED, HIGH); // gives a visual aid to tell when it is being magnetized
  }

  else
  {
    digitalWrite(LED, LOW); //Turns off when no magnet is detected 
  }

  //convert seconds to minutes
  RPM=(float)RPS*60.0; 
  

  //print the results to a screen 
  lcd.print(RPM);
  lcd.setCursor(0, 1); 
  lcd.print("Time  ");
  lcd.print(timeSincePass);  
  lcd.print(" MPH ");
  lcd.print();
  lastreedState = reedState;

  Serial.begin(9600);
  Serial.println(RPM);
}

Moderator edit: [code] ... [/code] tags added. (Nick Gammon)

Counting the objects that pass with IR sensor when reach the expected quantity will generate a alarm signal.

Have any sample code?

I am curious about the magnet you are using with the reed switch. If it is a bar magnet, is it oriented so only one pole passes the reed switch, or do both poles pass the reed switch when the wheel rotates? If the latter is the case, that may be the source if what you think is "bounce".

Paul