Read RPM Very Fast on Arduino UNO

Hi,

I've got a motor with an encoder that outputs 16 pulses per revolution.

The maximum speed of the motor is 4,300 rpm.

I tried coding with interrupts but the sample rate is much too slow as I need to read the speed as quickly as possible on the UNO.

I found an approach that seems to be what I need but it was coded for an "IR to sensor" application.

Below is the current code I use.

Code: Select all | TOGGLE FULL SIZE
`// read RPM#include <Wire.h>volatile int rpmcount = 0;//see http://arduino.cc/en/Reference/Volatilefloat rpm = 0;unsigned long lastmillis = 0;void setup(){ Serial.begin(9600);  attachInterrupt(0, rpm_fan, FALLING);//interrupt cero (0) is on pin two(2). dac.begin(0x60);}void loop(){  if (millis() - lastmillis == 1000){  /*Uptade every one second, this will be equal to reading frecuency (Hz).*/  detachInterrupt(0);    //Disable interrupt when calculating   rpm = (rpmcount/12.0) * 1.875;  /* Convert frecuency to RPM, note: this works for one interruption per full rotation. For two interrups per full rotation use rpmcount * 30.*/   Serial.print("RPM =\t"); //print the word "RPM" and tab. Serial.print(rpm); // print the rpm value. Serial.print("\t Hz=\t"); //print the word "Hz". Serial.println(rpmcount); /*print revolutions per second or Hz. And print new line or enter.*/ rpmcount = 0; // Restart the RPM counter lastmillis = millis(); // Uptade lasmillis attachInterrupt(0, rpm_fan, FALLING); //enable interrupt  }}void rpm_fan(){ /* this code will be executed every time the interrupt 0 (pin2) gets low.*/  rpmcount++;}`

Here is the One I need Converted;

Code: Select all | TOGGLE FULL SIZE
`/*Pulse Tach by R. DrechslerThe setup...I'm using Arduino Ethernet or Uno but should work with any.I have a 1.875"dia plexiglass wheel, painted flat black, attached directly to a small motor shaft.The wheel has a single .116" wide notch cut into, about a .250" deep. The motor is powered by a 0-3A, 0-12V power supply.I'm using a sparkfun photo interrupter (http://www.sparkfun.com/products/9299) sensing the notch. The signalgoes low when the the photo interrupter is blocked. The signal wire is to Pin 2.The numbers...Math to define some "magic" numbers... adjust as needed... timerrevCalc = duration * 50.7801;   RPM = 60000000/timerrevCalc; //See aboveWhere:duration = amount of time notch exposes IR to sensor in uS.50.7801 = the notch is 50.7801 times smaller than the remainder of the wheel (so the circumference of my 1.875"dia wheel = 5.8905, 5.8905" / .116" = 50.7801^^^^^^^ this number needs to be calculated and adjusted per application.60000000 is a convertion from uS to just minutes.The good...This appears to be VERY fast at reading RPM. The RPM is calculated with EACH pulse. So instead of using a interrupt and have to waitthe interrupt interval to refresh the data, the data comes in after each pulse is timed.Also, when using interrupts, you have limited resolution. Example, 10 pulses, counted over 250ms = 40 pulses a second. 40 pulses a second x 60 seconds = 2400 RPM, right?So 1 pulse per 250ms = 4 pulses per second or 240 RPM and 2 pulse per 250ms = 8 pulses per second or 480 RPM!!! 1 pulse more = 240 RPM?!?!To get better resolution, you need a longer interrupt interval. A longer interrupt interval = slower repsonse.*/const int Pin = 2; // Photo Interrupter. http://www.sparkfun.com/products/9299const int ledPin =  9; //LED pin on the arduino ethernet, to help visualize the notch passing byint photoState = 0;long RPM = 0;long timerrevCalc = 0;unsigned long duration;void setup() { Serial.begin(38400); // had problems with 9600... pinMode(ledPin, OUTPUT);       pinMode(Pin, INPUT);    }void loop(){ duration = pulseIn(Pin, HIGH, 100000); //times the amount of microseconds the notch is exposing the IR, Times out after 100000 uS. Raise the timeout for slower RPM readings. timerrevCalc = duration * 50.7801; //See above RPM = 60000000/timerrevCalc; //See above Serial.print(duration); //print out results. Serial.print("  "); Serial.println(RPM); }`

Thanks.

greenbarron64

Posts: 15
Joined: Wed Jun 15, 2016 11:57 am

Re: Read RPM Very Fast on Arduino UNO

Interrupts is the way to go. For faster response, wait until you have 16 pulses, then do the timing math. You can use the micros() function to find the duration in microseconds:
https://www.arduino.cc/reference/en/lan ... me/micros/

Posts: 71779
Joined: Sat Feb 07, 2009 10:11 am

Re: Read RPM Very Fast on Arduino UNO

Thanks...

I tried altering the millis, I get a faster rate but my RPM calculation breaks.

What section of the first code should I modify?

greenbarron64

Posts: 15
Joined: Wed Jun 15, 2016 11:57 am

Re: Read RPM Very Fast on Arduino UNO

You need to use micros() not millis(). That gives you 1,000 times more resolution.

Posts: 71779
Joined: Sat Feb 07, 2009 10:11 am

Re: Read RPM Very Fast on Arduino UNO

Thanks,

Is there something wrong with my implementation. The code compiled but isn't working anymore.

Code: Select all | TOGGLE FULL SIZE
`// read RPM#include <Wire.h>#include <Adafruit_MCP4725.h>volatile int rpmcount = 0;//see http://arduino.cc/en/Reference/Volatilefloat rpm = 0;unsigned long lastmicros = 0;Adafruit_MCP4725 dac;void setup(){ Serial.begin(9600);  attachInterrupt(0, rpm_fan, FALLING);//interrupt cero (0) is on pin two(2). dac.begin(0x60);}void loop(){  if (micros() - lastmicros == 1000){  /*Uptade every one second, this will be equal to reading frecuency (Hz).*/  detachInterrupt(0);    //Disable interrupt when calculating   rpm = (rpmcount/12.0) * 1.875;  /* Convert frecuency to RPM, note: this works for one interruption per full rotation. For two interrups per full rotation use rpmcount * 30.*/   Serial.print("RPM =\t"); //print the word "RPM" and tab. Serial.print(rpm); // print the rpm value. Serial.print("\t Hz=\t"); //print the word "Hz". Serial.println(rpmcount); /*print revolutions per second or Hz. And print new line or enter.*/ uint32_t val; // get your RPM value here val=map(rpm,0,144.22,0,4096);   //0 - 200 rpm range calculated RPM / 0 to 4096 MCP4725 voltage output val = constrain(val, 0, 4096); dac.setVoltage(val, false);  rpmcount = 0; // Restart the RPM counter lastmicros = micros(); // Uptade lasmillis attachInterrupt(0, rpm_fan, FALLING); //enable interrupt  }}void rpm_fan(){ /* this code will be executed every time the interrupt 0 (pin2) gets low.*/  rpmcount++;}`

greenbarron64

Posts: 15
Joined: Wed Jun 15, 2016 11:57 am

Re: Read RPM Very Fast on Arduino UNO

Code: Select all | TOGGLE FULL SIZE
` if (micros() - lastmicros == 1000){  /*Uptade every one second, this will be equal to reading frecuency (Hz).*/`

1000 microseconds is not 1 second. It is one millisecond.

What you want to do is count pulses instead of microseconds. When you get to 16 pulses, you have one revolution. At that point the value of (micros() - lastmicros) will be the number of microseconds for one revolution. From that you should be able to calculate the RPM with reasonably good accuracy.

For even more accuracy, you could count more pulses for more revolutions. You are basically trading off calculation latency vs accuracy.