I'm using a 400 PPR encoder with an Arduino to measure encoder values. I was able to get the encoder to read values 0-400 for positive rotation and 0 through -400 for negative rotation and resetting to 0 once the encoder does a full rotation. This part of my code works, the issue I'm having is translating these positional values into RPM.
My approach was this: I am using the millis() function to keep track of overall program time, and a variable time_now to store this value. I have an if statement that runs each time millis() becomes greater than time_now by the REFRESH_RATE (in my case, this is defined as 1 mSec) so every 1 millisecond this runs.
My next approach is to take two samples every millisecond. The difference between pointSample1 and pointSample2 should be giving me the amount of ticks the encoder moves in 1 mSec. This gets a little complicated because it's essentially a 400 degree circle, so for example if pointSample1 is at 395 degrees and pointSample2 reads 5 degrees, we need to take the difference of the encoder resolution and pointSample1, then add to pointSample 2 or (PPR - pointSample1) + pointSample2.
Again, this SHOULD be giving me the ticks per mSecond which is then an easy conversion into RPM.
#define REFRESH_RATE 1 //sets the refresh rate for tracking RPM (in mSec)
volatile float temp, counter = 0; //This variable will increase or decrease depending on the rotation of encoder
int revsTotal = 0;
int pulseInitial = 0;
int PPR = 400; //Equal to the number of ticks per revolution of your encoder
unsigned long time_now = 0; //time since program start
int pulseDifferential = 24; //number of ticks per mSec for desired RPM
float pointSample1 = 0; //first tick sample
float pointSample2 = 0; //second tick sample
float pulsemSec = 0; //the amount of ticks the encoder is reading every millisecond
float RPM = 0; //RPM of the motor
void setup() {
pointSample1 = counter;
Serial.begin (4800);
pinMode(2, INPUT_PULLUP); //sets pin mode for pin 2
pinMode(3, INPUT_PULLUP); //sets pin mode for pin 3
//Setting up interrupt
//A rising pulse from encoder activated ai0(). AttachInterrupt 0 is DigitalPin nr 2 on most Arduinos.
attachInterrupt(0, ai0, RISING);
//B rising pulse from encoder activated ai1(). AttachInterrupt 1 is DigitalPin nr 3 on most Arduinos.
attachInterrupt(1, ai1, RISING);
}
void loop() {
// Send the value of counter
if( counter != temp ){ //if change is detected in the encoder, print the positional data values
Serial.println ("Positional Data: ");
Serial.println (counter);
temp = counter;
if( counter >= PPR or counter <= -PPR){ //This if statement resets the counter every time the encoder does a full revolution, protecting from reset after memory becomes filled
counter = 0;
}
}
if(millis() > (time_now + REFRESH_RATE) or millis() == 0){ //should run once every time the refresh rate is met (refresh rate = 1mSec, if statement runs once every mSec). millis() = 0; is for varibale overflow protection
pointSample2 = counter; //sets pointSample2 to the encoder position, pointSample1 starts at 0. This allows the difference between the two to be taken to determine the rotation per mSec
if(pointSample1 - pointSample2 < 0){ //conditional if / elseif statement checks the sign (positive or negative) of the ticks between samples, and makes sure that the pulses per mSec is always positive
pulsemSec = pointSample2 - pointSample1;
}
else if (pointSample1 - pointSample2 > 0){
pulsemSec = (PPR - pointSample1) + pointSample2;
}
RPM = (((pulsemSec / PPR) * 1000) * 60); //pulsemSec / PPR = revs per msec; revs per msec * 1000 = revs per sec; revs per sec * 60 = RPM
pointSample1 = pointSample2; //changes pointSample1 for the next cycle
time_now = millis(); //sets time_now to the amount of time since program start
Serial.println ("pulsemSec: ");
Serial.println (pulsemSec);
Serial.println ("RPM: ");
Serial.println (RPM);
}
}
void ai0() {
// ai0 is activated if DigitalPin nr 2 is going from LOW to HIGH
// Check pin 3 to determine the direction
if(digitalRead(3)==LOW) {
counter++;
}else{
counter--;
}
}
void ai1() {
// ai0 is activated if DigitalPin nr 3 is going from LOW to HIGH
// Check with pin 2 to determine the direction
if(digitalRead(2)==LOW) {
counter--;
}else{
counter++;
}
}
During testing, I had a motor spinning at about 473 RPM measured using a digital tachometer. According to the Arduino output, this translated to an RPM measured by my code as 13,350 RPM, a much higher number. I believe something is incorrect with the measuring of the pulsemSec variable and not the way that is converted into RPM, but I am not sure what the issue is exactly.