Using Optical Encoder to measure RPM

I am using an arduino in some research I am conducting. I am measuring the voltages from two small motors and the RPMs of two wind turbines. The RPMs are going to be measured using a Bourns optical encoder (link below).

http://www.bourns.com/data/global/pdfs/ENC1J.pdf

I tried the code I had written but its not reading anything. This is the first project I have used the arduino for, so I am having some issues debugging it. Please help.

Code:

/*This program is designed to use an Arduino Uno to
data log values obtained from two small DC generators
and two quadrature rotary encoders. The data will be
written to a CSV file through the SD shield attached
to the Arduino Uno. */

//Include the library for the SD Card
#include <SD.h>

//Dictate which pins on the SD Shield are what
//SPI SD Card Pins
//MOSI = Pin 11
//Miso = Pin 12
//SCLK = Pin 13
int SDpin = 4;
int pow_pin = 8;

//Declare pin values for the two generators
int gen1Pin = 1;
int gen2Pin = 2;

//Declare constants for the Data logging
const int vreference = 5;
int NumPoints = 1;
float vout1 = 0.0;
float vout2 = 0.0;
int vin1;
int vin2;
long value1 = 0.0;
long value2 = 0.0;

//Initialize variables for the quadrature encoder
int Pulses1 = 0; //initial pulse value for encoder 1
int Pulses2 = 0; //initial pulse value for encoder 2
int NewPulse1 = 0; /Number of pulses when data is being read
from encoder 1
/
int NewPulse2 = 0; /Number of pulses when data is being read
from encoder 2
/
long dtStore1[2];
long dtStore2[2];
float dt1 = 0.0;
float dt2 = 0.0;
float Revolutions1 = 0.0;
float Revolutions2 = 0.0;
long Vel1 = 0.0;
long Vel2 = 0.0;

//Signal declaration for the Rotary encoder
int A_SIG1 = 0;
int B_SIG1 = 1;
int A_SIG2 = 0;
int B_SIG2 = 1;

void setup()
{
//Begin communicating to the computer
Serial.begin(115200);

//Initialize pins as input
pinMode(gen1Pin, INPUT);
pinMode(gen2Pin, INPUT);

Serial.println("Initializing Card...");

pinMode(SDpin, OUTPUT);
pinMode(pow_pin, OUTPUT);
digitalWrite(pow_pin, HIGH);

//If SD shield does not get powered, will cause program failure
if(!SD.begin(SDpin))
{
Serial.println("Card failed to initialize...");
return;
}
Serial.println("Initialization is complete...");

//Create a file to write data to
File logFile = SD.open("Log.csv", FILE_WRITE);
if(logFile)
{
logFile.println(", , , ,"); //prints a blank line
String header = "Number of Points, Voltage 1, Voltage 2, RPM 1, RPM 2";
logFile.println(header);
logFile.close();
Serial.println(header);
}
else
{
Serial.println("The log file could not be opened...");
}

//attach the interrupts for the rotary encoder
attachInterrupt(0, A_RISE1, RISING);
attachInterrupt(1, B_RISE1, RISING);
attachInterrupt(0, A_RISE2, RISING);
attachInterrupt(1, B_RISE2, RISING);
}

void loop()
{
Pulses1 = 0; //Set the initial pulse value to zero for encoder 1
Pulses2 = 0; //Set the initial pulse value to zero for encoder 2
dt1 = 0.0; //Set the time difference to zero
dt2 = 0.0; //Set the time difference to zero

//Can use micro() if millis() isn't fast enough
dtStore1[0] = millis();
dtStore2[0] = millis();

vin1 = analogRead(gen1Pin);
vin2 = analogRead(gen2Pin);

delay(1000); //delay one-second to allow computer to read values

dtStore1[1] = millis();
dtStore2[1] = millis();
NewPulse1 = Pulses1;
NewPulse2 = Pulses2;

value1 = vin1/1024.0;
value2 = vin2/1024.0;

dt1 = dtStore1[1] - dtStore1[0];
dt2 = dtStore2[1] - dtStore2[0];
Revolutions1 = NewPulse1/48.0; //Finding the total number of revolutions achieved
Revolutions2 = NewPulse2/48.0; //Finding the total number of revolutions achieved
Vel1 = Revolutions1/(dt1100060.0);
Vel2 = Revolutions2/(dt2100060.0);

//Create a string of data using the values collected
String dataString = String(NumPoints) + "," + String(value1) + ","

  • String(value2) + "," + String(Vel1) + "," + String(Vel2);

//Open the CSV file
File logFile = SD.open("Log.csv", FILE_WRITE);
if(logFile)
{
logFile.println(dataString); //Write the data string to the file
logFile.close();
Serial.println(dataString);
}
else
{
Serial.println("Couldn't open log file");
}
NumPoints++; //Increment point count by 1
delay(2500);
}

//////////////
//First Rotary Encoder
void A_RISE1(){
detachInterrupt(0);
A_SIG1 = 1;
if(B_SIG1 == 0){
//Forward motion
Pulses1++;
}
if(B_SIG1 == 1){
//Reverse Motion
Pulses1--;
}
attachInterrupt(0, A_FALL1, FALLING);
}
void A_FALL1()
{
detachInterrupt(0);
A_SIG1 = 0;

if(B_SIG1 == 1)
{
//Forward Motion
Pulses1++;
}
if(B_SIG1 == 0)
{
//Reverse Motion
Pulses1--;
}
attachInterrupt(0, A_RISE1, RISING);
}
void B_RISE1()
{
detachInterrupt(1);
B_SIG1 = 1;

if(A_SIG1 == 1)
{
//Forward Motion
Pulses1++;
}
if(A_SIG1 == 0)
{
//Reverse Motion
Pulses1--;
}
attachInterrupt(1, B_FALL1, FALLING);
}
void B_FALL1()
{
detachInterrupt(1);
B_SIG1 = 0;

if(A_SIG1 == 1)
{
//Forward Motion
Pulses1++;
}
if(A_SIG1 == 0)
{
//Reverse Motion
Pulses1--;
}
attachInterrupt(1, B_RISE1, RISING);
}
///////////
//Second Rotarty Encoder
void A_RISE2()
{
detachInterrupt(0);
A_SIG2 = 1;

if(B_SIG2 == 0)
{
//Forward Motion
Pulses2++;
}
if(B_SIG2 == 1)
{
//Reverse Motion
Pulses2--;
}
attachInterrupt(0, A_FALL2, FALLING);
}
void A_FALL2()
{
detachInterrupt(0);
A_SIG2 = 0;

if(B_SIG2 == 1)
{
//Forward Motion
Pulses2++;
}
if(B_SIG2 == 0)
{
//Reverse Motion
Pulses2--;
}
attachInterrupt(0, A_RISE2, RISING);
}
void B_RISE2(){
detachInterrupt(1);
B_SIG2 = 1;
if(A_SIG2 == 1){
//Forward Motion
Pulses2++;
}
if(A_SIG2 == 0){
//Reverse Motion
Pulses2--;
}
attachInterrupt(1, B_FALL2, FALLING);
}
void B_FALL2(){
detachInterrupt(1);
B_SIG2 = 0;
if(A_SIG2 == 1){
//Forward Motion
Pulses2++;
}
if(A_SIG2 == 0){
//Reverse Motion
Pulses2--;
}
attachInterrupt(1, B_RISE2, RISING);
}

Again, thank you for input!

why not use the Encoder library.

I tried rolling my own but Encoder does the job much better. Increments a counter. So unless you need to do something on every rising or falling edge (not necessary if RPM is all you need to measure) Encoder will do just fine.

http://www.pjrc.com/teensy/td_libs_Encoder.html

You don't need a massive amount of code to illustrate your problem. You need to create small sketches that perform a single action, like reading the encoder or reading the voltage from the motor. When that sketch works, you can incorporate the functionality into your main program.

Which you posted incorrectly.

Typically, you need two interrupt pins to read an encoder at full speed if knowing direction is important. For two encoders, that means 4 interrupt pins. Only the Mega and DUE have that many.

Detaching and reattaching interrupts in the interrupt service routine is not necessary. Use the correct interrupt trigger (CHANGE) and determine whether the change was a rise or a fall. Much faster than changing the interrupt trigger.

I see lots of problems with your encoder reading method.

//attach the interrupts for the rotary encoder
  attachInterrupt(0, A_RISE1, RISING);
  attachInterrupt(1, B_RISE1, RISING);
  attachInterrupt(0, A_RISE2, RISING);
  attachInterrupt(1, B_RISE2, RISING);

You can't attach four different interrupt cases for the two channel signals from a quadrature encoder. The last two statements will 'override' the first two statements.

More importantly you don't need to use or process both encoder channels to measure the speed that the encoder shaft is turning, just detecting a single channel using RISING or FALLING will allow you to process the signal as a simple counter to then calculate speed by determining the numbers of pulses received over a fixed duration of time.

Lefty

PinChangeInt is also a good way to attach multiple hardware interrupts to interrupt-starved boards like the Uno.

But like I said.. why reinvent the wheel. the PJRC Encoder library does all that, and if you have no interrupts on the pins it will poll.

Another approach might be to see if there are any hardware solutions that convert the encoder pulses to signals you are already are familiar with. (I'm more of a hardware guy, so this is my usual approach. It keeps my sketches to something simple and can really control number of pins required, but isn't nearly as flexible as controlling the world through code.)

That said, it does help to know where to start looking. If you do a google image search for "quadrature decoder" you should get a lot of circuit results that you can study for ideas. There is a mix of discreet component (and/or TTL gate) solutions and dedicated decoder IC solutions. If all you need is RPM, some of the discreet solutions output just two signals that should be easily understood: direction and a pulse stream. Many of the dedicated decoder IC solutions are counters, and provide a direction and a count of pulses since the last reset.

For one of my projects I have a pinch-roller feeder. I'm using a stepper motor to turn the drive wheel of the feeder x number of steps. I then have a rotary encoder on the pinch wheel and I read out the number of counts to make sure my feed stock didn't slip on the feed wheel. I could try to read the encoder pulses while driving a stepper, but instead I'm letting something else count the encoder while my sketch concentrates on moving the stepper (and watching for the emergency-stop button on a hardware interrupt...).