What is a Rotary Encoder?
A rotary encoder is a knob that turns forever in either direction and reports every click. It has no end stops and no absolute position — it doesn't know where it's pointing, only that it just moved one click clockwise or counterclockwise. Your code counts the clicks and decides what they mean.
That's the key difference from a potentiometer-style knob: a pot reports an absolute position on a fixed arc; an encoder reports relative motion, endlessly. It's why volume knobs on modern car stereos and mice scroll wheels are encoders.
Your Kit's Encoder
A 5-pin module with 20 clicks (detents) per full rotation — 18° of travel per click. The detents are little mechanical notches, so each click is a crisp, countable event.
| Pin | Connects to | |-----|-------------| | CLK | Pin 2 | | DT | Pin 3 | | SW | Built-in push switch — unused in the missions, leave unconnected | | + | 5V | | GND | GND |
How Quadrature Works
Inside are two switches (CLK and DT) rubbing against the same notched wheel, mounted slightly offset. Turning the shaft produces two square waves, out of phase:
turning clockwise →
CLK ──┐ ┌────┐ ┌────
└────┘ └────┘
DT ────┐ ┌────┐ ┌──
└────┘ └────┘
The information is in the order: if CLK changes first, you're turning clockwise; if DT changes first, counterclockwise. This trick — two offset signals encoding direction — is called quadrature encoding, and it's the same scheme inside mice, CNC machines, and robot wheel sensors.
Why Interrupts Are Mandatory
Those pulses happen whenever your hand moves — they don't wait for your loop() to come around. If the loop is busy updating a display and a pulse comes and goes unseen, that click is simply lost, and your count drifts.
The fix is hardware interrupts: the chip pauses whatever it's doing the instant CLK or DT changes, runs a tiny handler, and resumes. On the HERO board only pins 2 and 3 support interrupts — which is exactly why the missions insist the encoder goes there and nowhere else.
attachInterrupt(digitalPinToInterrupt(2), updateEncoder, CHANGE);
attachInterrupt(digitalPinToInterrupt(3), updateEncoder, CHANGE);
The BasicEncoder Library
The missions use BasicEncoder to turn raw pulses into clean click counts:
| Function | What it does | |----------|--------------| | service() | Processes pin changes — call it from the interrupt handler | | get_change() | Clicks since you last asked (+CW / −CCW). Resets itself after reading | | get_count() | Running total of all clicks | | reset() | Zero the count | | set_reverse() | Flip which direction counts up |
The pattern: interrupts call service(), your loop polls get_change() and reacts only when it's non-zero.
The SW Pin
Push the knob straight down and it clicks — that's a momentary switch on the SW pin. The missions leave it unconnected. If you want it for your own builds: wire SW to any digital pin with pinMode(pin, INPUT_PULLUP), and it reads LOW while pressed.
Where You'll Use It
| Mission | What it does | |---------|--------------| | Day 18 | Ascent-rate control for the climb off the ocean floor — one click = one meter, with HOLd/nOPE lockouts on the depth gauge | | Day 19 | The final ascent to the surface |
Common Mistakes
- Skipped or erratic clicks: CLK/DT aren't on pins 2 and 3, or
service()isn't being called from interrupts - Direction is backwards: swap the CLK and DT wires — or just call
set_reverse() - Counts jump 2 or 4 per click: steps-per-detent mismatch; the constructor's
stepsparameter (default 4) should match your encoder - Count drifts when you're not touching it: loose ground wire — the signals need a solid GND reference