Go Down

Topic: Revolutions Per Minute Calculating Error (Read 2 times) previous topic - next topic

Jun 19, 2012, 01:59 am Last Edit: Jun 19, 2012, 02:01 am by mykiscool Reason: 1
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.


Code: [Select]

//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);
}
Proud Member of the Nighthawk Robotics Club Team 569 B

marco_c

It is possible that you are counting multiple times each time the sensor is triggered, as the code will execute very fast.

You need to include some form of trigger detection. This is done by remembering if the digital input was low or high the last time you checked and only counting the low to high (or high to low) transitions.

A relatively straightforward way to do this is to use an interrupt that will be triggered on the transition only - there is lots of stuff on hte board on how this can be done.
Arduino libraries http://arduinocode.codeplex.com
Parola for Arduino http://parola.codeplex.com

Ok thanks that's a good idea. Ill look into it and get back to you if it doesn't work.
Proud Member of the Nighthawk Robotics Club Team 569 B

jraskell

Code: [Select]

//convert milliseconds to seconds
RPS=(float)timeSincePass*1000;


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

Then RPS is 1 / time for 1 revolution (I assume that's your timeSincePass value).

James C4S


Code: [Select]

//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.
Capacitor Expert By Day, Enginerd by night.  ||  Personal Blog: www.baldengineer.com  || Electronics Tutorials for Beginners:  www.addohms.com

marco_c

#5
Jun 19, 2012, 04:12 am Last Edit: Jun 19, 2012, 05:02 am by marco_c Reason: 1
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?
Arduino libraries http://arduinocode.codeplex.com
Parola for Arduino http://parola.codeplex.com

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?
Proud Member of the Nighthawk Robotics Club Team 569 B

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.
Proud Member of the Nighthawk Robotics Club Team 569 B

Nick Gammon

Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Here's my updated code to include a debounce:

Code: [Select]


//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;
}
Proud Member of the Nighthawk Robotics Club Team 569 B

jraskell


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.
Proud Member of the Nighthawk Robotics Club Team 569 B

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.

Code: [Select]


//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;
}
Proud Member of the Nighthawk Robotics Club Team 569 B

GoForSmoke


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.
Nick Gammon on multitasking Arduinos:
1) http://gammon.com.au/blink
2) http://gammon.com.au/serial
3) http://gammon.com.au/interrupts

jraskell

Quote
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.

Go Up