0

Read RPM Very Fast on Arduino UNO
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Read RPM Very Fast on Arduino UNO

by greenbarron64 on Mon Feb 11, 2019 5:07 pm

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.

Can someone please help me make it work for my 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/Volatile
float 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. Drechsler

The 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 signal
goes 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 above
Where:
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 wait
the 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/9299
const int ledPin =  9; //LED pin on the arduino ethernet, to help visualize the notch passing by

int 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

by adafruit_support_bill on Mon Feb 11, 2019 5:15 pm

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/

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

Re: Read RPM Very Fast on Arduino UNO

by greenbarron64 on Mon Feb 11, 2019 6:40 pm

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

by adafruit_support_bill on Mon Feb 11, 2019 7:31 pm

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

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

Re: Read RPM Very Fast on Arduino UNO

by greenbarron64 on Mon Feb 11, 2019 8:27 pm

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/Volatile
float 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

by adafruit_support_bill on Mon Feb 11, 2019 9:03 pm

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.

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

Please be positive and constructive with your questions and comments.