Optical Incremental Encoder Reading

I have a HP Optical Incremental Encoder (256 CPR) in which Pin 1 = A, Pin 2 = VCC, Pin 3 = GND, Pin 8 = B.

I already read and see tutorials for rotary encoder such this one :
https://playground.arduino.cc/Main/RotaryEncoders

I have written a program that is supposed to print out the position of my encoder;However, I am only getting zeros in my serial monitor. I want to be able to use interrupts. I would like to actually return the position in degrees of my encoder as we rotate it.

#define encoderPinA 2
#define encoderPinB 3
int counter =0;
void setup() {
  pinMode(encoderPinA, OUTPUT);
  pinMode(encoderPinB, OUTPUT);
  Serial.begin (9600);
  attachInterrupt(encoderPinA, isr,CHANGE);
}

void loop() {

  Serial.print(counter);

}
//Interrupts 
void isr(){

    int channel_A = digitalRead(encoderPinA);
    int channel_B = digitalRead(encoderPinB);
  
   if(channel_A == channel_B){
    counter++;
   }
   else{
    counter--;
   }
         
}

These should be INPUT, and possibly INPUT_PULLUP.

  pinMode(encoderPinA, OUTPUT);
  pinMode(encoderPinB, OUTPUT);

You will very likely discover other problems with the code. When you do, study your link more carefully.

yeas I declared it as inputs. I did study but im still printing 0. it is not working

Hi,
Can you post spec/data part number of the encoder please?

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Thanks.. Tom... :slight_smile:

Post a wiring diagram (anything but Fritzing).

int counter =0;Must be declared volatile to work in an ISR.

Why not start with code that actually works? There are plenty of encoder examples available.

#define encoderPinA 2
attachInterrupt(encoderPinA, isr,CHANGE);

This is not correct syntax for attachInterrupt()
https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/

you are right. I already modified. But I can't get it to print the position.

#define encoderPinA 2
#define encoderPinB 3
volatile int counter =0;
void setup() {
  pinMode(encoderPinA, INPUT);
  pinMode(encoderPinB, INPUT);
  Serial.begin (9600);
  attachInterrupt(digitalPinToInterrupt(encoderPinA), isr,CHANGE);
}

void loop() {

  Serial.print(counter);

}
//Interrupts 
void isr(){

    int channel_A = digitalRead(encoderPinA);
    int channel_B = digitalRead(encoderPinB);
  
   if(channel_A == channel_B){
    counter++;
   }
   else{
    counter--;
   }
         
}

Why haven't you posted a WIRING DIAGRAM?

I posted a photo.

QEDS-7163

IMG_3187.jpg

Moderator: Edited to show image using tags

Useless photo. Good luck with your project.

I provided the pins I am using from my encoder. I just connected those pins to my Arduino. I was hoping maybe someone can help me. thank you

These should be INPUT, and possibly INPUT_PULLUP.

yeas I declared it as inputs.

Did you try INPUT_PULLUP?

Many encoders have open collector outputs, and require pullups on the outputs to work correctly.

Yes. I am just getting a bunch of zeros in a infinite loop. As I move the encoder it does change to 1, etc. But it does not stop.

Could anyone of you tell me the difference between CHANGE AND RISING? my original code had change because I thought if a detect a change in both channels I can interrupt. However I just changed it to RISING and seems to input more "accurate" number. It is still missing some pulses if I go to fast.
Since my encoder is 256 cpr I should get (input in the serial monitor) in one rotation 256 right?
I feel like since I don't have a clear understanding of encoders my code is not efficient.
I hope you can help me guys! thanks

#define encoderPinA 2
#define encoderPinB 3
volatile int counter =0;
volatile int encoder0Pos = 0;
int val_new =0;
int val_old=0;
void setup() {

  pinMode(encoderPinA, INPUT);
  pinMode(encoderPinB, INPUT);
  Serial.begin (9600);
  attachInterrupt(digitalPinToInterrupt(encoderPinA), isr, RISING);
}

void loop() {


}
//Interrupts 
void isr(){

    int channel_A = digitalRead(encoderPinA);
    int channel_B = digitalRead(encoderPinB);
  
   if(channel_A == channel_B){
    counter++;
    Serial.print("Position: ");
  
     //int degrees = (counter) % 360 ;
       Serial.println(counter);
   }
   else{
    counter--;
   Serial.print("Position: ");
  
    // int degrees = (counter) % 360 ;
       Serial.println(counter);
   }
         
}

I already read and see tutorials for rotary encoder such this one :
Arduino Playground - RotaryEncoders

I feel like since I don't have a clear understanding of encoders

Read it again.

Could anyone of you tell me the difference between CHANGE AND RISING?

A RISING interrupt will be triggered by an input going from LOW to HIGH

A CHANGE interrupt will be triggered by both a RISING or FALLING (input going from HIGH to LOW) signal.

I suggest you look at this tutorial on interrupts. You should not use Serial printing within an ISR, and you should understand how to transfer "protected" data from an interrupt variable to the main program without risk of the value changing.
http://gammon.com.au/interrupts

Try some sample code you find for your encoder. Get it to work. If it doesn’t work, your circuit is wrong. If it works, edit the sample code in small steps that work, until you get to what you want.

I get it to work forward and not backward. I added a print statement to see if I am hitting my else in my isr_2 , it does print but it never decrements my counter. Not sure why.

#define encoderPinA 2
#define encoderPinB 3
volatile int counter =0;

volatile boolean flag;

void setup() {

  pinMode(encoderPinA, INPUT);
  pinMode(encoderPinB, INPUT);
  Serial.begin (9600);
  attachInterrupt(digitalPinToInterrupt(encoderPinA), isr_2, RISING);
}

void loop() {
  
   if(flag == true){     
        Serial.println(counter);
        flag = false;
  }
   
}
//Interrupts 
void isr(){
  flag = true;

    int channel_A = digitalRead(encoderPinA);
    int channel_B = digitalRead(encoderPinB);
  
   if(channel_A == HIGH && channel_B == HIGH){
    counter++;
   // Serial.println("BOTH HIGH = " + counter);
   }
   else{
    counter--;
   // Serial.println("ELSE counter = " + counter);
    }
}

void isr_2(){
flag = true;
  if(digitalRead(encoderPinA) == HIGH){
    if(digitalRead(encoderPinB) == LOW){
      counter = counter -1; //COUNTER CLOCK WISE
    }
    else{
      counter = counter +1; //CW
    }
  }
  else{
    if(digitalRead(encoderPinB) == LOW){
      counter = counter + 1; //CW
    }
    else{
      counter = counter - 1 ; //CCW
    }
  }
}

I found the issue I was connecting GND to a wrong pin in my decoder.

Quick question for this great community. I was able to read the position of my encoder and converted that position to an angle. However when I rotate the encoder right it gives me a negative output and when I rotate it left it gives me a positive output. It is backwards, I checked my Code and I can’t find the issue.
Output rotating right starting from zero:

-1
-2
-4
-5
-7
-8
-9
-11

Output rotating right starting from zero:
0
1
2
4
5
7
8
9
11

This is my final code :

#include <LiquidCrystal.h>

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);


#define encoderPinA 2
#define encoderPinB 3
#define CPR 256
volatile int counter =0;
volatile boolean flag;

volatile int var_degrees =0;

void setup() {

  pinMode(encoderPinA, INPUT);
  pinMode(encoderPinB, INPUT);
  Serial.begin (9600);
  attachInterrupt(digitalPinToInterrupt(encoderPinA), isr, RISING);
  lcd.clear();
  
}

void loop() {
  
   if(flag == true){     
        var_degrees = ((360/256.0)*counter);
        Serial.println(var_degrees);
        lcd.setCursor(0, 1);
        lcd.print("Degrees: ");
        lcd.setCursor(9, 1);
        lcd.print(var_degrees);
        flag = false;
  }
   
}
//Interrupts 

void isr_2(){
flag = true;
  if(digitalRead(encoderPinA) == HIGH){
    if(digitalRead(encoderPinB) == LOW){
      counter = counter -1; //COUNTER CLOCK WISE
    }
    else{
      counter = counter +1; //CW
    }
  }
  else{
    if(digitalRead(encoderPinB) == LOW){
      counter = counter + 1; //CW
    }
    else{
      counter = counter - 1 ; //CCW
    }
  }
}