Sunday, December 11, 2011

Arduino + Rotary Encoders, rev1

After a few days of reading forums of how people can't get accurate results from a rotary encoder from an arduino, I finally figured out a way to make it work for me. In my application, its ok to drop a few "ticks" of the encoder but I really wanted to avoid over-counter and much worse, count in wrong direction. Other solutions often cause both when rotating at slow speeds. Attached is my 1st implementation that only uses the interrupt to notify the arduino that it should look at the encoder pins and instead uses the program loop to read the values. This may not be optimal for code that uses excessive delays but it works well for my application.

   read a rotary encoder with interrupts
   Encoder hooked up with common to GROUND,
   encoderPinA to pin 2, encoderPinB to pin 3
   it doesn't matter which encoder pin you use for A or B  

   uses Arduino pullups on A & B channel outputs
   turning on the pullups saves having to hook up resistors 
   to the A & B channel outputs
   initially based on

#define encoderPinA 2
#define encoderPinB 3
#define interruptPin 0

// we can probably have 2 stage debounce where 2nd stage 
// debounce lower when a few ticks in same direction
const long encoderDebounce = 150;
long nextEncoderTime = 0;
boolean encoderActive = false;

void setup() { 
  pinMode(encoderPinA, INPUT); 
  digitalWrite(encoderPinA, HIGH);
  pinMode(encoderPinB, INPUT); 
  digitalWrite(encoderPinB, HIGH);
  attachInterrupt(interruptPin, startEncoder, CHANGE);  // encoder pin on interrupt 0 - pin 2
  Serial.begin (9600);

void loop() {
  if (encoderActive) {
    encoderActive = false;
    long now = millis();
    if (now > nextEncoderTime) {
      int a = digitalRead(encoderPinA);
      int b = digitalRead(encoderPinB);
      int encoderDirection = 0;
      if (a == b && a == HIGH) {
        encoderDirection = 1;
      } else if (a != b && b == HIGH) {
        encoderDirection = -1;
      } else {
        encoderDirection = 0;    
      if (encoderDirection != 0) {
        nextEncoderTime = now + encoderDebounce;


void startEncoder() {
  encoderActive = true;

As you can see if relies heavily on a high debounce time. If I reduce the debounce time to less than 50, I get lots of double-counts on slow rotations so I just played it safe and set it really high. I'll address the limited count speed in my next post.

1 comment:

sodamnmad said...

after playing with it a bit, i notice that i do get an occassional wrong-direction count when fast-rotating but they are few and i can live with them.