Rotary Encoder with 2-bit gray code gets "stuck"?

Hi there,

I bought this rotary encoder off of amazon:

This one has a tactile "click" for each 20° turn or so.

My goal is to detect clockwise/anti-clockwise rotation. In the end I want to make a volume control knob for my PC. Also I am using a digispark.

This is my setup:

First Pin -> P2 Middle Pin -> Ground Last Pin -> P3

|500x280

I tried several code snippets I found on the internet (none worked), but in the end I wanted to understand the problem and really figure it out for myself anyway.

This is what I came up with:

#include "DigiKeyboard.h"

int pin2Previous = HIGH;

void setup() {
   pinMode(1, OUTPUT); // Debug LED
  
   pinMode(2, INPUT);
   digitalWrite(2, INPUT_PULLUP);
   
   pinMode(3, INPUT);
   digitalWrite(3, INPUT_PULLUP);   
}


void loop() {
  int pin2 = digitalRead(2);
  int pin3 = digitalRead(3);
  
  digitalWrite(1, not pin2); // Debug LED
  
  if (((pin2Previous == HIGH) && (pin2 == LOW) && (pin3 == LOW)) || 
     ((pin2Previous == LOW) && (pin2 == HIGH) && (pin3 == HIGH))) {
      DigiKeyboard.println("L");   
      delay(50); 
  }
  
  if (((pin2Previous == HIGH) && (pin2 == LOW) && (pin3 == HIGH)) || 
     ((pin2Previous == LOW) && (pin2 == HIGH) && (pin3 == LOW))) {
      DigiKeyboard.println("R");   
      delay(50); 
  }
  
  pin2Previous = pin2;
}

In my first experiments, it seemed to work, kinda. To make sure the rotary encoder did something, I made the onboard LED turn on whenever the first pin goes to ground.

What happens now is that I turn the encoder left, the LED turns on, but stays on and I have no idea why. I must be doing something wrong, but I am not sure.

Thank you very much.

Greetings, SecondSun

Without a data sheet showing the details of the switching pattern, it is nearly impossible to predict what will happen with your code. Please post a link.

Asides:

The standard way to initialize a port pin to input, with the internal pullup resistor is   pinMode(2, INPUT_PULLUP); Note that the constant INPUT_PULLUP is equal to 2.

The following may work

  digitalWrite(1, not pin2); // Debug LED

but everyone should understand the more common syntax

  digitalWrite(1, !pin2); // Debug LED

jremington: Without a data sheet showing the details of the switching pattern, it is nearly impossible to predict what will happen with your code. Please post a link.

I couldn't find the data sheet myself, which is part of the problem. The model number is stk0114002056, but google doesn't turn up with anything.

Asides:

The standard way to initialize a port pin to input, with the internal pullup resistor is     pinMode(2, INPUT_PULLUP); Note that the constant INPUT_PULLUP is equal to 2.

Thank you very much.

The following may work

    digitalWrite(1, not pin2); // Debug LED

but everyone should understand the more common syntax

  digitalWrite(1, !pin2); // Debug LED

Seems I programmed too much python recently. I just saw that "not" was a keyword and used it.

I suggest to use a multimeter or scope to figure out what the switching pattern is. Some rotary encoders make only momentary contacts, while transitioning from one detent to another.

You also have to worry about switch bounce, which can be eliminated using a capacitor and a couple of resistors. See this page or this thread (also with code for a grey code encoder).

Do not use an RC low pass filter to debounce a quadrature encoder, that will end badly.

Lose the delay(50)'s and it will probably just work fine. You need to capture every transition, and
the delays are allowing you to miss them and thus fail to account transitions correctly. If the switch
bounces, the accounting will get it right so long as you read the pins fast enough, which means
no delaying. Typically people use interrupts to read quadrature encoders as this works nicely and
any delays() elsewhere in the code don’t break the encoder logic.

Removing the delay doesn't do anything. Most of the time, that part of the code isn't even executed. To me it seems like the microcontroller itself freezes, because if I manually connect P2 to 5V, the LED won't turn off.

Do not use an RC low pass filter to debounce a quadrature encoder, that will end badly

What utter nonsense.

MarkT: Do not use an RC low pass filter to debounce a quadrature encoder, that will end badly.

What is bad about it? I've got some encoders that were giving me trouble until I added 22pF to them. That cleaned up the signals perfectly. (R in this case was the Teensy internal pullup.)

Not only do a number of manufacturers specifically recommend RC filters for use with similar knob encoders, they sell modules with the filters built in.

I use a similar rotary encoder for my projects. The following code is non blocking and uses interrupts. You will need to add about .1uf caps on all switches (clock, data, and switch ) to ground to prevent false counting from the switch bounce. I can turn the nob as fast as I can by hand and have accurate results. Here is my code:

#define SwitchPin 2
#define ClockPin  3
#define DataPin   4

volatile uint8_t EncodeCTR;
volatile uint8_t EncoderChange;
volatile uint8_t SwitchCtr;


void setup() {
  pinMode(SwitchPin, INPUT_PULLUP);
  pinMode(ClockPin, INPUT_PULLUP);
  pinMode(DataPin, INPUT_PULLUP);
  Serial.begin(115200);
  attachInterrupt(digitalPinToInterrupt(SwitchPin), Switch, FALLING);
  attachInterrupt(digitalPinToInterrupt(ClockPin), Encode, FALLING);
}

void loop() {


  if (EncoderChange || SwitchCtr) {

    EncoderChange = 0;
    Serial.print("EncodeCTR: ");
    Serial.print(EncodeCTR);
    Serial.println();
    Serial.print("Switch Pressed ");
    Serial.println(SwitchCtr);
    SwitchCtr = 0;
  }
}



void Switch() {
  static unsigned long DebounceTimer;
  if ((unsigned long)(millis() - DebounceTimer) >= (100)) {
    DebounceTimer = millis();
    if (!SwitchCtr) {
      SwitchCtr++;
    }
  }
}
void Encode() {
  static unsigned long DebounceTimer;
  if ((unsigned long)(millis() - DebounceTimer) >= (100)) {
    DebounceTimer = millis();

    if (digitalRead(DataPin) == HIGH)
    {
      EncodeCTR++;
    }
    else {
      EncodeCTR--;
    }
    EncoderChange++;

  }
}

There is a way to get 4x the resolution if you need more detail in your counting but this is a great start. Z