max with a rotary encoder

If you pay $100 for your industrial-grade encoder you get nicely conditioned, pristine square waves out of your 2 outputs. A $2 encoder, however, is really two (three if it has a push-button) mechanical switches in a housing, and they are surprisingly bouncy. The Playground code is good for the former, but not the latter. Have a read of Debouncing Contacts and Switches, you will see (page 16) that using interrupts on the output of undebounced switches is a really bad idea unless you are sure the flip-flops in your micro can handle the bounce rise-times & pulse widths.

The solution is to use the debounce code in Ganslee document (page 20), triggered on a 1 ms timer interrupt. I've spent the last few evenings getting the following code to work, and it's really reliable. I've used the FrequencyTimer2 lib from the Playground, but since you have a fixed frequency you can probably hack it down significantly. The core routine (the timer interrupt) appears below, I have omitted the timer setup.

Note that there are four transitions that you can update the value of encoder0Pos on, I am only using one because my encoder goes through all 4 states in one "click", so I get one step per "click". Uncomment the others if yours is different. Note also that encoder0Pos needs to be declared volatile, this prevents the compiler from caching its value if you set it then read it back immediately (the interrupt may have triggered & the value updated between the two operations, you want the updated value, not the one you just wrote).

Add another element to the State[] array and process it in the same manner if you want to debounce the push-button as well. My encoder doesn't have one, so neither does my code.

The code assumes that A & B are connected to pins configured as input ( pinMode(encoder0PinA, INPUT); ), with the internal pullups enabled ( digitalWrite(encoder0PinA, HIGH); ), and the common pin to ground.

#define encoder0PinA  2
#define encoder0PinB  4

volatile int encoder0Pos = 0;

// Service routine called by a timer interrupt
void DebounceSwitch()
{
  static unsigned char rawEncoder0A = 1;
  static unsigned char rawEncoder0B = 1;
  static unsigned char debouncedEncoder0A = 1;  // debounced state of encoder0PinA 
  static unsigned char debouncedEncoder0B = 1;  // debounced state of encoder0PinB 
  static uint16_t State[] = { 0, 0  }; // Current debounce status of encoder pins { A, B }

  rawEncoder0A = digitalRead(encoder0PinA);
  rawEncoder0B = digitalRead(encoder0PinB);

  // Encoder Pin A
  State[0]=(State[0]<<1) | rawEncoder0A | 0xe000;
  if(State[0]==0xf000) { // High --> Low Transition
    if (!debouncedEncoder0B) {   // check channel B to see which way
      // encoder is turning  
      encoder0Pos = encoder0Pos + 1;          // CW
    } 
    else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }
    debouncedEncoder0A = 0;
  }
  if(State[0]==0xefff) { // Low --> High Transition
    //if (!debouncedEncoder0B) {  // check channel B to see which way
    //  // encoder is turning
    //  encoder0Pos = encoder0Pos - 1;         // CCW
    //} 
    //else {
    //  encoder0Pos = encoder0Pos + 1;         // CW
    //}
    debouncedEncoder0A = 1;
  }

  // Encoder Pin B
  State[1]=(State[1]<<1) | rawEncoder0B | 0xe000;
  if(State[1]==0xf000) { // High --> Low Transition
    //if (debouncedEncoder0A == HIGH) {   // check channel A to see which way
    //  // encoder is turning  
    //  encoder0Pos = encoder0Pos + 1;          // CW
    //} 
    //else {
    //  encoder0Pos = encoder0Pos - 1;          // CCW
    //}
    debouncedEncoder0B = 0;
  }
  if(State[1]==0xefff) { // Low --> High Transition
    //if (debouncedEncoder0A == HIGH) {  // check channel A to see which way
    //  // encoder is turning
    //  encoder0Pos = encoder0Pos - 1;         // CCW
    //} 
    //else {
    //  encoder0Pos = encoder0Pos + 1;         // CW
    //}
    debouncedEncoder0B = 1;
  }

  // Clamp
  if (encoder0Pos < 0) encoder0Pos = 0;
  if (encoder0Pos > 32) encoder0Pos = 32;

}

Hope this helps,

Sam.