# How to detect the direction of a rotary encoder?

I'm trying to detect the direction of rotation for an incremental rotary encoder. I'm supposed to get +1 for clockwise and -1 for counter-clockwise. However, I receive random values. Any help appreciated.

Following is the code.

``````const int encoderPinA = 7;
const int encoderPinB = 12;
int val;
int encoderPos = 0;
boolean encoderALast = LOW; // remember the previous pin state

void setup() {
pinMode(encoderPinA,INPUT);
pinMode(encoderPinB,INPUT);
Serial.begin(9600);
}

void loop() {
analogWrite(3,250); //powering motor
val = encoder_direction();
// receive the direction for clockwise 1 and for counter clock wise  -1
Serial.println(val); // print it in serial monitor
}

int encoder_direction() {
if ((encoderALast == HIGH) && (encoderA == LOW)) {
//encoderPos --;
encoderPos = -1;
} else {
//encoderPos ++;
encoderPos = 1;
}
return  encoderPos;
// return the value either 1 or -1
}
encoderALast = encoderA;
}
``````

``````// using digitalread() code  for four encoders
const int ENCODERS = 4; // the number of encoders
const int encoderPinA[ENCODERS] = { 7, 8, A1 , 2 };
// encoderA pins on 2 and 4
const int encoderPinB[ENCODERS] = { 12, 5, 4 , A2 };
// encoderB pins on 3 and 5
int encoderPos[ ENCODERS] = { 0, 0, 0, 0 };
// initialize the positions to 0
boolean encoderALast[ENCODERS] = { LOW, LOW, LOW, LOW };
// holds last state of encoderA pin

void setup() {
Serial.begin (9600);
}

int updatePosition(int encoderIndex) {
if ((encoderALast[encoderIndex] == HIGH) && (encoderA == LOW)) {
//encoderPos[encoderIndex]--;
encoderPos[encoderIndex] = -1;
} else {
//encoderPos[encoderIndex]++;
encoderPos[encoderIndex] = 1;
}
Serial.print("Encoder ");
Serial.print(encoderIndex+1,DEC);
Serial.print("=");
Serial.println (encoderPos[encoderIndex]);
}
encoderALast[encoderIndex] = encoderA;
}

void loop() {
updatePosition(0);
updatePosition(1);
updatePosition(2);
updatePosition(3);
}
``````

``````// using direct access to digitalread code in order to process faster
int val[4];
int encoderPos = 0;
boolean encoderALast[4] ={LOW, LOW, LOW, LOW};
// remember the previous pin state

void setup() {
Serial.begin(9600);
}

void loop() {
for (int ii=0; ii<4;ii++) {
encoder_direction(ii);
}
}
int encoder_direction(int m) {
boolean encoderA;
boolean encoderB;
if(m == 0) {
encoderA = (PIND & _BV (7)) == 0;
encoderB = (PINB & _BV (4)) == 0;
}
if (m == 1) {
encoderA = (PINB & _BV (0)) == 0;
encoderB = (PIND & _BV (5)) == 0;
}
if (m == 2) {
encoderA = (PINC & _BV (1)) == 0;
encoderB = (PIND & _BV (4)) == 0;
}
if (m == 3) {
encoderA = (PIND & _BV (2)) == 0;
encoderB = (PINC & _BV (2)) == 0;
}
if ((encoderALast[m] == HIGH) && (encoderA == LOW)) {
if (encoderB == LOW) {
//encoderPos --;
encoderPos = -1;
} else {
//encoderPos ++;
encoderPos = 1;
}
Serial.print(" Encoder ");
Serial.print(m+1);
Serial.print(" = ");
Serial.println(encoderPos);
}
encoderALast[m] = encoderA;
}
``````
• I don't think its possible using only 2 detector and detect only based on the order of activation. – dhimaspw Dec 23 '16 at 4:23
• when this is in the loop works fine. But, now I want to use function. The problem is I can not retrieve correct value from function's output. – zalt Dec 23 '16 at 4:48

I have a page about rotary encoders - in that I show how you can detect encoder changes (and direction) using one interrupt pin and another pin, like this:

``````// Wiring: Connect common pin of encoder to ground.
// Connect pin A (one of the outer ones) to a pin that can generate interrupts (eg. D2)
// Connect pin B (the other outer one) to another free pin (eg. D5)

volatile bool fired;
volatile bool up;

const byte encoderPinA = 2;
const byte encoderPinB = 5;

// Interrupt Service Routine for a change to encoder pin A
void isr ()
{
else
fired = true;
}  // end of isr

void setup ()
{
pinMode (encoderPinA, INPUT_PULLUP);     // enable pull-ups
pinMode (encoderPinB, INPUT_PULLUP);
attachInterrupt (digitalPinToInterrupt (encoderPinA), isr, CHANGE);   // interrupt 0 is pin 2

Serial.begin (115200);
}  // end of setup

void loop ()
{
static long rotaryCount = 0;

if (fired)
{
if (up)
rotaryCount++;
else
rotaryCount--;
fired = false;

Serial.print ("Count = ");
Serial.println (rotaryCount);
}  // end if fired

}  // end of loop
``````

In the main loop you just need to test the boolean `fired` to see if the encoder changed, and `up` to see if it went up (otherwise it went down).

I receive random of these values ...

I think with your code you need to detect a change - that is, if the encoder pin is now `HIGH` but was previously `LOW`.

# Solution for 4 encoders

The problem is I am working on four encoders ...

I have reworked my code to use pin-change interrupts instead of external interrupts.

``````// Wiring: Connect common pin of encoder to ground.

volatile bool fired;

const byte ENCODERS = 4;

typedef struct
{
int aPin;     // which Arduino pin for the "A" side
int bPin;     // which Arduino pin for the "B" side

// values below are calculated at run-time
volatile byte * aPort;  // which processor port the A side is plugged into
volatile byte * bPort;  // which processor port the B side is plugged into
byte aBitMask;   // which bit in the A port
byte bBitMask;   // which bit in the B port
byte whichInterrupt;  // which pin-change interrupt port (0, 1, 2)
byte oldValue;   // old value of A port (to see if it changed)
int count;       // current encoder counter
} encoder;

volatile encoder encoders [ENCODERS] =
{

// A   B  pins  (eg. D4 and D8, D5 and D9 and so on)

{ 4,  8 },
{ 5,  9 },
{ 6, 10 },
{ 7, 11 },

};  // end of encoders

void checkForPinChange (const byte which)
{
for (byte i = 0; i < ENCODERS; i++)
{
if (encoders [i].whichInterrupt == which)
{
byte newValue = *(encoders [i].aPort) & encoders [i].aBitMask;
if (newValue != encoders [i].oldValue)
{
bool up;
byte bPort = *(encoders [i].bPort) & encoders [i].bBitMask;
if (newValue)
up = bPort;
else
up = !bPort;
fired = true;
if (up)
encoders [i].count++;
else
encoders [i].count--;
encoders [i].oldValue = newValue;
}  // end of if value has changed
}   // end of if this is the right interrupt number
}     // end of for each encoder
} // end of checkForPinChange

// handle pin change interrupt for D8 to D13 here
ISR (PCINT0_vect)
{
checkForPinChange (PCIE0);
}  // end of PCINT0_vect

// handle pin change interrupt for A0 to A5 here
ISR (PCINT1_vect)
{
checkForPinChange (PCIE1);
}  // end of PCINT1_vect

// handle pin change interrupt for D0 to D7 here
ISR (PCINT2_vect)
{
checkForPinChange (PCIE2);
}  // end of PCINT2_vect

void setup ()
{
Serial.begin (115200);
Serial.println ("Starting ...");

PCIFR  |= bit (PCIF0) | bit (PCIF1) | bit (PCIF2);   // clear any outstanding interrupts

for (byte i = 0; i < ENCODERS; i++)
{
pinMode (encoders [i].aPin, INPUT_PULLUP);
pinMode (encoders [i].bPin, INPUT_PULLUP);
// convert pin number to port, mask, etc. for efficiency

// Input port
encoders [i].aPort = portInputRegister (digitalPinToPort (encoders [i].aPin));
encoders [i].bPort = portInputRegister (digitalPinToPort (encoders [i].bPin));

// Which bit in the port to test

// Which interrupt number (0, 1, 2)
encoders [i].whichInterrupt = digitalPinToPCICRbit (encoders [i].aPin);

// activate this pin-change interrupt bit (eg. PCMSK0, PCMSK1, PCMSK2)
volatile byte * ICRmaskPort = digitalPinToPCMSK (encoders [i].aPin);
*ICRmaskPort  |= bit (digitalPinToPCMSKbit (encoders [i].aPin));

// enable this pin-change interrupt
PCICR |= bit (digitalPinToPCICRbit (encoders [i].aPin));

} // end of for each encoder

}  // end of setup

void loop ()
{
if (fired)
{
for (byte i = 0; i < ENCODERS; i++)
{
char buf [10];
sprintf (buf, "%5d", encoders [i].count);
Serial.print (buf);
}
Serial.println ();
fired = false;
}  // end if fired

}  // end of loop
``````

The code is general enough to handle 1 to x encoders (eg. 10 encoders) - within reason! You need to have 2 pins spare per encoder (the third pin goes to ground). One of the pins needs to be capable of pin-change interrupts (on the Uno that is all of them).

The code sets up to detect an interrupt on the "A" side of the encoder and tests the value on the "B" side to see if it was turned clockwise or counter-clockwise.

I am getting results like this (with 2 encoders connected):

``````   16  -19    1    1
17  -19    1    1
18  -19    1    1
19  -19    1    1
20  -19    1    1
21  -19    1    1
22  -19    1    1
23  -19    1    1
23  -20    1    1
23  -21    1    1
23  -22    1    1
23  -23    1    1
23  -24    1    1
23  -25    1    1
``````

The other two are showing as "1" (that is, not turned yet).

• I see. The problem is I am working on four encoders, Therefore, I trying to realize by status change detecting. – zalt Dec 23 '16 at 7:06
• It's always helpful to give as much detail as you can in the question. Then we don't have to guess, and we don't give an answer which doesn't suit your situation. – Nick Gammon Dec 23 '16 at 7:43
• And if I may say, your posted code in the question doesn't have four encoders in it. – Nick Gammon Dec 23 '16 at 7:43
• Nick, I think it would make sense to update your code to say `pinMode(encoderPinA, INPUT_PULLUP);` instead of `digitalWrite (encoderPinA, HIGH); // enable pull-ups` – James Waldby - jwpat7 Dec 23 '16 at 7:46
• ok, I got to different code for four encoders. – zalt Dec 23 '16 at 7:48

Using polling to read multiple encoders may work if you program quite carefully and if you dedicate the processor just to reading encoders; but generally, interrupt-driven encoder processing has a higher likelihood of working ok. The example below uses pin-change interrupts to read from three rotary encoders that are attached to PD2...PD7, ie, six consecutive bits of port D. It can be extended to more encoders by adding another pin-change ISR to handle another interrupt vector, or by using PD0 and PD1 as encoder inputs. (However, PD0 and PD1 are RX and TX on the Uno I'm testing with, and I'm using Serial for test output.)

Example: rotors3.ino

``````/*  Rotors3 -- JW, December 2016 -- Uses 4-state state-machine
*  implementation of rotary encoding for KY-040 rotary knobs.  The
*  state-machine picture in a Feb 4, 2014 7:40 PM post at
*  https://e2e.ti.com/support/microcontrollers/hercules/f/312/t/318762
*  by Anthony Seely shows counts increasing on transitions 10 -> 11
*  -> 01 -> 00 -> 10 and decreasing on transitions the other way.
*  The ISR in this program decodes those transitions using logical
*  operations on variables `changes` and `wayflag`.  Transitions
*  between 00 and 11 or 10 and 01 are invalid and ignored.
*/

volatile byte abOld;        // Old state
volatile byte changedCount; // Change-indication
volatile int count[3];      // current rotary count

void setupRotors3(byte ioMode, byte enablePCI) {
pinMode( 2, ioMode); //  2, PD2   PCINT 18
pinMode( 3, ioMode); //  3, PD3   PCINT 19
pinMode( 4, ioMode); //  4, PD4   PCINT 20
pinMode( 5, ioMode); //  5, PD5   PCINT 21
pinMode( 6, ioMode); //  6, PD6   PCINT 22
pinMode( 7, ioMode); //  7, PD7   PCINT 23
if (enablePCI) {
PCMSK2 |= 0xfc; // Set mask for bits of interest
PCIFR  |= 0x04; // Clear PC interrupts if any
PCICR  |= 0x04; // Enable PC interrupts
}
abOld = changedCount = count[0] = count[1] = count[2] = 0;
}

void setup() {
setupRotors3(INPUT_PULLUP, true);
Serial.begin(115200);
Serial.println("Starting Rotary Encoder Test");
}

// counts.  This code depends on encoder data appearing like ddccbbaa,
// ie data is in properly paired consecutive port bits.
ISR(PCINT2_vect) {
byte abNew = PIND & 0xfc; // Read pins  2, 3, 4, 5, 6, 7,
byte changes = abNew^abOld;   // Use XOR to flag changed bits
char wayflag = 2*abNew^abNew; // Use XOR to get state-bits
byte test2;
for (byte i=0; i<3; ++i) {
// For PD2-7, low 2 bits aren't relevant.  Move the shifts to
// end of for loop if low 2 bits carry encoder info.
wayflag >>= 2;      // Discard low state-bits
changes >>= 2;      // Discard low change-bits
test2 = changes & 3;    // Get 2 bits of interest
if (test2==1) {
count[i] += 1-(wayflag&2);
changedCount = true;
} else
if (test2==2) {
count[i] -= 1-(wayflag&2);
changedCount = true;
}
}
abOld = abNew;        // Save new state
}

void loop() {
if (changedCount) {
changedCount = false;   // Race condition can be ignored
for (byte i=0; i<3; ++i) {
Serial.print(count[i]);   // Announce counts
Serial.print("  ");
}
Serial.println(millis());   // Announce change-time
}
}
``````

Note, `setupRotors3()` and a few other statements in the program were generated using the ISR-framework-generating Sketch from an answer to an earlier question. If you need to use other input pins than those shown in the program above, you can use the framework generator to produce a setup routine and port-I/O statements for them.

Following is some output from the program as above, from when I turn two of the three 60-counts-per-turn encoders as fast as I can by hand, for half a turn each at the same time (about 4 RPS, or 240 RPM).

``````-2  1  0  1127
-2  2  0  1133
-2  3  0  1148
-2  4  0  1155
-3  4  0  1165
-3  5  0  1166
-4  5  0  1169
-4  6  0  1170
-5  6  0  1174
-5  7  0  1177
-6  7  0  1178
-6  8  0  1181
-7  8  0  1184
-7  9  0  1187
-8  9  0  1189
-8  10  0  1190
-9  10  0  1196
-9  11  0  1197
-10  12  0  1200
-10  12  0  1201
-11  12  0  1205
-11  13  0  1207
-12  13  0  1210
-12  14  0  1211
-12  15  0  1215
-13  15  0  1216
-13  16  0  1220
-14  16  0  1221
-15  16  0  1224
-15  17  0  1226
-16  17  0  1229
-16  18  0  1230
-17  18  0  1234
-17  19  0  1235
-18  19  0  1239
-18  20  0  1241
-19  20  0  1243
-19  21  0  1247
-20  21  0  1248
-21  21  0  1251
-21  22  0  1252
-22  22  0  1255
-22  23  0  1258
-23  23  0  1259
-24  23  0  1263
-24  24  0  1264
-25  24  0  1266
-25  25  0  1269
-26  25  0  1271
-27  25  0  1275
-27  26  0  1275
-28  26  0  1280
-28  27  0  1281
-29  27  0  1284
-29  28  0  1288
-30  28  0  1291
-31  28  0  1301
-31  29  0  1341
-32  29  0  1346
-32  30  0  1385
-33  30  0  1389
``````
• Looks like we had similar ideas. I didn't spot your answer until I had rewritten my code. :) We even have similar debugging output. – Nick Gammon Dec 24 '16 at 1:30