The encoder we have used is the incremental encoder. This rotary encoder is also known as relative or quadrature rotary encoder and the output of this encoder is series of square wave pulses. Working of Rotary Encoder. The rotary encoder generates two square waves which have a phase difference of 90 degrees. Low resolution rotary encoders used for dials or knobs turned only by human fingers are good candidates for low performance polling mode. High resolution encoders attached to motors usually require interrupts! With Arduino Uno, Duemilanove or Mega, Serial.print can cause trouble.
Arduino library for reading rotary encoders that output a 2-bit gray code.
This is a repackaged version of Ben Buxton's excellent rotary library organized for the Arduino 1.x IDE, keyword highlighting, polling example, Arduino library capitalization conventions.
Features
- Debounce handling with support for high rotation speeds
- Correctly handles direction changes mid-step
- Checks for valid state changes for more robust counting / noise immunity
- Interrupt based or polling in loop()
- Counts full-steps (default) or half-steps
Installation
- Download and unzip to ArduinolibrariesRotary. So for example rotary.h will be in ArduinolibrariesRotaryrotary.h.
- Restart Arduino IDE
- File -> Examples -> Rotary
Background
A typical mechanical rotary encoder emits a two bit gray code on 3 output pins. Every step in the output (often accompanied by a physical 'click') generates a specific sequence of output codes on the pins.
There are 3 pins used for the rotary encoding - one common and two 'bit' pins.
The following is the typical sequence of code on the output when moving from one step to the next:
From this table, we can see that when moving from one 'click' to the next, there are 4 changes in the output code.
- From an initial 0 - 0, Bit1 goes high, Bit0 stays low.
- Then both bits are high, halfway through the step.
- Then Bit1 goes low, but Bit2 stays high.
- Finally at the end of the step, both bits return to 0.
Detecting the direction is easy - the table simply goes in the other direction (read up instead of down).
To decode this, we use a simple state machine. Every time the output code changes, it follows state, until finally a full steps worth of code is received (in the correct order). At the final 0-0, it returns a value indicating a step in one direction or the other.
It's also possible to use 'half-step' mode. This just emits an event at both the 0-0 and 1-1 positions. This might be useful for some encoders where you want to detect all positions. In rotary.h, uncomment
#define HALF_STEP
to enable half-step mode.If an invalid state happens (for example we go from '0-1' straight to '1-0'), the state machine resets to the start until 0-0 and the next valid codes occur.
The biggest advantage of using a state machine over other algorithms is that this has inherent debounce built in. Other algorithms emit spurious output with switch bounce, but this one will simply flip between sub-states until the bounce settles, then continue along the state machine. A side effect of debounce is that fast rotations can cause steps to be skipped. By not requiring debounce, fast rotations can be accurately measured. Another advantage is the ability to properly handle bad state, such as due to EMI, etc. It is also a lot simpler than others - a static state table and less than 10 lines of logic.
License
GNU GPL Version 3
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!
Other Approaches
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.