Fun with Arduino 25 Rotary Encoder with Switch

Rotary_EncoderA rotary encoder is a device that can be used to incremented or decremented the value of a variable in the Arduino. Possible applications are to control a LED strip light level via PWM, or to control a servo motor angle.

They are incredibly cheap, the encoder shown here is just €0,50 (it can be had here). It also has a switch, which is operated by pressing the shaft. The encoder connects to the Arduino via 3 inputs: Clock, Data and Switch.

SignalsThe Clock and Data signal names suggest they have different functions, but when the encoder is rotated they actually are similar block waves, just shifted half a pulse. Often they are simply called A and B, as they are in the diagram.

What we need to do in the Arduino is to detect the LOW to HIGH transitions one signal. If a transition is detected, we look at the other signal. If it is LOW (the green dotted lines) the encoder is rotating CCW, if it is HIGH (the red dotted lines) the encoder is rotating CW. We can now increment or decrement a variable based on this info.

 

 

This is the code that reads the encoder and switch. It changes the variable rotary_value and shows it on screen. We also send it to a pin with analogWrite() to control a LED via PWM. The switch is also read, we detect if it is pressed and we can do something if it is.

#define ENC_DATA_PIN       10
#define ENC_CLK_PIN        11
#define ENC_SW_PIN         12
#define LED_PWM_PIN         6
#define INCREMENT           1 // change this for different steps sizes

byte enc_clk, enc_clk_old;
byte enc_switch, enc_switch_old;
int rotary_value;

void setup() { 
  pinMode (ENC_CLK_PIN,INPUT_PULLUP);
  pinMode (ENC_DATA_PIN,INPUT_PULLUP);
  pinMode (ENC_SW_PIN,INPUT_PULLUP);
  enc_clk_old    = digitalRead(ENC_CLK_PIN);
  enc_switch_old = digitalRead(ENC_SW_PIN);
  Serial.begin (9600);
  Serial.println("Rotary Value");
  Serial.println(rotary_value);
}

void loop() {
// READ SWITCH
  enc_switch = digitalRead(ENC_SW_PIN);
  if((enc_switch_old == 1) && (enc_switch == 0)) { // 1->0 transition
    Serial.println("SWITCH is PRESSED");
    delay(10);
  }
  enc_switch_old = enc_switch;

// READ ROTARY
  enc_clk = digitalRead(ENC_CLK_PIN);
  if((enc_clk_old == 0) && (enc_clk == 1)) { // 0->1 transition
    if(digitalRead(ENC_DATA_PIN) == 1)
      rotary_value = rotary_value + INCREMENT;
    else
      rotary_value = rotary_value - INCREMENT;
    Serial.println(rotary_value);
    analogWrite(LED_PWM_PIN, rotary_value);
    delay(10);
  }
  enc_clk_old = enc_clk;
}

bouncingOK, this works … but for this one thing: every now and then we can get multiple readings with both the switch or the encoder. This is due to bouncing of the mechanical contacts. They are in the millisecond domain and the Arduino is fast enough to detect them. As a result our variable can sometimes show more than 1 increment. This can easily be overcome with a small delay, 10 ms will do.

Of course we rather not use a delay … it’s only OK if our code has no other tasks to perform. In video 27 we are going to use a timer to distinguish between short and long switch presses and between slow and fast rotating such that we can increment a value in small steps or in larger steps, which is often useful. In the process we will also kill the bouncing effect.

First, we are going to put our rotary encoder to use … in the next video we are going to build a servo tuning application.

Leave a comment