Jul 172011
 
Rotary Encoder Decoded

Potentiometers are the easiest way to get an analog value into the Arduino. Nice for e.g. volume control or setting the speed of a fan or the brightness of a lamp.

A more digital way is to use rotary encoder instead which return pulses or even absolute positions. I found one here and it works as expected. Without using interrupts I was able to decode it just fine, but this really should be done via interrupts, but I never used interrupts before, so I checked via Google and found this. Works well and does not need a polling loop. The rotary encoder even looks identical to the one I bought.

For reference here the short test program:

/**
 * A simple incremental Rotary Encoder decoder example
 * [email protected]
 * http://www.rocketnumbernine.com/2010/03/06/decoding-a-rotary-encoder/
 * use freely
*/

#include <util/delay.h>

volatile uint8_t pinValues[2] = {0,0};
volatile int position = 0;

const int APin=2;
const int BPin=3;

void setup()
{
  Serial.begin(38400);
  pinMode(APin, INPUT); // 2 Encoder pins as inputs
  pinMode(BPin, INPUT);
  digitalWrite(APin, HIGH); // use internal pull-ups
  digitalWrite(BPin, HIGH);

  // enable interrupts on those two pins:
  // Depends on APin=2 and BPin=3
  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();
}

ISR(PCINT2_vect)
{
  _delay_ms(1);
  int pin0 = digitalRead(APin);
  int pin1 = digitalRead(BPin);
  if (pin0 != pinValues[0]) {
    rotary_encoder_change(0, pin0);
  } else if (pin1 != pinValues[1]) {
    rotary_encoder_change(1, pin1);
  }
} 

void rotary_encoder_change(uint8_t changedPin, uint8_t value)
{
  pinValues[changedPin] = value;
  position += ((pinValues[0] == pinValues[1]) ^ changedPin) ? 1 : -1;
}

void loop()
{
  Serial.println(position);
  delay(200);
}

Small addition: Arduino Playground has some more info.