Rotary Encoder
A Rotary Encoder is a device that detects angular position or rotation.
Usage
There are two types of rotary encoders: Absolute and Incremental. Absolute rotary encoders will tell you the exact position of the shaft while an incremental rotary can only tell you when the shaft has rotated.
Absolute
Absolute rotary encoders can tell us the exact position of the shaft. This is done by encoding the position in a series of rings that are rotated over a series of contacts. Depending on the precision of the encoder, there may be 3 or more bits in use.
Incremental
Incremental rotary encoders can tell us how many increments the shaft has rotated, but unlike an absolute rotary encoder, it is not possible to determine the position of the shaft.
These encoders have two rings and two contacts. The encoding on both rings are offset slightly such that one is slightly ahead of the other such that when the shaft is rotated, one of the contacts will make contact before the other. These two pins are sometimes labeled as DT and CLK and are essentially interchangable (though the direction sensed would be reversed). The mode of operation is that as the shaft is rotated clockwise, pin A will go high before pin B. As the shaft continues rotating, pin A will then go low before pin B. When the shaft is rotated counter clockwise, the reverse happens. Based on this property, microcontrollers can determine the direction and speed of the shaft.
Arduino Usage
These rotary encoders when used naively seems to result in jitter where it may for example sense correctly a clockwise rotation but incorrectly senses an counterclockwise step midway. This is mitigated by using a state machine so that any unreasonable inputs are ignored. The MD_REncoder library or the Arduino does just that and can be found at: https://github.com/MajicDesigns/MD_REncoder
For a more responsive experience, you may wish to use interrupts rather than polling such as with the case of the MD_REncoder library. This can be done by tying both A and B inputs from the rotary encoder to an interrupt handler. Here is some code I hacked out really late one night. I had to add the direction check in order to mitigate the jittering issue (it isn't perfect but is sufficient).
const uint8_t RE_A_PIN = 3; // dt
const uint8_t RE_B_PIN = 4; // clk
int re_counter = 0;
int re_last_clk;
int re_last_dir = 0;
void input_setup(void) {
pinMode(RE_PIN, INPUT_PULLUP);
pinMode(RE_A_PIN, INPUT_PULLUP);
pinMode(RE_B_PIN, INPUT_PULLUP);
// see https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/
attachInterrupt(digitalPinToInterrupt(RE_A_PIN), rotary_isr, CHANGE);
attachInterrupt(digitalPinToInterrupt(RE_B_PIN), rotary_isr, CHANGE);
}
void rotary_isr() {
cli(); // stop interrupts
int re_a = digitalRead(RE_A_PIN);
int re_b = digitalRead(RE_B_PIN);
if (re_a && ! re_b) {
re_counter--;
}
if (re_a && re_b) {
re_counter++;
}
// avoid random wrong direction
if (re_last_clk - millis() < 100) {
if (re_counter > 0 && re_last_dir < 0) {
// uh oh.
re_counter = 0;
}
if (re_counter < 0 && re_last_dir > 0) {
// uh oh.
re_counter = 0;
}
}
re_last_clk = millis();
// resume interrupts
sei();
}
Devices
KY-040
The KY-040 is an incremental rotary encoder. A full rotation produces 20 pulses.