📋 Overview
The KY-040 Rotary Encoder Module is a 360-degree rotary input device — essentially a control knob — that tells your microcontroller how much the knob has been rotated and which direction it's turning. Unlike a potentiometer, a rotary encoder has no start or end point — it spins continuously in either direction. It also includes a built-in pushbutton switch activated by pressing down on the shaft, giving you an extra input for things like confirming selections or toggling modes.
The module comes on a small breakout board with the necessary pull-up resistors already included, so you can connect it directly to an Arduino or other microcontroller with just a few jumper wires. It's a great choice for menu navigation, volume controls, parameter adjustments, and any project where you need precise rotational input.
⭐ Key Features
- Incremental Rotary Encoder — Continuous 360° rotation with no mechanical stops
- 20 Detents Per Revolution — Each click represents an 18° step, providing tactile feedback
- Quadrature Output — Two output channels (CLK and DT) allow direction detection
- Built-in Pushbutton Switch — Press the shaft to activate a normally-open momentary switch
- On-Board Pull-Up Resistors — 10kΩ pull-ups on CLK, DT, and SW lines — no external resistors needed
- 5V and 3.3V Compatible — Works with Arduino, Raspberry Pi, ESP32, and other common boards
- Simple 5-Pin Interface — Easy connection with standard jumper wires
📌 Pinout
| Pin | Label | Description |
|---|---|---|
| 1 | CLK | Encoder Output A (clock signal) |
| 2 | DT | Encoder Output B (direction signal) |
| 3 | SW | Pushbutton switch output (active LOW when pressed) |
| 4 | + | Power supply (+5V DC) |
| 5 | GND | Ground (also Encoder Pin C — common contact) |
🔧 How It Works
Inside the encoder there are two internal switches. One connects pin A (CLK) to pin C (GND), and the other connects pin B (DT) to pin C (GND). The encoder has 20 fixed positions per revolution — you can feel these as small "clicks" when you turn the shaft.
At each resting position, both internal switches are in the same state — either both open or both closed. When you rotate the encoder by one click, both switches change state. The key to detecting direction is which switch changes first:
- Clockwise rotation: Pin A (CLK) changes state before Pin B (DT)
- Counter-clockwise rotation: Pin B (DT) changes state before Pin A (CLK)
This is called quadrature encoding, and it's the standard method used by incremental rotary encoders. Your Arduino code simply monitors these two signals and compares their states to determine the direction and number of steps.
The pushbutton switch is a separate normally-open momentary switch. When you press down on the encoder shaft, the SW pin is pulled LOW (connected to GND). When released, the on-board pull-up resistor pulls it back HIGH.
📏 Specifications
| Parameter | Value |
|---|---|
| Encoder Type | Incremental rotary encoder |
| Operating Voltage | 5V DC (also works with 3.3V) |
| Detents Per Revolution | 20 (18° per detent) |
| Pulses Per Revolution | 20 |
| Rotation | Continuous 360° (no mechanical stops) |
| Pushbutton Switch | Normally open, momentary (active LOW) |
| On-Board Pull-Up Resistors | 10kΩ on CLK, DT, and SW |
| Output Signal | 2-bit quadrature (Gray code) |
| Module Dimensions | Approx. 30 × 18 × 30 mm (1.18 x 0.71 x 1.18 inches) L × W × H |
🔌 Wiring — Connecting to Arduino
The KY-040 module connects to an Arduino Uno (or compatible board) using five jumper wires. No additional components are needed — the pull-up resistors are already on the module.
| KY-040 Pin | Arduino Pin |
|---|---|
| CLK | Digital Pin 2 |
| DT | Digital Pin 3 |
| SW | Digital Pin 4 |
| + | 5V |
| GND | GND |
Tip: Using Digital Pins 2 and 3 for CLK and DT is recommended on the Arduino Uno because these pins support hardware interrupts (INT0 and INT1), which can improve responsiveness if you later upgrade to interrupt-driven code.
🚀 Step 1: Basic Rotation and Button Detection
This sketch reads the rotary encoder and detects both the direction of rotation and button presses. The current position counter and button state are displayed in the Serial Monitor. Turn the knob clockwise to increase the counter, counter-clockwise to decrease it, and press the shaft to see the button event.
/*
* KY-040 Rotary Encoder — Basic Reading
* Envistia Mall - Product Support
*
* Reads rotation direction and pushbutton press.
* Displays position counter and button state
* on the Serial Monitor.
*
* Connections:
* CLK -> Arduino Pin 2
* DT -> Arduino Pin 3
* SW -> Arduino Pin 4
* + -> Arduino 5V
* GND -> Arduino GND
*/
// Pin definitions
const int pinCLK = 2; // Encoder output A
const int pinDT = 3; // Encoder output B
const int pinSW = 4; // Pushbutton switch
int counter = 0; // Position counter
int lastStateCLK; // Previous CLK state
int currentStateCLK; // Current CLK state
unsigned long lastButtonPress = 0; // Debounce timer
void setup() {
pinMode(pinCLK, INPUT);
pinMode(pinDT, INPUT);
pinMode(pinSW, INPUT_PULLUP); // Use internal pull-up as backup
Serial.begin(9600);
// Read the initial state of CLK
lastStateCLK = digitalRead(pinCLK);
Serial.println("KY-040 Rotary Encoder Test");
Serial.println("Turn the knob or press the button.");
Serial.println("----------------------------------");
}
void loop() {
// --- Read Rotation ---
currentStateCLK = digitalRead(pinCLK);
// CLK state changed — a rotation step occurred
if (currentStateCLK != lastStateCLK) {
// If DT state differs from CLK, rotation is clockwise
if (digitalRead(pinDT) != currentStateCLK) {
counter++;
Serial.print("Clockwise | Position: ");
} else {
counter--;
Serial.print("Counter-CW | Position: ");
}
Serial.println(counter);
}
// Save current CLK state for next loop
lastStateCLK = currentStateCLK;
// --- Read Pushbutton ---
if (digitalRead(pinSW) == LOW) {
// Simple debounce — ignore presses within 250ms
if (millis() - lastButtonPress > 250) {
Serial.println("Button pressed!");
lastButtonPress = millis();
}
}
}
How to Use:
- Wire the KY-040 module to your Arduino as shown in the wiring table above.
- Copy and paste the sketch into the Arduino IDE and upload it to your board.
- Open the Serial Monitor (Tools → Serial Monitor) and set the baud rate to 9600.
- Turn the encoder knob — you'll see "Clockwise" or "Counter-CW" with a running position counter.
- Press down on the encoder shaft — you'll see "Button pressed!" in the Serial Monitor.
🚀 Step 2: Encoder-Controlled LED Brightness
This sketch uses the rotary encoder to control the brightness of an LED connected to a PWM pin. Turning clockwise increases brightness, turning counter-clockwise decreases it, and pressing the button toggles the LED on and off. This demonstrates a practical real-world use of the encoder as a dimmer control.
/*
* KY-040 Rotary Encoder — LED Brightness Control
* Envistia Mall - Product Support
*
* Turn the encoder to adjust LED brightness.
* Press the button to toggle the LED on/off.
*
* Connections:
* CLK -> Arduino Pin 2
* DT -> Arduino Pin 3
* SW -> Arduino Pin 4
* + -> Arduino 5V
* GND -> Arduino GND
* LED -> Arduino Pin 9 (PWM) with 220 ohm resistor
*/
const int pinCLK = 2;
const int pinDT = 3;
const int pinSW = 4;
const int pinLED = 9; // PWM-capable pin
int brightness = 128; // Start at 50% brightness (0–255)
int lastStateCLK;
bool ledOn = true;
unsigned long lastButtonPress = 0;
void setup() {
pinMode(pinCLK, INPUT);
pinMode(pinDT, INPUT);
pinMode(pinSW, INPUT_PULLUP);
pinMode(pinLED, OUTPUT);
Serial.begin(9600);
lastStateCLK = digitalRead(pinCLK);
analogWrite(pinLED, brightness);
Serial.println("LED Brightness Control");
Serial.println("Turn knob to adjust, press to toggle on/off.");
}
void loop() {
int currentStateCLK = digitalRead(pinCLK);
if (currentStateCLK != lastStateCLK) {
if (digitalRead(pinDT) != currentStateCLK) {
brightness += 15; // Increase brightness
} else {
brightness -= 15; // Decrease brightness
}
// Constrain to valid PWM range
brightness = constrain(brightness, 0, 255);
if (ledOn) {
analogWrite(pinLED, brightness);
}
Serial.print("Brightness: ");
Serial.print(map(brightness, 0, 255, 0, 100));
Serial.println("%");
}
lastStateCLK = currentStateCLK;
// Toggle LED on/off with button press
if (digitalRead(pinSW) == LOW) {
if (millis() - lastButtonPress > 250) {
ledOn = !ledOn;
if (ledOn) {
analogWrite(pinLED, brightness);
Serial.println("LED ON");
} else {
analogWrite(pinLED, 0);
Serial.println("LED OFF");
}
lastButtonPress = millis();
}
}
}
How to Use:
- Wire the KY-040 module to your Arduino as shown in Step 1.
- Connect an LED with a 220Ω resistor between Arduino Pin 9 and GND.
- Upload the sketch and open the Serial Monitor at 9600 baud.
- Turn the encoder to adjust brightness — the Serial Monitor shows the percentage.
- Press the encoder button to toggle the LED on and off.
💡 Tips & Best Practices
- Debouncing matters. Mechanical encoders produce electrical noise (bouncing) when the contacts open and close. The example sketches include basic software debouncing, but for fast-spinning applications, consider adding 0.1µF (100nF) capacitors between CLK/GND and DT/GND for hardware debouncing.
- Use interrupts for better performance. The polling approach in the examples works well for basic projects, but if you're doing other processing in your loop, you may miss steps. Using hardware interrupts on pins 2 and 3 (Arduino Uno) ensures every step is captured, even during heavy processing.
-
The module includes pull-up resistors. The KY-040 board has 10kΩ pull-ups on CLK, DT, and SW. You generally don't need to enable Arduino's internal pull-ups for CLK and DT, though using
INPUT_PULLUPfor the SW pin provides a reliable backup. - Add a knob for easier use. The encoder shaft accepts any standard 6mm D-shaft knob, which makes it much easier to turn precisely.
- Keep wires short. Long jumper wires can pick up noise and cause false readings. Keep your connections under 20cm (8 inches) for best results.
🛠️ Troubleshooting
| Symptom | Possible Cause | Solution |
|---|---|---|
| Counter jumps by 2 or skips steps | Contact bounce / noise | Add 100nF capacitors between CLK→GND and DT→GND, or add a small delay in your code |
| Direction is reversed | CLK and DT wires swapped | Swap the CLK and DT connections, or swap the pin definitions in your sketch |
| No response when turning | Wiring issue or wrong pins | Double-check all five connections and verify pin numbers match your code |
| Button not detected | SW pin not configured correctly | Ensure SW pin is set to INPUT_PULLUP and you're checking for LOW
|
| Erratic readings at high speed | Polling too slow | Switch to interrupt-driven code for CLK and DT |
| Counter changes when pressing button | Mechanical vibration triggering encoder | Add debounce delay or ignore encoder changes within 50ms of a button press |
🎯 Typical Applications
- Menu navigation and selection on LCD/OLED displays
- Volume and brightness controls
- Parameter adjustment (set points, thresholds, timer values)
- Motor speed and direction control
- Scrolling through lists or data
- CNC and 3D printer manual jog controls
- DIY audio equipment (amplifiers, mixers)
- Robotics — steering and configuration input
🏪 Where to Buy
This module is available at Envistia Mall.
- 📦 Fast US Shipping
- 🔄 Hassle-Free Returns
- 📧 Responsive Customer Support
📚 Additional Resources
- How Rotary Encoder Works and How To Use It with Arduino — How To Mechatronics
- Coding a KY-040 Rotary Encoder on a Raspberry Pi Pico — GurgleApps.com
- Best code to use with a KY-040 Rotary Encoder? Let’s find out! — Brainy Bits
- Arduino attachInterrupt() Reference — for interrupt-driven encoder reading
-
VIDEO: How to use Rotary Encoder with Arduino video by Mario’s Ideas on YouTube
Sold and supported by Envistia Mall. Ships from the USA. The manufacturer and Envistia LLC (dba Envistia Mall) are not responsible for any damages or losses resulting from the use of this product. Always follow proper electrical safety practices when working with electronic components. Specifications are based on manufacturer data and are subject to change without notice.