Rotary Encoder works one way but not the other?

Hi All,

I plan on using rotary encoders in my project to control a menu. Having never used arduino or rotary encoders before, I followed this tutorial http://www.hobbytronics.co.uk/arduino-tutorial6-rotary-encoder, just to get the basics to work. However I am using a different encoder http://docs-europe.electrocomponents.com/webdocs/1364/0900766b81364717.pdf

Spinning counter-clockwise (making the LED glow brighter) at any speed is fine and works reliably, however spinning the opposite direction is not. If I spin too fast, the LED brightness doesn’t seem to change at all, and if I spin it really slowly, it does go down but every so often the LED gets brighter (like every 3/4th click, but cant be sure its not random) rather than dimmer?

I have tried swapping the encoder out for another one but hit the same issues.

I was wondering if this is a known issue and there is a simple bit of code to add in to fix it? A bit like with debouncing and microswitches!

Cheers!

Edit: Code added

int brightness = 120;    // how bright the LED is, start at half brightness
int fadeAmount = 10;    // how many points to fade the LED by
unsigned long currentTime;
unsigned long loopTime;
const int pin_A = 12;  // pin 12
const int pin_B = 11;  // pin 11
unsigned char encoder_A;
unsigned char encoder_B;
unsigned char encoder_A_prev=0;

void setup()  {
  // declare pin 9 to be an output:
  pinMode(9, OUTPUT);
  pinMode(pin_A, INPUT);
  pinMode(pin_B, INPUT);
  currentTime = millis();
  loopTime = currentTime; 
} 

void loop()  {
  // get the current elapsed time
  currentTime = millis();
  if(currentTime >= (loopTime + 5)){
    // 5ms since last check of encoder = 200Hz  
    encoder_A = digitalRead(pin_A);    // Read encoder pins
    encoder_B = digitalRead(pin_B);   
    if((!encoder_A) && (encoder_A_prev)){
      // A has gone from high to low 
      if(encoder_B) {
        // B is high so clockwise
        // increase the brightness, dont go over 255
        if(brightness + fadeAmount <= 255) brightness += fadeAmount;               
      }   
      else {
        // B is low so counter-clockwise      
        // decrease the brightness, dont go below 0
        if(brightness - fadeAmount >= 0) brightness -= fadeAmount;               
      }   

    }   
    encoder_A_prev = encoder_A;     // Store value of A for next time    
    
    // set the brightness of pin 9:
    analogWrite(9, brightness);   
   
    loopTime = currentTime;  // Updates loopTime
  }
  // Other processing can be done here
                           
}

Post YOUR code, using code tags ("</>" button).

Just tried it with an old encoder had to hand ( an Alps) at it works ok, but be aware that program only checks for a change in signal every so often, so if you turn the encoder very fast it can get false readings.

If you change its looptime then you get a more even response.

One this line, change the 5 to 1

if(currentTime >= (loopTime + 5)) {

Hi,
Are you using the pullup resistors?

Can you please post a copy of your sketch, using code tags?
They are made with the </> icon in the reply Menu.
See section 7 http://forum.arduino.cc/index.php/topic,148850.0.html

Tom… :slight_smile:

Try adding the filters at the bottom of page 2 here for mechanical rotary encoders: http://www.bourns.com/docs/Product-Datasheets/PEC12R.pdf This has worked well in projects where I used this encoder.

Just to let you know I update the original post to include the code!

below I have beefed it up further, by adding in serial prints, to see how the program is looping through the code.

void loop()  {
 Serial.println("loop start");
  // get the current elapsed time
  currentTime = millis();
  if(currentTime >= (sampleTime + 5)){
    Serial.println("taking a reading");
    // 5ms since last check of encoder = 200Hz  
    encoder_A = digitalRead(pin_A);    // Read encoder pins
    encoder_B = digitalRead(pin_B); 
    
    Serial.print("encoder_A=");
    Serial.println(encoder_A);
    Serial.print("encoder_B=");
    Serial.println(encoder_B) ; 
    Serial.print("encoder_A_prev=");
    Serial.println(encoder_A_prev);
    
    if((!encoder_A) && (encoder_A_prev)){
      // A has gone from high to low 
      Serial.println("change in state at A");
      if(encoder_B) {
        // B is high so clockwise
        // increase time
        Serial.println("Time Increase");
        BlinkTime += Onesec;
        Serial.print("Blinktime=");
        Serial.println(BlinkTime) ;              
      }   
      else {
        // B is low so counter-clockwise      
        // decrease the brightness, dont go below 0
        Serial.println("Time Decrease");
        if(BlinkTime - Onesec >= 0) BlinkTime -= Onesec; 
        Serial.print("Blinktime=");
        Serial.println(BlinkTime)  ;             
      }   
    }   
    encoder_A_prev = encoder_A;     // Store value of A for next time    
    sampleTime = currentTime;  // Updates sampleTime
   }

Looking at this excerpt from the serial monitor:

loop start
taking a reading
encoder_A=1
encoder_B=1
encoder_A_prev=0    <---------
loop start
taking a reading
encoder_A=1
encoder_B=1
encoder_A_prev=1
loop start
taking a reading
encoder_A=1
encoder_B=1
encoder_A_prev=1
loop start
taking a reading
encoder_A=1
encoder_B=1
encoder_A_prev=1

I have put in an arrow where I think it starts to go wrong, surely as there has been a change (going from low to high) it should go through the if statements and deduce that the encoder is being spun anti-clockwise?
I am sure that functionality is in the code—> if((!encoder_A) && (encoder_A_prev)){
I can’t see why it wouldn’t do it?

Also it seems weird that it is pretty much always reading highs from both pins? I am very confident I have wired up my circuit correctly. I was wondering if it was a sampling issue (happen to sample at the same point every loop), but I am using a 24 pulse rotary encoder and sampling at 200hz which are different frequencies…

I have put in an arrow where I think it starts to go wrong, surely as there has been a change (going from low to high) it should go through the if statements and deduce that the encoder is being spun anti-clockwise?
I am sure that functionality is in the code—> if((!encoder_A) && (encoder_A_prev)){
I can’t see why it wouldn’t do it?

loop start
taking a reading
encoder_A=1
encoder_B=1
encoder_A_prev=0    <---------

Your observation that pinA has gone from low to high is correct, but the highest conditional is only entered if pin A went from high to low.

if((!encoder_A) && (encoder_A_prev)){
      // A has gone from high to low

The nested if else statements only relate to the state of pin B.

Cheers cattledog! That is obviously an issue (I want it to notice any change in state of A, not just high to low).

Any problems with the new code below?

void loop()  {
  // get the current elapsed time
  currentTime = millis();
  if(currentTime >= (sampleTime + 5)){
    // 5ms since last check of encoder = 200Hz  
    encoder_A = digitalRead(pin_A);    // Read encoder pins
    encoder_B = digitalRead(pin_B); 
        
    if(encoder_A != encoder_A_prev){
      // A has changed state 
         if(encoder_A == 1) {
        // A is high so clockwise
            if(encoder_B == 0){
        // B low so clockwise spin
        BlinkTime += Onesec;              
      }   
      else {
      // B High so anticlockwise spin
      if(BlinkTime - Onesec >= 0) BlinkTime -= Onesec;
      }
      else {
        // A is low
           if(encoder_B == 0){
           // B low so clockwise spin
            BlinkTime += Onesec;              
       }
      else {
      // B High so anticlockwise spin
      if(BlinkTime - Onesec >= 0) BlinkTime -= Onesec;
   }
    encoder_A_prev = encoder_A;     // Store value of A for next time    
    sampleTime = currentTime;  // Updates sampleTime
   }

Should look to see if there is a change in the state of A, then find if A is now low or high, and then action different responses (adding to BlinkTime or taking away from BlinkTime) depending on if B is found to be low or high.

Two sections of code are basically the same (about actioning a response depending on the state of B). Is there a simpler way of doing it, rather than pasting it within different if statements as I have done?

Should look to see if there is a change in the state of A, then find if A is now low or high, and then action different responses (adding to BlinkTime or taking away from BlinkTime) depending on if B is found to be low or high.

Two sections of code are basically the same (about actioning a response depending on the state of B). Is there a simpler way of doing it, rather than pasting it within different if statements as I have done?

By triggering off of a change in pinA (instead of both pins) you are effectively going to read two of the four quadrature steps available. But, this level of resolution alllows for a very simple analysis routine.

If you study the high/low patterns of quadrature output you will see that in one direction B will read the same as A whether or not A goes high or low. In the other direction, B will be different than A when A changes whether or not it goes high or low.

Your loop will simplify to what I post below. I'm not sure how the encoder is wired so you may have to reverse A and B to make the logic work as described.

void loop()  {
  // get the current elapsed time
  currentTime = millis();
  if (currentTime >= (sampleTime + 5)) {
    // 5ms since last check of encoder = 200Hz
    encoder_A = digitalRead(pin_A);    // Read encoder pins
    encoder_B = digitalRead(pin_B);

    if (encoder_A != encoder_A_prev) { //encoder A has changed state

      if (encoder_B == encoderA) { //B and A are the same
        BlinkTime += Onesec;
      }
      else {
        // B and A are different
        if (BlinkTime - Onesec >= 0) BlinkTime -= Onesec;
      }

    }
    encoder_A_prev = encoder_A;     // Store value of A for next time
    sampleTime = currentTime;  // Updates sampleTime
  }
}

Trying out the code but running into some weeiirrddd issues, ones that havent come up before.
Code is below!
The new bit added seems to work ok, but from the serial monitor, the program isn’t looping through the code correctly! Code I am using is below…

unsigned long BlinkTime = 3000;    // LED blinks for 1000ms by default
unsigned int Onesec = 1000;    // LED can be incremented by steps of 10 seconds
unsigned long currentTime; // variable holding actual time
unsigned long sampleTime; // variable for time with encoder sampling
unsigned long previousTime; // variable for time with led blink
const int pin_A = 5;  // set pin 5
const int pin_B = 6;  // set pin 6
const int pin_LED = 9; // set pin 9
int LedState1 = HIGH; // Set Led to low by default
unsigned char encoder_A; //an unsigned big variable for clockwise spin
unsigned char encoder_B; //an unsigned big variable for anti-clockwise spin
unsigned char encoder_A_prev;

void setup()  {
  // declare pin 9 to be an output:
  Serial.begin(9600);
  pinMode(pin_LED, OUTPUT); //set the pin which the led is attached to as an output
  pinMode(pin_A, INPUT); //set pin 5 to input
  pinMode(pin_B, INPUT); //set pin 6 to input
  currentTime = millis(); //set variable to equal time in milliseconds since program start
  sampleTime = currentTime; //set both times equal to eachother
  previousTime = currentTime;
} 

void loop()  {
  // get the current elapsed time
  Serial.println("Loop Start");
  currentTime = millis();
  Serial.print("SampleTime=");
  Serial.println(sampleTime);
  Serial.print("CurrentTime=");
  Serial.println(currentTime);
  if (currentTime >= (sampleTime + 1)) {
    // 5ms since last check of encoder = 200Hz
    encoder_A = digitalRead(pin_A);    // Read encoder pins
    encoder_B = digitalRead(pin_B);
    Serial.print("Encoder_A Value=");
    Serial.println(encoder_A);
    Serial.print("Encoder_B Value=");
    Serial.println(encoder_B);
    Serial.print("Encoder_A_Prev Value=");
    Serial.println(encoder_A_prev);
    
    if (encoder_A != encoder_A_prev) { //encoder A has changed state

      if (encoder_B == encoder_A) { //B and A are the same
        BlinkTime += Onesec;
        Serial.println("Add to Time");
        Serial.print("Blink Time Value=");
        Serial.println(BlinkTime);
      }
      else {
        // B and A are different
        if (BlinkTime - Onesec >= 0) BlinkTime -= Onesec;
        Serial.println("Minus From Time");
        Serial.print("Blink Time Value=");
        Serial.println(BlinkTime);
      }
    }
    encoder_A_prev = encoder_A;     // Store value of A for next time
    sampleTime = currentTime;  // Updates sampleTime
  }                   
   currentTime = millis(); //make sure the value in current time is actually current
   Serial.print("PreviousTime=");
   Serial.println(previousTime);
   Serial.print("CurrentTime=");
   Serial.println(currentTime);
   if (currentTime - previousTime >= BlinkTime) { //if the time now - time on previous sample is equal to or bigger than blink time
          // if the LED is off turn it on and vice-versa:
         Serial.println("Time for blink exceeded");
         Serial.print("BlinkTime=");
         Serial.println(BlinkTime);
         Serial.print("previousTime=");
         Serial.println(previousTime);
         Serial.print("currentTime=");
         Serial.println(currentTime);
          
    if (LedState1 == LOW) { //if led is off
      LedState1 = HIGH; //turn it on
    } else { //if not (and is therefore on)
      LedState1 = LOW; // turn it off
    }
   }
      // set the LED with the ledState of the variable:
   digitalWrite(pin_LED, LedState1); //set LED to designated state (off or on) 
   previousTime = currentTime; //update the sample time
   Serial.print("Led State Value=");
   Serial.println(LedState1);
  }

If we assume nothing there is no change, so for example encoder_A=1 and encoder_A_prev=1, nothing will happen and the code will bypass the first if statement. In this case, on my monitor it should print out: Loop start>sample time and current time>previous time and current time>Ledstate

however, I am getting this…

Loop Start
SampleTime=8871
CurrentTime=9004
Encoder_A Value=1
Encoder_B Value=1
Encoder_A_Prev Value=1   <------ Misses out a section between here...
Led State Value=0            <------ and here! Was expecting it to print previous time and current time
Loop Start

I cant see why it is missing out the section it is, it isn’t within a conditional loop or anything.
The only thing that has changed is that I am using a new board, the other broke yesterday, the chip started making some high pitched noise whenever it was programmed…
This one does seem to have trouble being programmed, says there is no device on com4 unless I hit the reset/erase button on it every time!

(both boards are Due, using programming port)

When I run your code on my Uno, and there is no change to the state of A, I see the print out of Previous Time and Current Time before the led state.

Sorry I partially found the issue... my IDE wasnt seeing changes I made to the code? Not sure how I managed that but rebooting my computer fixed the issue.

when using your encoders, are you finding the readings for the encoders unreliable?

For example, if I spin my encoder 360 degrees clockwise for example, the code should attempt to count up 24 x 2 times...

But I am not seeing anywhere near that level of resolution? I have to keep spinning the dials constantly and then maybe after a few full turns it notices something? From the serial print out, whenever it checks the encoders it seems to be always reading 1, which makes it think there is no change and I guess thats why It isnt doing anything...

You having this issue?

I spin my encoder 360 degrees clockwise for example, the code should attempt to count up 24 x 2 times...

What specific model of encoder do you have. How may detents, and many cycles. Refer to the data sheet you posted.

when using your encoders, are you finding the readings for the encoders unreliable?

No. The mechanical encoders might have bounce, but should not be showing the lack of response you indicate.

I believe that you might have a wiring issue. Can you please provide a sketch of how the A and B outputs are connected to the Arduino. How is the ground of the encoder connected to the Arduino? Are you using any pull up or pull down resistors on the Arduino inputs?

Have attached my circuit I use.

I did have two more 10k resistors in with capacitors in line with the encoders (linking to ground) to clear noise as others suggested, but then my code really didnt pick up anything…

Encoder.PNG

Hi, "Spinning" has me worried, how fast is "Spinning", the encoder and arduino combination will have a finite maximum speed. The faster you turn the spindle the shorter are the pulse widths at the output of the encoder. Plus its a mechanical switched encoder, you might have to get a CRO (Oscilloscope) to check. Have you written a simple encoder program without the bells and whistles to check the capabilities of your project?

Tom... :)

It looks like you have pull ups on the A/B outputs. Try change these to pull downs to ground. The encoder specs say that the switches are normally open, and putting out 5v when closed. I think the Arduino inputs may be floating when the switches are open.

Pull ups are usually required on open collector outputs, but your encoder is a simple mechanical switch.

TomGeorge: Hi, "Spinning" has me worried, how fast is "Spinning", the encoder and arduino combination will have a finite maximum speed.

The best I can manage is a full rotation in 2 seconds! I deduce there will be 48 changes in logic for Pin A in one revolution, so 24 logic level changes per second (as I managed 1 full revolution in 2 seconds). Therefore the logic level change is occuring at 24Hz, which is much slower than my sampling speed of 200Hz so it should pick up every change?

TomGeorge: Have you written a simple encoder program without the bells and whistles to check the capabilities of your project?

Simpler than the one I have shown? No because the LED's are good at showing if something has been detected at the encoders. I guess I could just use serial prints but I sometimes get confused by them, have to be careful where you put them in the loops otherwise I have found myself thinking my program is operating incorrectly when infact it wasn't! Could just be an issue of me having only been properly programming for a week!

cattledog: It looks like you have pull ups on the A/B outputs. Try change these to pull downs to ground. The encoder specs say that the switches are normally open, and putting out 5v when closed. I think the Arduino inputs may be floating when the switches are open.

Pull ups are usually required on open collector outputs, but your encoder is a simple mechanical switch.

Ok, I was following this... ...but I am not using the same encoder so I will have a go with what you have suggested and see how it goes!

Unfortunately, attaching the resistors to ground didnt fix the issue. However, the serial monitor was constantly reading 0's rather 1's now, so it did clearly influence the circuit, but the detection of the encoder was just as random and unreliable as it was previously... :(

Forget about any code to read/interpret the encoder. Just attach the power,ground and A and B outputs to the Arduino. With digital read, and turning the encoder slowly by hand, can you see the outputs change state the correct number of times?

Have you watched my YouTube video on Rotary Encoders, it's video #19 and may help you figure out what's going on in your code (if you get my example code working first you will probably figure it all out by yourself!).

URL to my videos is in the signature of this very post. And don't forget to subscribe so you get notification of new videos that appear weekly-ish. All aimed at the beginner because we all have to start somewhere. And it may reduce the questions we get on this forum :confused: