A rotary encoder converts the angular position and rotation speed of a shaft into electrical signals that Arduino can count and track. Unlike potentiometers, encoders have no physical stop — they rotate continuously and can track both direction and speed. This makes them essential for user interface dials (volume control, menu navigation), motor position feedback, CNC machines, and any application where you need to know “how far and how fast something turned.” This guide covers both manual encoders for UI projects and optical encoders for motor feedback.
Encoder Types Explained
Mechanical (Manual) Rotary Encoders
The KY-040 is the most common module for Arduino projects. It has a knob you turn by hand, with detents (clicks) at each step — typically 20 detents per revolution. Each click outputs one pulse on channels A and B. By reading which channel pulses first, you determine rotation direction. A built-in push button activates when you press the knob down. Cost: ₹30-50.
Optical Encoders
Optical encoders use a slotted disc and IR sensor to detect rotation. They generate hundreds or thousands of pulses per revolution (PPR), making them suitable for precision position tracking. They’re used for motor speed measurement, CNC axis position, and industrial controls. A basic slot-type speed sensor module costs ₹30-50; a proper incremental encoder costs ₹300-2,000 depending on resolution.
Absolute vs Incremental
- Incremental: Outputs pulses as the shaft rotates. You count pulses from a reference point. If power is lost, position is lost — you must re-reference (home). Both KY-040 and most optical encoders are incremental.
- Absolute: Outputs a unique code for each angular position. Position is maintained even after power loss. More expensive and not common in hobby projects.
Mechanical Rotary Encoder (KY-040)
Pinout
| Pin | Function | Arduino |
|---|---|---|
| CLK | Channel A output | D2 (interrupt pin) |
| DT | Channel B output | D3 |
| SW | Push button | D4 (INPUT_PULLUP) |
| + | VCC | 5V |
| GND | Ground | GND |
Wiring and Arduino Code
const int CLK = 2; // Channel A
const int DT = 3; // Channel B
const int SW = 4; // Button
int counter = 0;
int lastCLK;
unsigned long lastButtonPress = 0;
void setup() {
Serial.begin(9600);
pinMode(CLK, INPUT);
pinMode(DT, INPUT);
pinMode(SW, INPUT_PULLUP);
lastCLK = digitalRead(CLK);
}
void loop() {
int currentCLK = digitalRead(CLK);
// Detect rotation
if (currentCLK != lastCLK && currentCLK == HIGH) {
if (digitalRead(DT) != currentCLK) {
counter++; // Clockwise
} else {
counter--; // Counter-clockwise
}
Serial.print("Position: ");
Serial.println(counter);
}
lastCLK = currentCLK;
// Detect button press
if (digitalRead(SW) == LOW) {
if (millis() - lastButtonPress > 300) { // Debounce
Serial.println("Button pressed! Counter reset.");
counter = 0;
lastButtonPress = millis();
}
}
}
Using Interrupts for Reliable Counting
The polling method above can miss steps if your main loop is busy with other tasks (display updates, sensor readings). For reliable counting, use hardware interrupts:
const int CLK = 2; // Must be interrupt pin
const int DT = 3;
volatile int counter = 0;
void setup() {
Serial.begin(9600);
pinMode(CLK, INPUT_PULLUP);
pinMode(DT, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(CLK), readEncoder, FALLING);
}
void readEncoder() {
if (digitalRead(DT) == HIGH) {
counter++;
} else {
counter--;
}
}
void loop() {
static int lastCounter = 0;
if (counter != lastCounter) {
Serial.print("Position: ");
Serial.println(counter);
lastCounter = counter;
}
delay(10);
}
The volatile keyword tells the compiler that counter can change outside normal program flow (in the interrupt service routine). Without it, the compiler may optimise away reads of the variable.
Optical Encoders for Motor Feedback
For measuring motor speed and position, attach a slotted encoder disc to the motor shaft and mount an optical slot sensor module beside it. Each slot in the disc triggers one pulse. With a 20-slot disc on a motor running at 300 RPM, you get 100 pulses per second — fast enough for PID speed control.
// Motor RPM measurement with optical encoder
const int encoderPin = 2; // Interrupt pin
volatile unsigned long pulseCount = 0;
unsigned long lastTime = 0;
const int SLOTS = 20; // Slots in encoder disc
void setup() {
Serial.begin(9600);
pinMode(encoderPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(encoderPin), countPulse, FALLING);
}
void countPulse() {
pulseCount++;
}
void loop() {
if (millis() - lastTime >= 1000) { // Every 1 second
noInterrupts();
unsigned long count = pulseCount;
pulseCount = 0;
interrupts();
float rpm = (count * 60.0) / SLOTS;
Serial.print("RPM: ");
Serial.println(rpm, 0);
lastTime = millis();
}
}
Practical Projects
1. Menu Navigation System
Use the KY-040 as a navigation interface for OLED menus. Rotate to scroll through options, press to select. This replaces multiple buttons with a single, intuitive control — common in 3D printers, oscilloscopes, and audio equipment.
2. Volume Control with Digital Potentiometer
Map encoder rotation to a digital potentiometer (MCP4131) for audio volume control. Each click of the encoder increases or decreases the resistance by one step, providing smooth, precise volume adjustment without mechanical potentiometer noise.
3. Motor PID Speed Controller
Use an optical encoder for motor speed feedback in a PID control loop. Set a target RPM, read actual RPM from the encoder, and adjust PWM duty cycle to maintain the target speed regardless of load changes. This is the foundation of every motor controller in robotics and CNC machines.
4. Odometry for Mobile Robots
Attach encoders to both wheels of a differential-drive robot. By counting pulses from each wheel, calculate the robot’s position and heading (dead reckoning). With known wheel diameter and wheel spacing, convert pulse counts to linear and angular displacement.
5. CNC Axis Position Display
Attach a linear encoder (or rotary encoder on a leadscrew) to a lathe or milling machine axis. Display the position on an OLED screen as a digital readout (DRO). This is a popular workshop upgrade that adds digital position display to manual machines.
Frequently Asked Questions
What’s the difference between a rotary encoder and a potentiometer?
A potentiometer has a fixed rotation range (usually 270°) and outputs an absolute position. A rotary encoder rotates infinitely and outputs relative changes (pulses). Encoders don’t wear out from rotation (no resistive element being scraped), but they don’t retain position when powered off (incremental type).
Why does my encoder skip steps or count double?
Mechanical encoders produce contact bounce — rapid on/off transitions at each detent. Without debouncing, Arduino counts these as multiple steps. Solutions: (1) Add 100nF capacitors between CLK/GND and DT/GND, (2) use software debouncing (ignore changes within 5ms of the last change), (3) use interrupts with a minimum time check.
How many pulses per revolution does the KY-040 produce?
The KY-040 typically has 20 detents per revolution. In the basic code above, each detent produces one count change. With quadrature decoding (reading both edges of both channels), you can get up to 80 counts per revolution for finer resolution.
Can I use an encoder to measure absolute position?
Incremental encoders only give relative position from the startup reference. To get absolute position, home the encoder to a known reference point at startup (e.g., a limit switch) and count from there. Or use an absolute encoder that outputs a unique code for each position — these are more expensive but retain position through power cycles.
Which encoder is best for motor speed control?
For DC motors, a 20-slot optical encoder disc gives sufficient resolution for PID speed control up to a few thousand RPM. For servo-grade precision, use a 200-600 PPR incremental encoder. The higher the PPR, the smoother and more accurate the speed control at low speeds.
Conclusion
Rotary encoders are fundamental input devices for both user interfaces and motor control. The KY-040 mechanical encoder at ₹30-50 adds an intuitive rotary control to any Arduino project, while optical encoders enable precise speed and position measurement for robotics and CNC. With interrupt-based reading, an Arduino can reliably track thousands of pulses per second — more than enough for any hobby or educational application.
Find rotary encoders, speed sensor modules, and motor control components at Zbotic’s sensor collection.
Add comment