A rotary encoder is an electronic component, that can replace a potmeter or other kinds of knob-like functionality. Here is a good Youtube video explaining how a rotary encoder works. Some rotary encoders also have a push button functionality when pushing the shaft.
Example of a rotary encoder with push button switch.
Detecting whether the rotary encoder is rotated left or right can be somewhat tricky, especially with fast rotations. However, the code from Buxtronix is bulletproof provided that the MCU still has breathing space.
Another thing to look out for is that some rotary encoders are half step and others are full step. If you don’t know which step type of rotary encoder you have at hand, you will have to try the different S/W solutions and look out for missing/double steps.
Finally, the detection can be either interrupt driven or by scanning for changes. Which one to use depends on the actual application.
Detecting the push button is not special to the rotary encoder, but is like any other push button, when it comes to detection and debouncing. Please see the push button tutorial.
Half step and interrupt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
// RFzero include and object creation #include <RFzero.h> // MUST ALWAYS be included for RFzero #define encA A0 // Rotary encoder A pin connected to A0 #define encB A1 // Rotary encoder B pin connected to A1 const uint8_t encTableHalfStep[6][4] = { {0x3, 0x2, 0x1, 0x0}, {0x23, 0x0, 0x1, 0x0}, {0x13, 0x2, 0x0, 0x0}, {0x3, 0x5, 0x4, 0x0}, {0x3, 0x3, 0x4, 0x10}, {0x3, 0x5, 0x3, 0x20} }; void RotEncHalfStep() { static volatile int encState = 0; // volatile for interrupt encState = encTableHalfStep[encState & 0xF][(digitalRead(encB) << 1) | digitalRead(encA)]; int result = encState & 0x30; if (result == 0x10) SerialUSB.println("Right/CW rotation"); else if (result == 0x20) SerialUSB.println("Left/CCW rotation"); } void setup() { // USB port setup SerialUSB.begin(9600); delay(2000); SerialUSB.println("Rotary encoder ready"); // Setup pins for encoder pinMode(encA, INPUT); pinMode(encB, INPUT); digitalWrite(encA, HIGH); digitalWrite(encB, HIGH); // Setup interrupt handling for the encoder half- or full-step attachInterrupt(digitalPinToInterrupt(encA), RotEncHalfStep, CHANGE); attachInterrupt(digitalPinToInterrupt(encB), RotEncHalfStep, CHANGE); } void loop() {} |
Full step and scanning
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
// RFzero include and object creation #include <RFzero.h> // MUST ALWAYS be included for RFzero #define encA A0 // Rotary encoder A pin connected to A0 #define encB A1 // Rotary encoder B pin connected to A1 const uint8_t encTableFullStep[7][4] = { {0x0, 0x2, 0x4, 0x0}, {0x3, 0x0, 0x1, 0x10}, {0x3, 0x2, 0x0, 0x0}, {0x3, 0x2, 0x1, 0x0}, {0x6, 0x0, 0x4, 0x0}, {0x6, 0x5, 0x0, 0x20}, {0x6, 0x5, 0x4, 0x0} }; void RotEncFullStep() { static int encState = 0; // volatile not needed for scanning encState = encTableFullStep[encState & 0xF][(digitalRead(encB) << 1) | digitalRead(encA)]; int result = encState & 0x30; if (result == 0x10) SerialUSB.println("Right/CW rotation"); else if (result == 0x20) SerialUSB.println("Left/CCW rotation"); } void setup() { // USB port setup SerialUSB.begin(9600); delay(2000); SerialUSB.println("Rotary encoder ready"); // Setup pins for encoder pinMode(encA, INPUT); pinMode(encB, INPUT); digitalWrite(encA, HIGH); digitalWrite(encB, HIGH); } void loop() { RotEncFullStep(); } |