reading optical incremental encoder

Hi everyone, this is my first time using arduino, I've tried to get data from an optical incremental encoder (TRD-S2500BD from automation direct) to an arduino board mega 2560, but codes posted in playground for rotary encoders doesn't work. The encoder has a resolution of 2500 pulses per revolution. I'm using just two channels (A and B). The down code is posted here Arduino Playground - HomePage. This is encoder datashhet http://www.automationdirect.com/static/specs/encoderld.pdf. Perhaps 2500 ppr is too much for arduino ??????
#define encoder0PinA 2
#define encoder0PinB 3
volatile unsigned int encoder0Pos = 0;
void setup() {
pinMode(encoder0PinA, INPUT);
pinMode(encoder0PinB, INPUT);
// encoder pin on interrupt 0 (pin 2)
attachInterrupt(0, doEncoderA, CHANGE);
// encoder pin on interrupt 1 (pin 3)
attachInterrupt(1, doEncoderB, CHANGE);
Serial.begin (9600); Serial.println("START READING");
}
void loop(){ /*Do stuff here */}
void doEncoderA(){
// look for a low-to-high on channel A
if (digitalRead(encoder0PinA) == HIGH) {
// check channel B to see which way encoder is turning
if (digitalRead(encoder0PinB) == LOW) {
encoder0Pos = encoder0Pos + 1; // CW
}
else {
encoder0Pos = encoder0Pos - 1; // CCW
}
}
else // must be a high-to-low edge on channel A
{
// check channel B to see which way encoder is turning
if (digitalRead(encoder0PinB) == HIGH) {
encoder0Pos = encoder0Pos + 1; // CW
}
else {
encoder0Pos = encoder0Pos - 1; // CCW
}
}
Serial.println (encoder0Pos, DEC);
// use for debugging - remember to comment out
}
void doEncoderB(){
// look for a low-to-high on channel B
if (digitalRead(encoder0PinB) == HIGH) {
// check channel A to see which way encoder is turning
if (digitalRead(encoder0PinA) == HIGH) {
encoder0Pos = encoder0Pos + 1; // CW
}
else {
encoder0Pos = encoder0Pos - 1; // CCW
}
}
// Look for a high-to-low on channel B
else {
// check channel B to see which way encoder is turning
if (digitalRead(encoder0PinA) == LOW) {
encoder0Pos = encoder0Pos + 1; // CW
}
else {
encoder0Pos = encoder0Pos - 1; // CCW
}
}
}

but codes posted in playground for rotary encoders doesn't work

Yes it does, you just mean that you have not been able to get it to work.

When posting code select it and hit the # icon please.

Perhaps 2500 ppr is too much for arduino

It depends on what speed the motor is going at dosn't it.

To give us a clue post a picture of your setup and a schematic of how you have wired it up.

The trick is to get things working a step at a time.
Can you read the values off the encoder? Do they change as you turn it by hand?
Serial.print is your friend.

in the file attached, the first sketch is for the 2500ppr encoder, the second one is for the encoder attached to a dc motor with a gearbox and output shaft speed is around 230rpm.
if I haven’t been able to get the code work, so What should I do ?? electronic isn’t my major.

You appere to be trying to use a print statement from inside an interrupt service routine. You can't do that.

I did a modification to attachInterrupt function, I deleted Serial.println() and passed to void loop, but despite of the fact Tx is blinking, I just get zeros from serial

#define encoder0PinA 2
#define encoder0PinB 3
volatile unsigned int encoder0Pos = 0;
void setup() {
  pinMode(encoder0PinA, INPUT); 
  pinMode(encoder0PinB, INPUT); 
// encoder pin on interrupt 0 (pin 2)
  attachInterrupt(0, doEncoderA, CHANGE);
// encoder pin on interrupt 1 (pin 3)
  attachInterrupt(1, doEncoderB, CHANGE);  
  Serial.begin (9600); Serial.println("START READING");
}

void doEncoderA(){
  // look for a low-to-high on channel A
  if (digitalRead(encoder0PinA) == HIGH) { 
    // check channel B to see which way encoder is turning
    if (digitalRead(encoder0PinB) == LOW) {  
      encoder0Pos = encoder0Pos + 1;         // CW
    } 
    else {
      encoder0Pos = encoder0Pos - 1;         // CCW
    }
  }
  else   // must be a high-to-low edge on channel A                                       
  { 
    // check channel B to see which way encoder is turning  
    if (digitalRead(encoder0PinB) == HIGH) {   
      encoder0Pos = encoder0Pos + 1;          // CW
    } 
    else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }
  }
  
}
void doEncoderB(){
  // look for a low-to-high on channel B
  if (digitalRead(encoder0PinB) == HIGH) {   
   // check channel A to see which way encoder is turning
    if (digitalRead(encoder0PinA) == HIGH) {  
      encoder0Pos = encoder0Pos + 1;         // CW
    } 
    else {
      encoder0Pos = encoder0Pos - 1;         // CCW
    }
  }
  // Look for a high-to-low on channel B
  else { 
    // check channel B to see which way encoder is turning  
    if (digitalRead(encoder0PinA) == LOW) {   
      encoder0Pos = encoder0Pos + 1;          // CW
    } 
    else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }
  }
}
void loop ()
{
Serial.println(encoder0Pos,DEC);
}

OK just step back from it.

Just write a routine that reads the two pins and prints out their value.

You need to isolate if this is a problem with getting the data into the arduino or if it is one of the arduino processing that data.

it seems working, but I want print data in degrees not in decimal system. I tried with this formula
grades=encoder0Pos360n/100, where n is gearbox ratio, but I get wrong data, so what's the correct way to get the number through serial.println in degrees ???

You need to use floating point values in all the variables and the constants. You see 360 is an integer and 360.0 is a floating point value.
You got the wrong answer due to the integer arithmetic.

Hi, thanks for advising me.
The code attached is for reading and get encoder data using attachInterrupt function and then convert that data to degrees, the ratio 360/400 means:
as encoder resolution is 100 cpr, but the code detects rising an falling edges from the two encoder channels, due to attachInterrupt configuration is set to CHANGE, so I got 4*100=400, so in 360 degrees there are 400 cpr. When I turn motor shaft a certain angle i.e. 180 degrees, it's supposed to get more or less the same value through serial. But I just get zeros through Serial.println function. It's the wrong formula ? or it's in the wrong place inside the code ?, and I apologize if you don`t get me, english isn´t my first language, I'm trying my best. Thanks once again.

/*PROGRAM TO READ ENCODER DATA AND CONVERT IT TO DEGRRES*/
#define A 20
#define B 21
volatile float count = 0;
float grades=0; /*variable to store converted value*/
void setup() {
  pinMode(A, INPUT); 
  pinMode(B, INPUT); 
// encoder pin on interrupt 3 (pin 20)
  attachInterrupt(3, doA, CHANGE);
// encoder pin on interrupt 2 (pin 21)
  attachInterrupt(2, doB, CHANGE);  
  Serial.begin (19200); Serial.println("START READING");

}

void doA(){
  // look for a low-to-high on channel A
  if (digitalRead(A) == HIGH) { 
    // check channel B to see which way encoder is turning
    if (digitalRead(B) == LOW) {  
      count = count + 1;         // CW
    } 
    else {
      count = count - 1;         // CCW
    }
  }
  else   // must be a high-to-low edge on channel A                                       
  { 
    // check channel B to see which way encoder is turning  
    if (digitalRead(B) == HIGH) {   
      count = count + 1;          // CW
    } 
    else {
      count = count - 1;          // CCW
    }
  }
 
}
void doB(){
  // look for a low-to-high on channel B
  if (digitalRead(B) == HIGH) {   
   // check channel A to see which way encoder is turning
    if (digitalRead(A) == HIGH) {  
      count = count + 1;         // CW
    } 
    else {
      count = count - 1;         // CCW
    }
  }
  // Look for a high-to-low on channel B
  else { 
    // check channel B to see which way encoder is turning  
    if (digitalRead(A) == LOW) {   
      count = count + 1;          // CW
    } 
    else {
      count = count - 1;          // CCW
    }
  }

}

void loop ()
{
  grades=count*(360/400);
Serial.println(grades); delay(500);
}

You don't listed do you!

grades=count*(360/400);

Is ALWAYS going to return zero. Because 360/400 is ALWAYS zero.
Try 360.0/400.0
Also try printing out just the value of count.

Done. the code now works perfectly. The thing is that when I link that code with another to change motor spin, everything is a complete mess. As you know the next code now works perfectly.

/*PROGRAM TO READ ENCODER DATA AND CONVERT IT TO DEGRRES*/
#define A 20
#define B 21
volatile float count = 0;
float grades=0; /*variable to store converted value*/
void setup() {
  pinMode(A, INPUT); 
  pinMode(B, INPUT); 
// encoder pin on interrupt 3 (pin 20)
  attachInterrupt(3, doA, CHANGE);
// encoder pin on interrupt 2 (pin 21)
  attachInterrupt(2, doB, CHANGE);  
  Serial.begin (19200); Serial.println("START READING");

}

void doA(){
  // look for a low-to-high on channel A
  if (digitalRead(A) == HIGH) { 
    // check channel B to see which way encoder is turning
    if (digitalRead(B) == LOW) {  
      count = count + 1;         // CW
    } 
    else {
      count = count - 1;         // CCW
    }
  }
  else   // must be a high-to-low edge on channel A                                       
  { 
    // check channel B to see which way encoder is turning  
    if (digitalRead(B) == HIGH) {   
      count = count + 1;          // CW
    } 
    else {
      count = count - 1;          // CCW
    }
  }
 
}
void doB(){
  // look for a low-to-high on channel B
  if (digitalRead(B) == HIGH) {   
   // check channel A to see which way encoder is turning
    if (digitalRead(A) == HIGH) {  
      count = count + 1;         // CW
    } 
    else {
      count = count - 1;         // CCW
    }
  }
  // Look for a high-to-low on channel B
  else { 
    // check channel B to see which way encoder is turning  
    if (digitalRead(A) == LOW) {   
      count = count + 1;          // CW
    } 
    else {
      count = count - 1;          // CCW
    }
  }

}

void loop ()
{
 grades=count*(360.0/400.0);
Serial.println(grades); delay(500);
}

and the next one uses an H-bridge LMD18200 (I attach datasheet) for make motor goes CW and CCW, that code works as well. The expression “TCCR1B = 0x01” is just for changing pwm frequency (arduino pwm frequency is by default 490Hz and one hear a noise comming from the motor, so I used the timer for raising that frequency in a no audible range)

/*PROGRAM TO MAKE MOTOR GOES FORWARD AND BACKWARD BY USING
  AN H-BRIDGE LMD18200*/

# define dir 13
# define pwm 12
# define brake 11
int r_pwm = 50;

void setup ()
{
  TCCR1B = 0x01;
  pinMode (dir, OUTPUT);
  pinMode (pwm, OUTPUT);
  pinMode (brake, OUTPUT);
}

void loop ()
{/*source1,sink2, the motor spins CW*/
  digitalWrite (dir, HIGH);
  delay(1);
  digitalWrite (pwm, HIGH);
  delay(1);
  digitalWrite (brake, LOW);
  delay(1);
  analogWrite(pwm,r_pwm);
  delay (1000);

  /*sink1,source2, the motor spins CCW*/
  digitalWrite (dir, LOW);
  delay(1);
  digitalWrite (pwm, HIGH);
  delay(1);
  digitalWrite (brake, LOW);
  delay(1);
  analogWrite(pwm,r_pwm);
  delay (1000);
  
}

and finally I put both codes together. So the idea is making motor spins from 0º to 120º in one direction and then from 120º to 0º in the other one using a for loop. But nothing works, the motor doesn’t track pwm signal as indicated (r_pwm=50), it just spins in one direction at the same power supply voltage (12V), and the serial just prints out the line “START READING” doesn’t return grades value. I wonder if the constrain function could be useful, but I’m very confused how to handle it inside the code.

/*Program to keep motor spinning in a range between 0º and 120º
  using a for structure*/
#define A 20
#define B 21
# define dir 13
# define pwm 12
# define brake 11
float r_pwm = 50;/*I chnaged from int to float but seems it doesn't have any effect*/
volatile float count = 0;
float grades=0;

void setup()
{
  TCCR1B = 0x01;
Serial.begin (19200); Serial.println("START READING");
pinMode (dir, OUTPUT);
pinMode (pwm, OUTPUT);
pinMode (brake, OUTPUT);
pinMode(A, INPUT); 
pinMode(B, INPUT);
attachInterrupt(3, doEncoderA, CHANGE);
attachInterrupt(2, doEncoderB, CHANGE); 
}

void doEncoderA(){
  // look for a low-to-high on channel A
  if (digitalRead(A) == HIGH) { 
    // check channel B to see which way encoder is turning
    if (digitalRead(B) == LOW) {  
      count = count + 1;         // CW
    } 
    else {
      count = count - 1;         // CCW
    }
  }
  else   // must be a high-to-low edge on channel A                                       
  { 
    // check channel B to see which way encoder is turning  
    if (digitalRead(B) == HIGH) {   
      count = count + 1;          // CW
    } 
    else {
      count = count - 1;          // CCW
    }
  }
  
}
void doEncoderB(){
  // look for a low-to-high on channel B
  if (digitalRead(B) == HIGH) {   
   // check channel A to see which way encoder is turning
    if (digitalRead(A) == HIGH) {  
      count = count + 1;         // CW
    } 
    else {
      count = count - 1;         // CCW
    }
  }
  // Look for a high-to-low on channel B
  else { 
    // check channel B to see which way encoder is turning  
    if (digitalRead(A) == LOW) {   
      count = count + 1;          // CW
    } 
    else {
      count = count - 1;          // CCW
    }
  }
}
void loop()
{
grades=count*(360.0/400.0);
for (grades=0;grades<120;)
  {/*source1,sink2, the motor spins CW*/
  digitalWrite (dir, HIGH);
  delay(1);
  digitalWrite (pwm, HIGH);
  delay(1);
  digitalWrite (brake, LOW);
  delay(1);
  analogWrite(pwm,r_pwm);
  }
for(grades=120;grades>0;)
  {/*sink1,source2, the motor spins CCW*/
  digitalWrite (dir, LOW);
  delay(1);
  digitalWrite (pwm, HIGH);
  delay(1);
  digitalWrite (brake, LOW);
  delay(1);
  analogWrite(pwm,r_pwm);
  }
Serial.println(grades); delay(500);
}

LMD18200.pdf (338 KB)

finally I put both codes together

But you didn't do it right.

  1. grades is only updated before you enter the for loop and therefore it can't change once inside it.
  2. You don't use for loops like that. It is more likely you want a while structure that keeps the motor on until the count exceeds a certain value.
  3. I would be surprised if you could actually do what you want due to the inertia in the spinning motor. You can't stop a motor dead even with flywheel breaking.

grades is only updated before you enter the for loop

but, grades is inside the "void loop", and the void loop is what arduino executes infinitely, so grades formula should be done infinitely, or I'm completely wrong ?

void loop()
{
grades=count*(360.0/400.0) ;
for (grades=0;grades<120; )
{/source1,sink2, the motor spins CW/
digitalWrite (dir, HIGH) ;
delay(1) ;
digitalWrite (pwm, HIGH) ;
delay(1) ;
digitalWrite (brake, LOW) ;
delay(1) ;
analogWrite(pwm,r_pwm) ;
}
While the loop() function repeats forever once the code enters the for loop it is no longer updated because the for loop does not alter the value of grades, there is nothing updating this value. As at the start of the for loop grades is set to zero, the for loop will continue forever because grades never gets up to 120 because nothing is changing it.

grades should be inside the for loop ? I just had done it

void loop()
{

for (grades=0;grades<120;)
  {/*source1,sink2, the motor spins CW*/
  digitalWrite (dir, HIGH);
  delay(1);
  digitalWrite (pwm, HIGH);
  delay(1);
  digitalWrite (brake, LOW);
  delay(1);
  analogWrite(pwm,r_pwm); grades=count*(360.0/400.0);
  }
for(grades=120;grades>0;)
  {/*sink1,source2, the motor spins CCW*/
  digitalWrite (dir, LOW);
  delay(1);
  digitalWrite (pwm, HIGH);
  delay(1);
  digitalWrite (brake, LOW);
  delay(1);
  analogWrite(pwm,r_pwm); grades=count*(360.0/400.0);
  }

now motor spins backward and forward, but it doesn’t it tracking the range (0º-120º) neither at the specified pwm signal(r_pwm=50). Something is inhibiting pwm action, doesn’t matter if I change duty cycle, motor spins at he same speed.

PWM will only change the speed of a motor when it is loaded. When a motor is unloaded the PWM makes little difference to the speed because the inertia of the motor keeps it moving during the time the power is off.

you’re right, but, Why in that code pwm works

/*PROGRAM TO MAKE MOTOR GOES FORWARD AND BACKWARD BY USING
  AN H-BRIDGE LMD18200*/

# define dir 13
# define pwm 12
# define brake 11
int r_pwm = 50;

void setup ()
{
  TCCR1B = 0x01;
  pinMode (dir, OUTPUT);
  pinMode (pwm, OUTPUT);
  pinMode (brake, OUTPUT);
}

void loop ()
{/*source1,sink2, the motor spins CW*/
  digitalWrite (dir, HIGH);
  delay(1);
  digitalWrite (pwm, HIGH);
  delay(1);
  digitalWrite (brake, LOW);
  delay(1);
  analogWrite(pwm,r_pwm);
  delay (1000);

  /*sink1,source2, the motor spins CCW*/
  digitalWrite (dir, LOW);
  delay(1);
  digitalWrite (pwm, HIGH);
  delay(1);
  digitalWrite (brake, LOW);
  delay(1);
  analogWrite(pwm,r_pwm);
  delay (1000);
  
}

and in that one doesn’t. I’m using the same pins, the same code above, just that now the program have to do some extra tasks, and what is curious the most is that when I click on serial monitor, the motor stops about one second and start up again, and when I close serial monitor happens the same, motor stops and star up again, even with an if or while structure.

/*Program to keep motor spinning in a range between 0º and 120º
  using a for structure*/
  #define A 20
#define B 21
# define dir 13
# define pwm 12
# define brake 11
float r_pwm = 50;
volatile float count = 0;
float grades=0;

void setup()
{
  TCCR1B = 0x01;
Serial.begin (19200); Serial.println("START READING");
pinMode (dir, OUTPUT);
pinMode (pwm, OUTPUT);
pinMode (brake, OUTPUT);
pinMode(A, INPUT); 
pinMode(B, INPUT);
attachInterrupt(3, doEncoderA, CHANGE);
attachInterrupt(2, doEncoderB, CHANGE); 
}

void doEncoderA(){
  // look for a low-to-high on channel A
  if (digitalRead(A) == HIGH) { 
    // check channel B to see which way encoder is turning
    if (digitalRead(B) == LOW) {  
      count = count + 1;         // CW
    } 
    else {
      count = count - 1;         // CCW
    }
  }
  else   // must be a high-to-low edge on channel A                                       
  { 
    // check channel B to see which way encoder is turning  
    if (digitalRead(B) == HIGH) {   
      count = count + 1;          // CW
    } 
    else {
      count = count - 1;          // CCW
    }
  }
  
}
void doEncoderB(){
  // look for a low-to-high on channel B
  if (digitalRead(B) == HIGH) {   
   // check channel A to see which way encoder is turning
    if (digitalRead(A) == HIGH) {  
      count = count + 1;         // CW
    } 
    else {
      count = count - 1;         // CCW
    }
  }
  // Look for a high-to-low on channel B
  else { 
    // check channel B to see which way encoder is turning  
    if (digitalRead(A) == LOW) {   
      count = count + 1;          // CW
    } 
    else {
      count = count - 1;          // CCW
    }
  }
}
void loop()
{
grades=count*(360.0/400.0);
for (grades=0;grades<120;)
  {/*source1,sink2, the motor spins CW*/
  digitalWrite (dir, HIGH);
  delay(1);
  digitalWrite (pwm, HIGH);
  delay(1);
  digitalWrite (brake, LOW);
  delay(1);
  analogWrite(pwm,r_pwm);
  }
for(grades=120;grades>0;)
  {/*sink1,source2, the motor spins CCW*/
  digitalWrite (dir, LOW);
  delay(1);
  digitalWrite (pwm, HIGH);
  delay(1);
  digitalWrite (brake, LOW);
  delay(1);
  analogWrite(pwm,r_pwm);
  }
Serial.println(grades); delay(250);
}

When you open or close the serial port the arduino is reset and the sketch starts again.

You only see speed changes form many turns of the motor not the small movement you are trying to do.

You are trying to use a DC motor like a stepping motor or a servo. You can not do this, as you have found out.

wow… I didn’t have any idea about serial, so it’s like push reset button on the board.

Well actually the motor is a DC brushed one, it just has two wires to power it and the encoder is attached.

Dear All,

please find attached my code, using CUIs (www.cui.com) AMT103-V incremental encoder and the Encoder.h library (courtesy of Paul J. Stoffregen, www.pjrc.com).

Should You have any questions, dont hesitate to contact me.
Feel free to use.

thanks for the help provided from this Forum!
Robert

encoder_20160408.ino (1.93 KB)