A Guide to Wiring a Rotary Encoder to Arduino and ESP32 for DIY Projects
Whether you are building a custom menu interface, a robotics project, or designing your own DIY CNC machine, mastering the rotary encoder is a critical skill. Unlike standard potentiometers that have a limited range of motion, rotary encoders can rotate infinitely, providing precise digital feedback regarding position, speed, and direction. This makes them indispensable in modern electronics.
In this comprehensive guide, we will walk you through everything you need to know about wiring, programming, and troubleshooting a rotary encoder using the two most popular microcontrollers in the maker community: the Arduino and the ESP32. We will also explore how these DIY concepts scale up to industrial applications, such as CNC handwheels and automated control systems.
Understanding the Basics: How Does a Rotary Encoder Work?
Before diving into wires and code, it is essential to understand the mechanics behind the hardware. A rotary encoder is an electro-mechanical device that converts the angular position or motion of a shaft into analog or digital output signals.
There are two main types of rotary encoders:
- Absolute Encoders: These output a unique digital code for every distinct angle of the shaft. They “remember” their position even after a power cycle.
- Incremental Encoders: These generate a series of pulses as the shaft rotates. They do not know their absolute position upon startup; they only track how far they have moved from a given starting point. This is the most common type used in DIY projects (such as the popular KY-040 module).
The Quadrature Output Explained
Incremental encoders typically utilize a “quadrature” output. Inside the encoder, a slotted disc rotates between light sensors (or mechanical contacts in cheaper models), generating two separate square wave signals, commonly referred to as Channel A (CLK) and Channel B (DT).
These two signals are out of phase by 90 degrees. By analyzing which channel transitions first, the microcontroller can determine the direction of rotation (clockwise or counter-clockwise). By counting the number of pulses, it can determine the distance or angle traveled.

Essential Components for Your Setup
To follow along with this guide, you will need a few basic components:
- An Arduino board (e.g., Arduino UNO, Nano) or an ESP32 development board.
- A Rotary Encoder module (the KY-040 is highly recommended for beginners as it includes built-in pull-up resistors).
- Jumper wires.
- A breadboard.
- Optional but recommended: Capacitors ($0.1\mu F$) and resistors ($10k\Omega$) for hardware debouncing, and a high-quality encoder cable to prevent signal noise if placing the encoder far from the board.
Wiring a Rotary Encoder to an Arduino
The Arduino UNO is an excellent starting point for reading encoder signals. While you can read the signals using standard digital polling, using Interrupts is highly recommended to ensure you never miss a pulse, even if the Arduino is busy processing other code.
Arduino Pinout and Connection Diagram
Assuming you are using a standard KY-040 rotary encoder module, the wiring to the Arduino is as follows:
- GND: Connect to Arduino GND.
- + (VCC): Connect to Arduino 5V.
- SW (Switch): Connect to Digital Pin 4 (This is the push-button feature of the encoder).
- DT (Data / Channel B): Connect to Digital Pin 3.
- CLK (Clock / Channel A): Connect to Digital Pin 2.
Note: Pins 2 and 3 on the Arduino UNO support hardware interrupts, which is why we route the DT and CLK signals to them.
Example Arduino Code (Using Interrupts)
Here is a robust code snippet that uses hardware interrupts to track the encoder’s position accurately:
#define CLK_PIN 2
#define DT_PIN 3
#define SW_PIN 4
volatile int encoderPosition = 0;
int lastPosition = 0;
void setup() {
Serial.begin(9600);
pinMode(CLK_PIN, INPUT_PULLUP);
pinMode(DT_PIN, INPUT_PULLUP);
pinMode(SW_PIN, INPUT_PULLUP);
// Attach interrupt to the CLK pin
attachInterrupt(digitalPinToInterrupt(CLK_PIN), updateEncoder, CHANGE);
}
void loop() {
if (encoderPosition != lastPosition) {
Serial.print(“Position: “);
Serial.println(encoderPosition);
lastPosition = encoderPosition;
}
if (digitalRead(SW_PIN) == LOW) {
Serial.println(“Button Pressed!”);
delay(200); // Simple debounce
}
}
void updateEncoder() {
int dtValue = digitalRead(DT_PIN);
int clkValue = digitalRead(CLK_PIN);
if (clkValue == dtValue) {
encoderPosition++;
} else {
encoderPosition–;
}
}

Wiring a Rotary Encoder to an ESP32
The ESP32 is significantly more powerful than the standard Arduino UNO. It operates on a 3.3V logic level, meaning you must power your encoder with 3.3V, NOT 5V, to avoid damaging the GPIO pins.
ESP32 Pinout and Connection Diagram
- GND: Connect to ESP32 GND.
- + (VCC): Connect to ESP32 3.3V.
- SW (Switch): Connect to GPIO 25.
- DT (Data / Channel B): Connect to GPIO 26.
- CLK (Clock / Channel A): Connect to GPIO 27.
Note: Almost all GPIO pins on the ESP32 support interrupts, giving you much more flexibility in your wiring layout.
Example ESP32 Code
While you can use the exact same interrupt logic as the Arduino, the ESP32 is fast enough to handle standard hardware interrupts seamlessly. Here is the adapted code for the ESP32:
#define CLK_PIN 27
#define DT_PIN 26
#define SW_PIN 25
volatile int encoderCounter = 0;
int lastCounter = 0;
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
void IRAM_ATTR isr() {
portENTER_CRITICAL_ISR(&mux);
int dtVal = digitalRead(DT_PIN);
int clkVal = digitalRead(CLK_PIN);
if (clkVal == dtVal) {
encoderCounter++;
} else {
encoderCounter–;
}
portEXIT_CRITICAL_ISR(&mux);
}
void setup() {
Serial.begin(115200);
pinMode(CLK_PIN, INPUT_PULLUP);
pinMode(DT_PIN, INPUT_PULLUP);
pinMode(SW_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(CLK_PIN), isr, CHANGE);
}
void loop() {
if (encoderCounter != lastCounter) {
Serial.print(“ESP32 Encoder Position: “);
Serial.println(encoderCounter);
lastCounter = encoderCounter;
}
}
The Critical Importance of Debouncing
Mechanical rotary encoders suffer from a phenomenon known as “contact bounce.” When the metal contacts inside the encoder touch and separate, they can bounce on a microscopic level, creating multiple false signals in a fraction of a millisecond. If left untreated, your microcontroller will count these bounces as actual rotations, leading to wild and erratic readings.
Software Debouncing
Software debouncing involves ignoring any signals that occur within a few milliseconds of each other. While the `delay()` function works for basic buttons, it ruins the fast responsiveness required for encoders. Instead, timing functions like `millis()` should be used to filter out noise.
Hardware Debouncing (RC Filter)
The most reliable method is implementing a hardware RC (Resistor-Capacitor) low-pass filter on the CLK and DT lines. By adding a $10k\Omega$ resistor in series and a $0.1\mu F$ capacitor to ground, you smooth out the erratic voltage spikes.
The cutoff frequency for this RC filter can be calculated using the formula:
$$f_c = \frac{1}{2 \pi R C}$$
With a $10k\Omega$ resistor and a $0.1\mu F$ capacitor, the cutoff frequency is approximately $159 Hz$, effectively filtering out the high-frequency mechanical bounce while allowing human-speed rotations to pass through perfectly.

Applications: From Simple DIY to Industrial CNC Handwheels
Once you understand how to interface a basic KY-040 encoder with a microcontroller, a massive world of applications opens up. In simple DIY electronics, encoders are used for volume knobs, menu navigation screens, and robotic wheel odometry.
However, the exact same underlying principles apply to industrial manufacturing and CNC machining. In the professional realm, rotary encoders are scaled up into heavy-duty Manual Pulse Generators (MPGs) or handwheels. These industrial handwheels allow machinists to manually position heavy CNC axes with micrometer precision.
While a DIY Arduino project uses a $2 encoder, an industrial control system might rely on a high-precision Fanuc encoder that outputs thousands of pulses per revolution (PPR) via differential signals (RS422) for extreme noise immunity over long cable runs. Despite the difference in scale and price, the core concept of counting quadrature pulses remains identically rooted in the logic you just learned.
Technical Comparison: Arduino vs. ESP32 for Encoder Interfacing
Which microcontroller is best for your encoder project? Review this technical breakdown to decide.
| Feature | Arduino UNO | ESP32 |
|---|---|---|
| Operating Voltage | 5V Logic | 3.3V Logic |
| Hardware Interrupt Pins | Only Pins 2 & 3 | Almost all GPIOs |
| Clock Speed | 16 MHz | 160 – 240 MHz |
| Hardware Pulse Counter (PCNT) | No | Yes (Dedicated hardware subsystem) |
| Best Use Case | Simple menus, basic DIY robotic feedback. | High-speed rotational tracking, complex CNC interfaces, IoT remote monitoring. |
Frequently Asked Questions (FAQ)
Why is my rotary encoder skipping values or counting backwards?
This is almost always caused by mechanical switch bounce. When you rotate the encoder, the internal contacts bounce, creating false signals. You must implement either software debouncing using a timing variable or, ideally, hardware debouncing using a small capacitor and resistor on the output pins.
Can I use a 5V encoder on the ESP32?
The ESP32 has a strictly 3.3V logic level limit. Connecting a 5V signal directly to an ESP32 GPIO pin can permanently damage the board. You must either power the encoder with 3.3V (most KY-040 modules work fine at 3.3V) or use a logic level converter.
How do I read a high-resolution industrial encoder with an Arduino?
High-resolution encoders output pulses much faster than a standard Arduino can read via basic interrupts. For industrial encoders (e.g., 2000+ PPR), you will need dedicated quadrature decoder ICs (like the LS7366R) or a much faster microcontroller like an ESP32 utilizing its dedicated PCNT (Pulse Counter) hardware peripheral.
Conclusion
Wiring and programming a rotary encoder with an Arduino or ESP32 is a fantastic way to introduce precise control and feedback into your DIY projects. By understanding quadrature signals, utilizing hardware interrupts, and effectively debouncing the output, you can create smooth, professional-feeling interfaces.
The skills you learn manipulating simple DIY encoders are highly transferable. The logic remains exactly the same whether you are turning down the volume on a custom Bluetooth speaker or dialing in the Z-axis on a massive industrial CNC milling machine.
Ready to Upgrade Your CNC Projects?
Whether you are transitioning from Arduino prototypes to professional builds, or repairing high-end industrial machinery, precision is everything. Explore our extensive catalog of genuine, high-performance CNC electronics and rotary encoders.








