Rotary encoder on an Arduino

Attach a rotary encoder to an Arduino and make it count when you turn the knob. How does it work?

IMG_3119

The encoder has two lines, an A line on the left, a ground line in the middle, and a B line on the right. A and B output so-called Gray codes when you turn the knob, the Gray codes reveal the sense of rotation, the knob has 24 position in one turn, and with each click line A or B will change like a switch does.

I started to play around with existing Arduino code inspired by an article on circuits at home [link] and I added a few bells and whistles. There are some comments in the code, you will soon learn that there is a state transition table. My  explanation of this table goes in four handwritten sheets after the code.

#define ENC_A 14        // this is analog pin A0
#define ENC_B 15        // analog pin A1 on the arduino Uno
#define ENC_PORT PINC   // ENC_PORT gets all analog pins in one byte
#define TIMEOUT 2       // feel free to experiment around, 2 msec was ok for me
#define DEBUG 0         // 1 gives a bit more output
#define ROTATE 0        // counter from 0 to 255 rotates across boundary or not
#define CLOCKWISE       // undefine if you want a counterclockwise counter

unsigned long int timer = 0;
uint8_t encoder,prevencoder;

void setup() {
  pinMode(ENC_A, INPUT); digitalWrite(ENC_A, HIGH);  // it is A0
  pinMode(ENC_B, INPUT); digitalWrite(ENC_B, HIGH);  // it is A1
  Serial.begin (115200);
  Serial.println("Start");  
}

void loop() {
 static uint8_t counter = 0;      // output of the encoder
 int8_t tmpdata;
 if ((millis() - timer) > TIMEOUT) {
    tmpdata = read_encoder();
    if (DEBUG) {
      if (prevencoder != encoder) BinPrint( encoder );
      prevencoder = encoder;
    }
    if (tmpdata) {
      Serial.println(counter, DEC);
      if (ROTATE) {
        counter += tmpdata;
      } else {
        if ((counter < 255) && (tmpdata == +1)) counter += tmpdata;
        if (  (counter > 0) && (tmpdata == -1)) counter += tmpdata;
      }
    }
    timer = millis();
  }  
}

// returns change in encoder state (-1,0,1) 
int8_t read_encoder()
{
  // put the gray codes in four quadrants and start to count, I checked 
  // this in the following way
  //
  //   01 | 00
  //   -------
  //   11 | 10
  //
  // and the start counting the transitions across the boundaries
  //
  #ifdef CLOCKWISE
    // this is for clockwise turning
    static int8_t enc_states[] = {0,-1,+1,0,+1,0,0,-1,-1,0,0,+1,0,+1,-1,0};   
  #else
     // this is for counterclockwise turning
    static int8_t enc_states[] = {0,+1,-1,0,-1,0,0,+1,+1,0,0,-1,0,-1,+1,0};   
  #endif
  static uint8_t old_AB = 0;                // only does this once actually
  encoder = ENC_PORT;                       // I want to be able to debug
  old_AB <<= 2;                             // remember previous state
  old_AB |= ( encoder & 0x03 );             // form the state byte 
  return ( enc_states[( old_AB & 0x0f )]);  // &0x0f zeros out other bits
}

// shows the state of the encoder ports when we are debugging
void BinPrint( uint8_t copy ) {
  for (int i=0; i<8; i++) {
    if (copy & 0x01) {
      Serial.print("1");
    } else {
      Serial.print("0");
    }
    copy = copy >> 1;
  }
  Serial.println(" ");  
}

Here is the theory:

IMG_3115IMG_3116IMG_3117IMG_3118

Last update: 17-aug-2016

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s