Reading rotary encoder on Arduino

By Oleg Mazurov

Rotary encoder connected to Arduino

Quadrature rotary encoders, also known as rotary pulse generators, are popular input devices for embedded platforms, including Arduino. Several rotary encoder code examples are posted on Arduino site and elsewhere, however, they treat encoder as a pair of switches, adding decoding/debouncing overhead. For many years, I used an algorithm based on the fact that quadrature encoder is a Gray code generator and if treated as such, can be read reliably in 3 straight step without need for debouncing. As a result, the code I’m using is very fast and simple, works very well with cheap low-quality encoders, but is somewhat cryptic and difficult to understand. Soon after posting one of my projects where I used rotary encoder to set motor speed i started receiving e-mails asking to explain the code. This article is a summary of my replies – I’m presenting small example written for the purpose of illustrating my method. I’m also going through the code highlighting important parts.

The hardware setup can be seen on title picture. The encoder from Sparkfun is connected to a vintage Atmega168-based Arduino Pro. Common pin of the encoder is connected to ground, pins A and B are connected to pins 14 and 15, AKA Analog pins 0 and 1, configured as digital inputs. We also need a serial connection to the PC to receive power, program the Arduino, and send program output to the terminal. For this purpose, Sparkfun FTDI basic breakout is used.

Connecting encoder pins to pins 0 and 1 of 8-bit MCU port makes encoder reading code very simple. If analog pins are needed for something else, it is possible to move encoder to digital pins 8,9 or 0,1 (losing serial port) with no modification of code logic. While technically using any two consecutive port pins is possible with a bit of tweaking, using non-consecutive pins for encoder input with this method is not recommended. Lastly, it sometimes hard to determine which encoder pin is A and which is B; it is easier to connect them at random and if direction is wrong, swap the pins.

Example code is posted below. It is complete sketch – you can copy it from this page and paste into Arduino IDE window, compile, upload and run. The result of rotating the encoder can be seen in terminal window. Note that serial speed in the sketch is set to 115200, you will need to set your PC terminal to that speed as well. The explanation of the code is given after the listing.

First three #defines set names for port pins. This way, if you want to use different pins for your encoder, you can do it here without sifting through code looking for pin names. If you’d like to move encoder pins to Arduino pins 8 and 9, ENC_PORT shall be defined as PINB, and for pins 0,1 as PIND.

In setup() function hardware is initialized. pinMode sets the pin as input so we can read it and digitalWrite turns on pull-up resistor on the pin so it won’t dangle in the air when switch is open. Finally, the serial port is initialized and message is printed as an indicator that port is working – if you can’t see “Start” on your terminal screen, check PC port settings.

Let’s go down to line 30, where read_encoder() function starts. enc_states[] array is a look-up table; it is pre-filled with encoder states, with “-1” or “1” being valid states and “0” being invalid. We know that there can be only two valid combination of previous and current readings of the encoder – one for the step in a clockwise direction, another one for counterclockwise. Anything else, whether it’s encoder that didn’t move between reads or an incorrect combination due to switch bouncing, is reported as zero.

Note that old_AB is declared static. This means that it’s value will be retained between function calls therefore previous encoder reading will be preserved. When function is called, old_AB gets shifted left two times (line 36) saving previous reading and setting two lower bits to “0” so the current reading can be correctly ORed here. Then ENC_PORT & 0x03 reads the port to which encoder is connected and sets all but two lower bits to zero so when you OR it with old_AB bits 2-7 would stay intact. Then it gets ORed with old_AB (line 37, old_AB |= ( ENC_PORT & 0x03 )). At this point, we have previous reading of encoder pins in bits 2,3 of old_AB, current readings in bits 0,1, and together they form index of (AKA pointer to) enc_states[] array element containing current state – either increment, decrement, or no change. All that is left to do is return this state to the calling function, and line 38 does just that. Upper half of old_AB gets zeroed by old_AB & 0x0f – if we don’t do this, we will be reading memory past enc_states[] array.

The above paragraph was pretty long explanation for mere 5 lines of code (counting two declarations); if you are reading this, 98% of work is done. The rest of the code is very easy. We are now moving up to line 17, where loop() function is residing. counter variable is the one which is modified by encoder and tmpdata is used to hold the reading. On line 22, encoder is read. If encoder state has changed, i.e., read_encoder() returned non-zero, current value of counter variable is printed and then counter is updated with tmpdata. This is done within conditional statement on lines 22-27. The loop then ends and starts again.

Now let’s briefly talk about real-life applications of this method. If you’re using Sparkfun encoder, you’ll notice that it gives 4 increments per click and it’s impossible to make it hold position between clicks. Therefore, to count clicks you will need to divide counter variable by 4. Also, if counter larger than 255 is necessary, the declaration for it would have to be changed to uint16_t or longer. Another nice modification, left out to keep code simple, would be to move enc_states[] array to program memory using PROGMEM type saving several bytes of precious RAM.

Rotary encoder singing out of tune

In order for this method to work well, read_encoder() function needs to be called fairly often . To see what happens when loop is slow, lower serial port speed – go to line 13, change “115200” to “9600”, recompile the sketch and run (don’t forget to reconfigure the terminal on PC side). Picture on the left shows the result. Note that encoder goes down from 237 to 229, then jumps up to 230, then continues going down. Sometimes it counts correctly but gives 2 or 3 states per click instead of 4. Instabilities like this are good indication of slow loop. If encoder is used to things like setting motor speed or sound volume, this behavior won’t do much harm because encoder will never skip back more than one state and overall counting would still be in the right direction. However, if encoder is used for interfacing with display, glitches like that are very annoying. Therefore, for user interface applications, loop time should be kept short and in some cases it may be even necessary to convert encoder reading function into interrupt service routine. Given the size and speed of encoder read function, such conversion can be done without much difficulty.

To summarize: I presented a method to convert quadrature rotary encoder output into direction information using look-up table. The method makes debouncing unnecessary, and as a result, conversion function implemented with this method is very short, fast, and works very well on small 8-bit MCU platforms, such as Arduino.

241 comments to Reading rotary encoder on Arduino

Ok thanks a lot – that is really helpful. But as soon as I integrated this code with some more complex code I have using pulseIn() function to understand the input of a standard RC receiver, and a motor driver controlling two motors, the encoder seems totally incorrect: When I had the the same functions working on the encoder, I was getting super clean values. But when I had more code and the encoder stuff running side by side, my counter values are totally off (see below, when spinning the encoder round and round) . So it seems like the timing is totally not working –
I’m wondering what I might be doing wrong because nothing too “high level” is happening in my code, and I took out all my “println”s.

I can attach my code if that’d be helpful, but was wondering your thoughts / experience (re using 2 encoders with some other processes going on in Arduino UNO)
Thank you so so much again for all the help and explanation.

Blocking is when a piece of code is waiting for some event to happen, interrupting the flow of execution of the main loop. For example, delay() is blocking; the program won’t continue until time specified as delay has expired. If you have a delay of one second in your main loop, you can rotate your encoder full circle and miss every pulse.

Fixing means finding blocking code and writing it differently. What does ‘20000’ mean in pulseIn?

Ok I guess I have to use an interrupt? I wanted to be able to use Arduino UNO with the two encoders. Would there be a way to use two encoders with Arduino UNO , even if the two default interupt pins are 2 and 3?
the third parameter (20000) is the time out in microseconds.

just fyi, PulseIN() : Reads a pulse (either HIGH or LOW) on a pin. For example, if value is HIGH, pulseIn() waits for the pin to go HIGH, starts timing, then waits for the pin to go LOW and stops timing. Returns the length of the pulse in microseconds. Gives up and returns 0 if no pulse starts within a specified time out. so it seems the problem is it “delays” and waits (ie blocking)

I really appreciate your tutorials, they are great and am wondering if you could put me in the right direction as I am building a bi-directional people sensor and counter in a room using two infrared sensors (S1 and S2).What I want is the first sequence on breaking S1 and S2 the change should signal entry and increment a counter whilst breaking the beam in the second sequence S2 first and S1 will signal exit and decrement a counter and display the count value on a 3 digit 7 segment display.
I have thought of using the idea of reading two rotary encoder using external interrupts on an arduino. Is this the right way to go??

This is the far have come so far and am serial printing the counts. Any idea how I could hold old value and update this old value with the current val count to a 3 digit seven segment display? depending on how the sensors are tripped? Any correction or suggestions am very willing to learn

I managed to do my 2 ISR for the two sensors and the 3 digit seven segment display, however the only problem have got is when my sensors are broken at first the count goes negative for 2 or 3 cycles then comes to positive(normal) does anyone have an idea how to correct this??please. I have declared static int i in loop.

Hi, thanks for the great tutorial. I adapted your code (with minor changes) to run on an STM32F100B (ARM) micro. Works flawlessly with my encoder without the need for any hardware debouncing circuitry!

Hi Mr. Oleg,
Good day to you and thanks for the great effort helping a rookie like me to start with the arduino, I have tried your code using Uno and it works flawlessly but when i tried it using the Leonardo, it didn’t work, wandering what could be the problem.

The debounce did not work for me but instead I used the switch which is perfect. The only output shown in the Serial Monitor is when the switch is being pressed but not the encoder, I am using an encoder with a switch.

Ok, I get it and I like it a lot. The only thing that I cannot figure out is how to keep it from going from 255 to 0 or from 0 to 255. Is there a way that I can stop it from overflowing the register and just park it at max or min until the user decreases the value?

It all depends on the total of tasks being performed by the Arduino, and the number of
pulses received by the processor.

Say for example you have a small slow moving xy table, with 2 rotary US Digital encoders generating say 4000 pulses per second and driving two stepper motors with an “Easydriver” board you would soon be running out of head room on your project.

My suggestions would be to use, two Tiny13 chips from Atmel to encode the rotary encoders, and than feed the resultant signal into your Arduino, where it will give an excellent performance. In my humble opnion it may even support three channels, XY and Z.

You would need some machine code experience to do this, but this would be worth learning.

Thank you for this great useful article… I’m wondering if I can use you idea with Arduino Uno?? I mean to use the rotary encoder with Arduino Uno in order to select the power of my RC car which I’m building…

Hi,
Sweet bit of code! I am using it in a project at work, using the encoder to display a motor speed selection on a seven segment display and it works very nicely indeed. Once you remove the serial.print commands it is difficult to spin the bare encoder shaft quickly enough to generate a misread. With a suitably sized knob on the shaft it is going to slow rotation speed and make it virtually impossible for the casual operator to get an erroneous reading… 😉

Hi, very nice job
my R2D2 Dome’s encoder sensor has 3 output, one of them reads just one special dent on the motor gear, like a ZERO POSITION.
How can I implement it in the code?
Also How can I make encoders count to 36 or +18 to -18?
Thanks just learning here

I am new to Arduinos and am starting to play with them. I have seen many examples of rotary encoder code. But non that use the Leonardo to output key press commands when rotated. My application is a small CNC vertical machining center. The control itself can not accept a rotary encoder (manual pulse generator) The control is PC based. It CAN accept key presses, for example, to move the table in these directions:
X+ Ctrl + Right Arrow Key
X- Ctrl + Left Arrow Key
Y+ Ctrl + Up Arrow Key
Y- Ctrl + Down Arrow Key
Z+ Ctrl + Page Up Key
Z- Ctrl + Page Down Key
A+ Ctrl + Plus (+) Key
A- Ctrl + Minus (-) Key

I can make it work with out a Ctrl key press, but better to have the combination if possible. I was thinking of using a 4 position rotary switch. Each position would be for each axis, X,Y,Z and A. When the switch is in the proper position, then depending on which direction the MPG handwheel was turned, Leonardo would send the appropriate keyboard character via the PC’s USB port. One keypress for each pulse of the MPG hand wheel. It is a 100PPR hand wheel.

Is this doable and might someone be able to help cut my learning curve down with an example of the code? Thanks!

You mention this:
“… Sparkfun encoder, you’ll notice that it gives 4 increments per click and it’s impossible to make it hold position between clicks. Therefore, to count clicks you will need to divide counter variable by 4”

My Quadrature encoders are giving 2 counts per click. Would you mind a quick lesson on just how to do this, since the variable isn’t a decimal number?

Many thanks! Your code is forcing me to “dig in” and learn more than just the “simple” programming, as given on the Arduino site examples… which is what “teaching” is all about!

If your encoder clicks every second pulse you can determine a click position without dividing. Even increments will have zero in bit 0 and odd increments will have one. This is true for both signed and unsigned representations. Also, checking single bit, like if( count & 0x01 ) { ..., could be faster than shifting.

I found HID board using PIC18F4550 chip capable of reading 16 rotary encoders (all types of rotary) in real-time, and no additional chip. anyone knows how to do that in arduino. what method is it implementing, timer or interrupt?

It should work then, but it doesn’t. Maybe your testing needs to be changed? Include the Arduino itself. For example, make sure you can read a singe pin. Connect a pushbutton to a data pin and write a sketch which reads the state of this pin and prints it out. There are many examples on the net doing similar things. Then move the button to one of the pins intended for the encoder and make it work there, then move to the other pin. Then connect the encoder, it’s really a pair of pushbuttons with some clever sequencer. Then modify my sketch with what you have learned – it will work.

I adapted this code to a PIC chip project I was/am working on many months ago. It made my rotary knob functionality perfectly clean for the first time. The code was more compact as well. I never did mention it, but this was really helpful to me. It works very very well on a rotary encoder with absolutely no hardware debouncing. The signal is really ugly.

Just wanted to say thanks. I’ve tried lots of debounce code. This is not only the best I’ve found, but the best by far.

Hi Oleg.
What an elegant algorithm; read_encoder is a very nice way to implement the state machine.
Thanks for publishing and sharing your ideas.
Very simple port to the Freescale platform on ARM”s MBED.
Worked very well but I did need 100nF of anti-bounce on my Panasonic EVE-KE2F2024B encoder, perhaps because this CPU is clocked so much faster than the ATMEGA and AVRs.

I am attempting to connect 6 encoders to an Arduino Leonardo with a MCP23017 I2c Port Expander. I have been able to use one encoder on the Arduino directly. What I am attempting to do is depending if the encoder is turned up or down to send a keyboard scan key to to the computer for a project. I was hoping to have some type of multiplexing to be able to read each encoder and know if they have been turned. The MCP23017 has not helped. I know it works because I have been able to use the example codes and blink an led and see button press input.

So my question is how can I multiplex 6 rotary encoders. Either using the MCP 23017 or a different input expander of some type.

My project is to control the speed of the dc motor(Hardware) using PID controller in LabVIEW. For interfacing Labview with dc motor, I am using Arduino Uno. For encoder, I am using Optocoupler and a schmitt trigger. The encoder has three terminals. Red brown & black. red and black are for +5v and ground respectively and brown will read the value (i.e no.of rotations) from the dc motor. This output should be connected to arduino which in turn will pass this value to labview. Please tell me on which pin of arduino I should connect that brown wire.

From digital pin 3 of arduino, I am passing PWM signal to the dc motor. I tried to connect that brown wire of encoder to pin 5 but I am getting an unknown error.

I am new at Arduino (Uno) and writing code so this is a good example for me. Thanks Oleg!
I have a bag of encoders that seem to work different. I put some LEDs on the terminals to see how they work. When turning clockwise both outputs become high similtanious, then one goes low and then the other goes low. When turning counter/anti clockwise the first one becomes high, then the second one and then they both get low at the same time. Like this:
010 010 010
011 011 011

Is there an easy way I can change this code so my rotary encoders will work? I hope you can help me.

I’m not sure about easy way. The fundamental property of Grey code encoder is that only one output is changing at a time so that when both has changed simultaneously you would know that the outputs are wrong somehow. You can change the lookup table to give proper increment/decrement for your sequence but then you’d have to add a debounce.

Hi, Oleg.
I connect my encoder to pins 10 and 11 on my Uno board, and code did notwork, even i define ENC_A and ENC_B to those pins.
How to implement this code to pins 10 and 11.
I feel i should change left shift operator and enc_states pattern. But how?

Hi Oleg,
I am using your code on a Arduino mega 2560 board. I have a rotary encoder(incremental) 1024 p/r hooked up using pins 36 & 37. Theverything seems to work okay but the counter will only go up to 255 and then it starts back at 0 after not even a quarter turn. Is there a way to change the code so I can read a full revolution of the encoder (1024)
Thanks
Bill

Hi Oleg,
Thanks for getting back to me. I did change the counter size too uint16_t. In 1 CW rotation the counter showed 926. I reset the board and tried another CW rotation and the counter showed 680 If I then turn CCW the counter will go back to 0 then display 65535, the max number for uint16_t.
I tried again using int8_t and I am getting weird readouts in the serial monitor. I turned CW from 0 and at 127 the next number was -128 as shown below.
Counter value: 116
Counter value: 117
Counter value: 118
Counter value: 119
Counter value: 120
Counter value: 121
Counter value: 122
Counter value: 123
Counter value: 124
Counter value: 125
Counter value: 126
Counter value: 127
Counter value: -128
Counter value: -127
Counter value: -126
Counter value: -125
Counter value: -124
Counter value: -123
Counter value: -122
Counter value: -121
Counter value: -120
Counter value: -119
Counter value: -118
Counter value: -117
Counter value: -116
Counter value: -115
Counter value: -114
Counter value: -113
Counter value: -112
Counter value: -111
Counter value: -110
Counter value: -109
Counter value: -108
I don’t know what to think here. I am running a arduino mega 2560 board with a dfRobot lcd keypad shield mounted to it. i have a yumo E6B2-CWZ3E Rotor Encoder(Incremental) 1024-P/R. I have the A-B wires plugged into 36 -37 of the mega board, the 5v lead to the 5v connection on the board and the common lead hooked up top ground. I want to be able to have the counter display The total number of pulses for 1 CW rotation 1024? and when rotated CCW 1 rotation the counter should display -1024. or what ever the total number of pulses for 1 rotation. The shaft would only be turned by hand. Is this possible with this code? I very new to arduino and your comments are greatly appreciated.
Bill

You are mixing ‘int_n*’ and ‘uint_n’. ‘u’ means unsigned, it can’t hold negative values. Also, if you kept initialization of ‘counter’ as in the example, you would only get ‘1’ or ‘65535’ on the first reading. If you’re getting random values after reset check if ‘counter’ was initialized.

Hi Oleg!
I am still pretty new to arduino and i have a problem adapting your code. is it possible to increase the value by 0.1?

I tried to modify the values in the array (see outcommented line below)but it didnt work…

or is it possible to get the 1/10th steps increasing/decreasing value by a simple division by 10. but i got no clue where to place this division.
or do i have to use your code in a subfunction to use the value set by the rotary encoder in a second program part that will substract values from the value set in the first part.

hope you can help me to get me on my way…
thanks a lot!
best regards
martin

I am experimenting with some Atmel microcontrollers that have very small amounts of SRAM, such as the ATtiny104. That micro has only 32 bytes of SRAM, so I wanted to try implementing your algorithm without using that array of 16 unsigned chars to hold the +1, 0, and -1 values.

I thought maybe I could do it with less memory, and it turns out that I can. Since there are only three possible return values, one easy way is to just make two integer constants and access them to decide what to return. The compiler (in my case, GCC in Atmel Studio) will generate code that puts these values in registers so they don’t need to use SRAM for the table. It will also inline the function and put the return value in a register, and the locals too, so it uses, basically, no SRAM at all except one byte for the history!

Initially, I tried to make one 32-bit constant and access it two bits at a time, but GCC appeared to generate incorrect code for that version.

Defining binary constants is a GCC extension. For another compiler they could be turned into hex, 0x4182 and 0x6996.

Other encoding schemes are possible and would use less storage; for example, I could have used run-length encoding allowing variable-width bit fields, which would make the table 24 bits wide, but make it so the table had to be scanned from bit zero each time. Or it is possible to encode compactly using fractional bits, but for this tiny amount of data that also seems like overkill.

Anyway, I thought you or your readers might find this amusing and maybe useful, as a way to do the same algorithm in even less memory…

I am experimenting with some Atmel microcontrollers that have very small amounts of SRAM, such as the ATtiny104. That micro has only 32 bytes of SRAM, so I wanted to try implementing your algorithm without using that array of 16 unsigned chars to hold the +1, 0, and -1 values.

I thought maybe I could do it with less memory, and it turns out that I can. Since there are only three possible return values, one easy way is to just make two integer constants and access them to decide what to return. The compiler (in my case, GCC in Atmel Studio) will generate code that puts these values in registers so they don’t need to use SRAM for the table. It will also inline the function and put the return value in a register, and the locals too, so it uses, basically, no SRAM at all except one byte for the history!

Initially, I tried to make one 32-bit constant and access it two bits at a time, but GCC appeared to generate incorrect code for that version.

Defining binary constants is a GCC extension. For another compiler they could be turned into hex, 0×4182 and 0×6996.