This code works well up to around 16000 RPM and then it breaks . After a bit of playing around I found that the elapsed variable (time between two consecutive detection signals from the optical sensor) isn't able to go below 3500-3600 microseconds. This roughly translates to 16,500-17,000 RPM. Here's the code:
/* This program calculates RPM by taking signal inputs from an
optical IR sensor module. The signal OUT pin from the IR sensor
module is connected to DigitalPin 2 of Uno/Nano. Whenever a signal
is detected HIGH, the intererupt routine is executed where the elapsed
time between the current and last detection is calculated. This data is
then convereted to RPM in setup().
*/
unsigned long printInterval = 100; // time interval to serial.Print (in milliseconds)
//set minimum speed as 1 RPS, speed < 1 RPS is counted as zero
unsigned long senseThreshold = 1000000; // in microseconds
unsigned long lastSense = 0; //
float RPM = 0.0;
unsigned long printTimer_start = 0;
unsigned long timerStart = 0;
unsigned long timerStop = 0;
unsigned long elapsed = 0;
void update_timer()
{
timerStop = micros();
elapsed = timerStop - timerStart; //calcultate time between two consecutive pulses
}
void setup()
{
Serial.begin(9600);
}
void loop()
{
if (elapsed != 0) {
RPM = 60000000.0 / elapsed;
}
if (elapsed == 0 || lastSense > senseThreshold ) {
RPM = 0;
}
// Printing RPM to serial port at the specified rate (in printInterval)
unsigned long printTimer_stop = millis();
if (printTimer_stop - printTimer_start >= printInterval) {
Serial.println(RPM);
printTimer_start = printTimer_stop;
}
lastSense = micros() - timerStart;
timerStart = timerStop;
attachInterrupt(0, update_timer, RISING); // interrupt when a signal is detected
}
/* This program calculates RPM by taking signal inputs from an
optical IR sensor module. The signal OUT pin from the IR sensor
module is connected to DigitalPin 2 of Uno/Nano. Whenever a signal
is detected HIGH, the intererupt routine is executed where the Count
time between the current and last detection is calculated. This data is
then convereted to RPM in setup().
*/
float RPM = 0.0;
unsigned long Period = 100000;
unsigned long timerStart = 0;
volatile unsigned long Count = 0;
void setup() {
Serial.begin(9600);
attachInterrupt(digitalPinToInterrupt(2), update_timer, RISING); // interrupt when a signal is detected
timerStart = micros();
}
void update_timer() {
Count++; //calcultate time between two consecutive pulses
}
void loop() {
if (micros() - timerStart >= Period ) {
noInterrupts();
RPM = 60000000.0 * (float)Count / (float)timerStart;
Count = 0;
Serial.println(RPM, 1);
timerStart += Period;
interrupts();
}
}
Perhaps you are overrunning the frequency response of your optical sensor module. Have you looked at it on your scope to ensure you're getting a clean signal? The other thing that makes me go "Hmmm" is the baud rate you are using. Perhaps 9600 baud is not getting the data printed fast enough to catch the next pulse correctly.
I would also significantly minimize the print requests and definitely not use 9600 bauds which might let the data accumulate in the outgoing buffer and transform print in a blocking call
Hi kolaha,
I did run your version today but I'm afraid it did not solve the problem. I was getting RPMs as single digit numbers even at expected RPM > 10000. I couldn't go through your code and see where the problem exactly was. Will give it a try again tomorrow. Thanks alot btw.
Hi,
Can you post a circuit diagram of your project?
A hand drawn image will be fine, please include ALL power supplies, component names and pin labels.
A couple of images of your project would also help.
Perhaps you could use Timer1 as a counter. You could set an appropriate compare value that will allow you to count revolutions. Once the compare value is reached (or you could use the overflow) you read the elapsed time between enabling the counter and rollover.
This would not only get you to 50k (I think, I've not looked at the numbers) but it will also average your pulses.
Perhaps it would speed up thing if you wrote your own for(; inside the loop, not depending on whatever overhead is present outside your code.
Second, instead of relying on the resolution of micros() for what seems from your code to address time between individual sensor ticks, to run a tight loop, counting on and off periods, assuming your sensor doesn't give off noise.
Something like
#define SAMPLES 100
int count=SAMPLES;
unsigned long start=micros();
while(count>0) {
while (sensor==on) ;
while (sensor==off) ;
count--;
}
unsigned long end=micros();
// calculate RPM across number of samples
Of course you'd like to extend the code with some failsafe against hanging forever. This is just an example
Everyone, I was finally able to read 20k RPM. The changes are:
Baud rate set to 57,600 (earlier 9600)
detachInterrupt() invoked while transmitting RPM value through serial
@J-M-L@ggutshal@PaulRB your replies have been of great help. Improvements are definitely required in terms of smoothing the output, and I think that's where the suggestions from @JohnRob@Rupert909 should work.
Here's the working version of the code:
/* This program calculates RPM by taking signal inputs from an
optical IR sensor module. The signal OUT pin from the IR sensor
module is connected to DigitalPin 2 of Uno/Nano. Whenever a signal
is detected HIGH, the intererupt routine is executed where the elapsed
time between the current and last detection is calculated. This data is
then convereted to RPM in setup().
*/
unsigned long printInterval = 100; // time interval to serial.Print (in milliseconds)
//set minimum speed as 1 RPS, speed < 1 RPS is counted as zero
unsigned long senseThreshold = 1000000; // in microseconds
unsigned long lastSense = 0; //
float RPM = 0.0;
unsigned long printTimer_start = 0;
unsigned long timerStart = 0;
unsigned long timerStop = 0;
unsigned long elapsed = 0;
void update_timer()
{
timerStop = micros();
elapsed = timerStop - timerStart; //calcultate time between two consecutive pulses
}
void setup()
{
Serial.begin(57600);
attachInterrupt(0, update_timer, RISING);
}
void loop()
{
if (elapsed != 0) {
RPM = 60000000.0 / elapsed;
}
if (elapsed == 0 || lastSense > senseThreshold ) {
RPM = 0;
}
// Printing RPM to serial port at the specified rate (in printInterval)
unsigned long printTimer_stop = millis();
if (printTimer_stop - printTimer_start >= printInterval) {
detachInterrupt(0);
Serial.println(RPM);
printTimer_start = printTimer_stop;
}
lastSense = micros() - timerStart;
timerStart = timerStop;
attachInterrupt(0, update_timer, RISING); // interrupt when a signal is detected
}