Rotary encoder on pins 2, 3 and 4, LEDs on 5 and 6, on an Arduino Uno
A rotary encoder with CLK on pin 2, DT on 3 and the switch on 4 counts its clicks; the LED on 5 lights for a turn one way and the LED on 6 for the other, and a press sets the count back to zero. The whole build, an Arduino Uno and 5 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
Teaching? Assign it to your class in one click.
Intermediate About 25 minutes to build and run. Runs in your browser. Free, and no account needed.
The parts retail for about $31. On Mokxi they're free.
What you'll learn
- How a quadrature encoder shows direction
- Hardware interrupts with
attachInterrupt - Sharing variables safely with
volatileandnoInterrupts - Reading a push switch with
INPUT_PULLUP
- Difficulty: Intermediate
- Time: About 25 minutes
- Board: Arduino Uno R3
- Parts: 7
How it works
The encoder has two switches, CLK and DT, a quarter of a click apart. When CLK falls, the level on DT tells which way the shaft turned: high is one way, low is the other.
CLK is on pin 2, an interrupt pin, so every click is caught even while the loop is printing. Each click changes a count and a direction. The green LED on pin 5 lights for one direction and the red LED on pin 6 for the other, and the count prints to serial.
Pressing the knob, switch SW on pin 4, resets the count to zero.
Parts list
7 parts, plus the jumper wires. They retail for about $31; on Mokxi they're free.
- 1 × Arduino Uno R3In Starter Kit, Claw Machine Kit, Vending Machine Kit.
- 1 × Half breadboard
- 2 × Resistor, 220 ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
- 1 × LED, redIn Starter Kit, Halloween Prop Kit, Logic Lab Kit.
- 1 × LED, greenIn Starter Kit, Halloween Prop Kit, Logic Lab Kit.
- 1 × Rotary encoder, KY-040
Kits that can build it
This starter kit has every part in the circuit. Each kit's page lists what else it can build.
Wiring table
Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.
| Part and pin | Connects to |
|---|---|
| LED, red pin C | Arduino Uno R3 pin GND |
| LED, green pin C | Arduino Uno R3 pin GND |
| Rotary encoder, KY-040 pin GND | Arduino Uno R3 pin GND |
| Resistor, 220 Ω (1) pin 1 | Arduino Uno R3 pin 6 |
| Resistor, 220 Ω (2) pin 1 | Arduino Uno R3 pin 5 |
| Rotary encoder, KY-040 pin SW | Arduino Uno R3 pin 4 |
| Rotary encoder, KY-040 pin DT | Arduino Uno R3 pin 3 |
| Rotary encoder, KY-040 pin CLK | Arduino Uno R3 pin 2 |
| Rotary encoder, KY-040 pin VCC | Arduino Uno R3 pin 5V |
| LED, red pin A | Resistor, 220 Ω (1) pin 2 |
| LED, green pin A | Resistor, 220 Ω (2) pin 2 |
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// A rotary encoder: count the clicks, and which way they went.
//
// encoder CLK -> pin 2 DT -> pin 3 SW -> pin 4 + -> 5V GND -> GND
// pin 5 -> 220 ohm -> LED -> GND pin 6 -> 220 ohm -> LED -> GND
//
// CLK and DT are two switches a quarter of a click apart. When CLK falls, DT
// says which way the shaft went: HIGH one way, LOW the other. Pin 2 is an
// interrupt pin, so no click is missed while the loop prints.
//
// Turn the encoder on the canvas, or press it to reset the count.
const int CLK = 2;
const int DT = 3;
const int SW = 4;
const int LED_FORWARD = 5;
const int LED_BACK = 6;
volatile int count = 0;
volatile int direction = 0;
int shown = 0;
void turned() {
if (digitalRead(DT)) {
count++;
direction = 1;
} else {
count--;
direction = -1;
}
}
void setup() {
pinMode(CLK, INPUT_PULLUP);
pinMode(DT, INPUT_PULLUP);
pinMode(SW, INPUT_PULLUP);
pinMode(LED_FORWARD, OUTPUT);
pinMode(LED_BACK, OUTPUT);
attachInterrupt(digitalPinToInterrupt(CLK), turned, FALLING);
Serial.begin(9600);
Serial.println("Mokxi Uno: rotary encoder on pins 2, 3 and 4");
}
void loop() {
if (digitalRead(SW) == LOW) {
noInterrupts();
count = 0;
interrupts();
digitalWrite(LED_FORWARD, LOW);
digitalWrite(LED_BACK, LOW);
Serial.println("STOP COUNT = 0");
delay(500);
}
noInterrupts();
int now = count;
int way = direction;
interrupts();
if (now != shown) {
shown = now;
digitalWrite(LED_FORWARD, way > 0 ? HIGH : LOW);
digitalWrite(LED_BACK, way < 0 ? HIGH : LOW);
Serial.println(now);
}
}
Code walkthrough
The key functions of the sketch, and what each one does.
turned sketch.ino
Runs every time CLK falls. It reads DT: if it is high, count goes up and direction is 1. Otherwise count goes down and direction is -1. Both are volatile because an interrupt changes them behind the loop's back.
void turned() {
if (digitalRead(DT)) {
count++;
direction = 1;
} else {
count--;
direction = -1;
}
}setup sketch.ino
Turns on pull-ups for CLK, DT and SW, makes the two LED pins outputs, and attaches turned to CLK on a FALLING edge. Then it opens serial at 9600 baud.
void setup() {
pinMode(CLK, INPUT_PULLUP);
pinMode(DT, INPUT_PULLUP);
pinMode(SW, INPUT_PULLUP);
pinMode(LED_FORWARD, OUTPUT);
pinMode(LED_BACK, OUTPUT);
attachInterrupt(digitalPinToInterrupt(CLK), turned, FALLING);
Serial.begin(9600);
Serial.println("Mokxi Uno: rotary encoder on pins 2, 3 and 4");
}loop sketch.ino
If the button reads LOW, it resets the count and turns both LEDs off. Otherwise it takes a safe copy of count and direction. When the count has changed, it lights the matching LED and prints the number.
void loop() {
if (digitalRead(SW) == LOW) {
noInterrupts();
count = 0;
interrupts();
digitalWrite(LED_FORWARD, LOW);
digitalWrite(LED_BACK, LOW);
Serial.println("STOP COUNT = 0");
delay(500);
}
noInterrupts();
int now = count;
int way = direction;
interrupts();
if (now != shown) {
shown = now;
digitalWrite(LED_FORWARD, way > 0 ? HIGH : LOW);
digitalWrite(LED_BACK, way < 0 ? HIGH : LOW);
Serial.println(now);
}
}Troubleshooting
The usual mistakes with this circuit, and the fix for each.
- The count goes the wrong way This project
Swap the CLK and DT wires, or flip the test inturned. - No clicks are counted This project
CLK must be on pin 2. The interrupt is attached there. - The LEDs stay dark This project
Check pin 5 and pin 6 each go through a 220 ohm resistor to an LED, with the LED's other end to ground. - The LED is dim, burns out, or the pin reads wrong because the resistor value is off. Resistor
Read the color bands again. A 220 ohm to 330 ohm resistor suits a typical LED on 5 V. 10k is a common pull-up or pull-down value. Check with a multimeter if unsure. - A pull-up or pull-down resistor is missing, so the input floats and reads random values. Resistor
Tie the input to 5 V or ground through about 10k. Or use INPUT_PULLUP and wire the switch to ground. - The LED does not light, or it lights once and then dies. LED
LEDs have polarity. The longer leg (anode) goes toward the positive side. Always add a series resistor, about 220 ohms to 1 k ohm on a 5 V pin. - A blue or white LED stays dark on 3.3 V. LED
Blue and white LEDs drop about 3 V, so 3.3 V leaves almost nothing for the resistor. Use a 5 V supply, or a smaller resistor and a red or green LED. - Turning the knob counts twice, skips, or goes the wrong way. The contacts bounce and polling is too slow. Rotary encoder
Use interrupts or a proper encoder library on pins CLK and DT. Use INPUT_PULLUP, since the contacts are open and shut to ground. Swap CLK and DT to flip direction.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.