Improved Arduino Rotary Encoder Reading
Rotary encoders are great input devices for electronics projects – hopefully this Instructable will inspire and help you use one in your next project.
Why write rotary encoder code?
I wanted to use a low cost rotary encoder as an input mechanism for one of my upcoming projects and was initially bewildered by the code options available to take readings from the rotary encoder and determine how many “detents” or cycles the encoder had clicked past and in what direction. I think my main sketch will need to use most of my Arduino’s memory so I am avoiding the various available encoder libraries, which seemed to be difficult to make work when I tried a couple of them. They also appear to use far more of the code budget than the sketch-based code approaches discussed from here on.
If you just want to bypass the thinking behind my approach and get straight into the Instructable, feel free to skip ahead to Step 1!
Several of the main sketch-based (i.e. they don’t use a library) approaches are discussed in rt’s blog post where they write rotary encoder code that makes the cheapest encoders usable as Arduino inputs. They also have a good example of they logic signal that the encoder produces. rt found that a timer interrupt system worked best for them but I’m concerned that the polling frequency would detract from screen update speed in the main loop of my project sketch. Given that the rotary encoder will be moving for a tiny proportion of the time I want the screen to be updating, this seems a poor match for my application.
I chose to start off using Steve Spence’s code here, which was fine on it’s own but appeared to really slow down when I incorporated the rest of my sketch code (which involves writing display updates to a small TFT screen). Initially I wondered if it could be because the main loop contains a debounce statement.
I then read Oleg’s rotary encoder article on an interrupt service routine version of his previous post, I also thought it might be a good idea to use direct port manipulation to read both pins simultaneously and as soon as the interrupt fires. His code can be used on any input pin if the port manipulation code is rewritten. In contrast, I decided to use only the hardware interrupts on digital pins 2 and 3, so we can set interrupts to only fire on a rising edge of the pin voltage, rather than on pin voltage change, which includes falling edges. This reduces the number of times the ISR is called, distracting from the main loop.
Oleg’s code uses a lookup table to reduce compiled code size to a really small size but I couldn’t get reliable results which would catch very slow rotation as well as reasonably fast rotation. Bear in mind that hardware debouncing (see Step 2) can help a lot with reliability of readings but I was after a software solution to simplify the hardware build and be as portable to other hardware applications as possible.
This concludes the introduction of my challenge and considerations. In Step 2 we’ll take a look at the encoder hardware, terminology and some practical considerations when you want to integrate a rotary encoder into your project.
Step 1: A Bit About Rotary Encoders
Why are rotary encoders so cool?
- Unlike a variable resistor/potentiometer they have infinite travel in any direction and because they produce a digital “Gray code” you can scale their readings to whatever range you like.
- The dual direction makes them useful for increasing or decreasing a value within a variable or navigating menus.
- Finally, many of these rotary encoders come with a centre push button, which can be used to select menu items, reset a counter or do anything you can think of that might suit a momentary push button.
- PPR: pulses per rotation – typically 12, 20 or 24. You might also see specifications for maximum rotation in rpm etc. This is probably determined by the encoder’s propensity to “bounce” contacts – see below.
Read more: Improved Arduino Rotary Encoder Reading
This Post / Project can also be found using search terms:
- esp32 read rotary encoder