Read a KY-040 rotary encoder with an Arduino
Beginner
Or see every lesson.
- 261parts on the bench
- 31boards running now
- 1.00xreal time, on every board
A rotary encoder is a knob that turns forever, with a click at each step. Unlike a potentiometer, it has no start or end. Your sketch counts the clicks and decides what they mean: volume, a menu choice, or a number on a display. The KY-040 module is the one in most kits, and it has a push button under the knob too.
The circuit above is an Arduino Uno with a KY-040 on pins 2, 3 and 4, and a lamp on pin 9. Turn the knob and the count goes up or down, and the lamp gets brighter or dimmer. Press the knob to switch to a second way of reading it, the one most tutorials print, and watch it get the count wrong. It runs in your browser on the sketch below.
What you need
- An Arduino Uno
- A KY-040 rotary encoder module
- An LED and a 220 ohm resistor
- A half-size breadboard and jumper wires
Wiring
| Part and pin | Goes to | Note |
|---|---|---|
| KY-040 CLK | Pin 2 | One half of the quadrature pair |
| KY-040 DT | Pin 3 | The other half |
| KY-040 SW | Pin 4 | The push button; read with INPUT_PULLUP |
| KY-040 + | 5 V | Powers the module’s pull-up resistors |
| KY-040 GND | GND | Shared ground |
| LED | Pin 9 | Through a 220 ohm resistor to GND |
Two switches, a quarter of a click apart
Inside the encoder are two switches, CLK and DT. As you turn the knob, they open and close a quarter of a step apart. So the pair of readings walks through four states. Turning clockwise goes 11, 01, 00, 10 and back to 11. Turning the other way walks the same states in reverse. The knob rests at 11 between clicks.
Which way the pair walks tells you which way the knob turned. That is the whole idea. The only hard part is doing it reliably.
Method 1: the state table
Every time the pair changes, the sketch looks up the move in a table. The table holds all sixteen moves from an old state to a new one. A real quarter step scores +1 or -1. A jump of two states at once scores 0 and is ignored, because that is what a bouncing contact looks like. When the knob gets back to 11 with four quarter steps one way, that is one click.
In the circuit above, three clockwise clicks print "count 1", "count 2", "count 3". One click back prints "count 2". Every time.
const int8_t STEP[16] = {
0, -1, 1, 0, // from 00
1, 0, 0, -1, // from 01
-1, 0, 0, 1, // from 10
0, 1, -1, 0, // from 11
};
uint8_t pair() {
return (uint8_t)((digitalRead(CLK) << 1) | digitalRead(DT));
}
// in loop():
uint8_t now = pair();
if (now != state) {
quarters += STEP[(state << 2) | now];
state = now;
if (now == 3) {
if (quarters >= 4) count++;
if (quarters <= -4) count--;
quarters = 0;
}
}Method 2: the falling edge, and why it miscounts
Most tutorials use a shorter method: when CLK goes from HIGH to LOW, read DT. If DT is different from CLK, count up; otherwise, count down. On a perfectly clean encoder, that works.
Real encoder contacts bounce. For a split second after they close, they open and close again. Each bounce on CLK is another falling edge, and the short method counts it. Press the knob in the circuit above to switch to this method, then turn it three clicks clockwise. In our run it printed 1, 0, 1, 0, 1, 0 and ended at 0 instead of 3.
Mokxi’s KY-040 bounces on purpose, because that is the lesson. Click the encoder’s properties and turn bounce off, and the short method counts 1, 2, 3 correctly. The state table gets it right either way.
The push button
SW is a plain button to ground. The module has no pull-up for it, so the sketch uses INPUT_PULLUP. The pin reads LOW when pressed. Here a press switches methods and resets the count. In your own project it might select a menu item.
Common mistakes
Counting every edge. Use the state table, or a library that does, so a bounce cannot add a count.
A slow loop. The sketch must check the pins often, every millisecond or two. A long delay() in loop() misses steps. Interrupts on pins 2 and 3 are another way to catch every change.
Counting at the wrong spot. Some encoders give two or four state changes per click. Count a click only when the knob is back at rest, as the state table does.
Forgetting INPUT_PULLUP on SW. Without it, the button pin floats and reads random presses.
Questions
How do I connect a KY-040 to an Arduino Uno?
CLK to pin 2, DT to pin 3, SW to pin 4, + to 5 V and GND to GND. Pins 2 and 3 are also the Uno’s interrupt pins, which is handy if you want to use interrupts later.
Why does my rotary encoder skip or count twice?
The contacts bounce, and a sketch that counts every falling edge on CLK counts the bounces too. Use a state table, as on this page, which ignores a bounce.
Rotary encoder or potentiometer?
A potentiometer gives a position between two ends. An encoder gives steps that never run out. Use an encoder for menus and anything that should keep turning.
What does Mokxi not model on the KY-040?
The feel of the clicks, and speed. Each click arrives at a fixed rate however fast you turn. The bounce is one short extra edge per quarter step, the same every time, where a real contact is less regular.
Keep going
Build this for real
Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.