Using a rotary encoder with Arduino

A rotary or “shaft” encoder is an angular measuring device. It is used to precisely measure rotation of motors or to create wheel controllers (knobs) that can turn infinitely (with no end stop like a potentiometer has). Some of them are also equipped with a pushbutton when you press on the axis (like the ones used for navigation on many music controllers). They come in all kinds of resolutions, from maybe 16 to at least 1024 steps per revolution, and cost from 2 to maybe 200 EUR. (From


This example is a low cost board from Keyes, I tried the example code from their website, but it needed some correction and even after that I was not happy with the result. I found this code on the Internet but cannot remember where. Full credit to the original writer of this sketch.

/* interrupt routine for Rotary Encoders

The average rotary encoder has three pins, seen from front: A C B
Clockwise rotation A(on)->B(on)->A(off)->B(off)
CounterCW rotation B(on)->A(on)->B(off)->A(off)

and may be a push switch with another two pins, pulled low at pin 8 in this case


// usually the rotary encoders three pins have the ground pin in the middle
enum PinAssignments {
encoderPinA = 2, // right (labeled DT on our decoder, yellow wire)
encoderPinB = 3, // left (labeled CLK on our decoder, green wire)
clearButton = 8 // switch (labeled SW on our decoder, orange wire)
// connect the +5v and gnd appropriately

volatile unsigned int encoderPos = 0; // a counter for the dial
unsigned int lastReportedPos = 1; // change management
static boolean rotating=false; // debounce management

// interrupt service routine vars
boolean A_set = false;
boolean B_set = false;

void setup() {

pinMode(encoderPinA, INPUT_PULLUP); // new method of enabling pullups
pinMode(encoderPinB, INPUT_PULLUP);
pinMode(clearButton, INPUT_PULLUP);
// turn on pullup resistors (old method)
//digitalWrite(encoderPinA, HIGH);
// digitalWrite(encoderPinB, HIGH);
// digitalWrite(clearButton, HIGH);

// encoder pin on interrupt 0 (pin 2)
attachInterrupt(0, doEncoderA, CHANGE);
// encoder pin on interrupt 1 (pin 3)
attachInterrupt(1, doEncoderB, CHANGE);

Serial.begin(9600); // output

// main loop, work is done by interrupt service routines, this one only prints stuff
void loop() {
rotating = true; // reset the debouncer

if (lastReportedPos != encoderPos) {
Serial.println(encoderPos, DEC);
lastReportedPos = encoderPos;
if (digitalRead(clearButton) == LOW ) {
encoderPos = 0;

// Interrupt on A changing state
void doEncoderA(){
// debounce
if ( rotating ) delay (1); // wait a little until the bouncing is done

// Test transition, did things really change?
if( digitalRead(encoderPinA) != A_set ) { // debounce once more
A_set = !A_set;

// adjust counter + if A leads B
if ( A_set && !B_set )
encoderPos += 1;

rotating = false; // no more debouncing until loop() hits again

// Interrupt on B changing state, same as A above
void doEncoderB(){
if ( rotating ) delay (1);
if( digitalRead(encoderPinB) != B_set ) {
B_set = !B_set;
// adjust counter - 1 if B leads A
if( B_set && !A_set )
encoderPos -= 1;

rotating = false;

Clockwise rotation gives an increasing value, anticlockwise rotation gives a decreasing value, from 0 the value becomes 65535 and the continues to decrease. Pressing the shaft in resets the value to 0.


Leave a Reply

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

You are commenting using your 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 )

Connecting to %s

%d bloggers like this: