PyRaspBeta

Rotary encoder

A rotary encoder is an endless knob — no end stops — that reports how far and which way it was turned, plus a push switch in the shaft. It costs $3 and is the natural input for menus, volume and setpoints, where a potentiometer would run out of travel. The shop's part is a KY-040 module: two contacts (CLK and DT) that open and close in quadrature as you turn, pull-up resistors already fitted, and a switch that shorts SW to ground when you press the shaft.

Rotary encoder

Pins

PinWhat it isType
CLKOutput A (pulled up)io
DTOutput B (pulled up)io
SWPush switch to GND (no pull-up on most boards)io
+Supply 3.3–5 V (for the pull-ups)power
GNDGroundground

Power the module from 3V3 through + — that same supply feeds its 10 kΩ pull-ups, so CLK and DT swing to 3.3 V, never 5 V. SW has no pull-up on most boards, so gpiozero's internal one is what holds it high. The module's own pins are marked the same way as the canvas: CLK, DT, SW, + and GND.

Wiring it in circuit.py

circuit.py
from wiring import *

pi = Pi()

encoder = RotaryEncoder()

pi.V3_3 >> encoder.plus        # powers the module's pull-ups: keep this 3.3 V
encoder.gnd >> pi.GND
encoder.clk >> pi.GPIO17       # output A
encoder.dt >> pi.GPIO27        # output B
encoder.sw >> pi.GPIO22        # push switch (internal pull-up in software)

gpiozero.RotaryEncoder needs both A and B, so two GPIOs minimum; the switch is a separate Button on a third. Any spare GPIOs will do — there is no special function required.

Driving it in main.py

main.py
from gpiozero import RotaryEncoder, Button
from time import sleep

knob = RotaryEncoder(17, 27, max_steps=0)          # max_steps=0: count without wrapping
press = Button(22, pull_up=True, bounce_time=0.02)

while True:
    print(f"steps {knob.steps}  pressed {press.is_pressed}")
    sleep(0.2)

Each detent is one full quadrature cycle, so steps counts detents: 20 per revolution on this part. threshold_steps and wrap in gpiozero tame wild counts, and encoder.when_rotated fires callbacks.

Electrical notes

  • The datasheet models 20 detents per revolution; each detent plays a complete quadrature cycle — four edges, one every 400 µs — with CLK leading DT when turned clockwise. Software that watches both pins can tell the direction from which edge comes first.
  • The module's pull-ups are 10 kΩ; contacts are about 1 Ω closed and bounce for 0.5–3 ms at the edges.
  • Power + from 3V3. From 5 V the module's pull-ups put 5 V on CLK, DT and SW, and the simulator raises an overvoltage warning that fails the judge's damage check. (Most KY-040 boards work at 3.3 V.)
  • gpiozero.RotaryEncoder debounces with bounce_time if you pass it; without it, the 0.5–3 ms chatter can add or lose a step. The push switch is a plain button and needs the same treatment.

Simulator vs real hardware

  • Turn the knob from the inspector (or drive the angle field with a wave) while the simulation runs; click the shaft to press the switch. The canvas knob follows the angle and the contacts play the quadrature cycle.
  • The model schedules one edge every 400 µs between detents, so a fast turn in Python sees a realistic burst of edges instead of an instant jump.
  • A real KY-040 usually reports four edges per detent and some firmware counts two detents per click; gpiozero's max_steps and threshold_steps absorb that. The simulator is the clean version.
  • Mechanical wear and missed edges at high speed are not modelled; the angle feed is authoritative.

Going deeper

Further reading