A rotary encoder is a fantastic component for controlling projects like FM radio receivers, display menus, or volume control. Unlike a standard potentiometer, it can rotate infinitely and also features a built-in push button.
However, when working with it, we quickly face the reality of the physical world: mechanical switch bounce (bouncing). When you turn the knob by a single click, the micro-controller detects dozens of tiny, false on/off triggers due to its extreme speed. The result? The values on your display jump wildly up and down.
How Does a Rotary Encoder Work?
An encoder uses two outputs (usually labeled CLK and DT). When you turn the knob, two square-wave signals are generated on these outputs, shifted 90 degrees out of phase (quadrature). By monitoring which signal changes first, the Arduino determines the direction of rotation:
- Clockwise Direction: The CLK signal changes state before the DT signal.
- Counter-Clockwise Direction: The DT signal changes state before the CLK signal.
The Solution: Software Debouncing Without Extra Components
While we could fix the bounce by soldering capacitors (hardware debouncing), it is much more elegant to solve this programmatically. Using the millis() function in our code, we can define a "dead time" (e.g., 5 milliseconds). If a signal state change occurs faster than this window, the Arduino simply ignores it as noise.
For this example, we will use the Arduino Mega 2560, which offers excellent stability and plenty of processing power to precisely track the signals.
Arduino Mega 2560 Code Example
Below is a fully functional program that you can upload directly to your Arduino. The code uses software debouncing to ensure smooth detection of every single turn:
// Pin definitions on the Arduino Mega
const int pinCLK = 3; // Pin for CLK signal (S1)
const int pinDT = 2; // Pin for DT signal (S2)
int lastStateCLK;
int currentStateCLK;
long position = 0;
// Variables for switch bounce prevention (debouncing)
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 5; // Time in milliseconds
void setup() {
pinMode(pinCLK, INPUT);
pinMode(pinDT, INPUT);
Serial.begin(9600);
// Read the initial state
lastStateCLK = digitalRead(pinCLK);
}
void loop() {
currentStateCLK = digitalRead(pinCLK);
// React ONLY when CLK goes from HIGH to LOW state (falling edge)
if (currentStateCLK != lastStateCLK && currentStateCLK == LOW) {
if ((millis() - lastDebounceTime) > debounceDelay) {
// If the DT state is different from CLK, it is rotating clockwise
if (digitalRead(pinDT) != currentStateCLK) {
position++;
} else {
position--;
}
Serial.print("Position: ");
Serial.println(position);
lastDebounceTime = millis(); // Update the last change time
}
}
lastStateCLK = currentStateCLK;
}
Quick Wiring Instructions
To get everything working on the first try, connect your components by following these simple steps:
- Connect the encoder's GND pin to the GND pin on the Arduino Mega.
- Connect the encoder's + (VCC) pin to the 5V pin on the Arduino.
- Connect the CLK output to digital pin 2 and the DT output to digital pin 3.
- Open the Serial Monitor (set to 9600 baud) and start turning the knob!