rotary encoder/Reading it/07. Why polling misses steps
Reading it · 07 of 10

Why polling misses steps

The first sketch has to read CLOCK within a quarter of a cycle of each fall, or DATA has already moved and the direction comes out wrong. Turned briskly, a quarter of a cycle is a few milliseconds. A loop with a delay() in it, or a slow print, is late, and the count goes wrong in ways that look like a faulty encoder.

A quarter of a cycle to get there

The first sketch reads CLOCK once each time round loop(). When it sees CLOCK LOW for the first time, it reads DATA. That read is only right if it happens before DATA falls too, a quarter of a cycle after CLOCK.

How long a quarter of a cycle lasts depends on how fast the knob turns. At 20 clicks a turn, the figure most EC11 parts give, two turns a second is 40 cycles a second: 25 ms a cycle, 6.25 ms a quarter. A quick flick of five turns a second leaves 2.5 ms.

Why polling misses steps
Turning
One pass of loop() takes2 ms
Cycles turned
10
Counted
10
Quarter-cycle
6.3 ms
Every cycle counted. The loop reads CLOCK every 2 ms, and DATA stays HIGH for 6.3 ms after CLOCK falls, so the first read after each fall always lands in time. Polling is fine as long as loop() stays this quick.

Start with a 2 ms loop: every cycle counts, at every speed. Now drag the loop time up with the knob turning briskly. Past about 6 ms some reads land after DATA has fallen, and those cycles count backwards. Past half a cycle, whole cycles fall between two reads and are never seen.

Why it looks like a broken encoder

The failure is not a missing click here and there. It is the count stalling, jumping back, or going the wrong way while the knob goes the right way, and only when someone turns it quickly. Turned gently on the bench, the same sketch is perfect. That is the worst kind of bug: it shows up only in use, and it points at the hardware.

The usual culprits are the ones any sketch collects: a delay() left in to slow the serial output down, a display that takes a few milliseconds to redraw, a sensor read that waits for its answer. None of them is wrong in itself. Each one makes loop() late for the encoder.

Two ways out

The first is to keep loop() short and never block, as the XL LED's blinking without stopping does for a different reason. It works, and it breaks again the next time somebody adds something slow.

The second is to stop asking. An interrupt makes the chip run a small function the moment CLOCK falls, whatever loop() is doing at the time, even if it is inside delay(). The read of DATA happens at the right instant by construction. That is the next article.

When it does not work

It counted perfectly until I added a display.

Updating a display can take several milliseconds a pass, and that time comes out of loop(), where the encoder is being read. Turned slowly it keeps up; turned briskly it misses. Move the counting into an interrupt, which the next article does.

Can I just make loop() faster?

Yes, and for a slow knob and a short loop that is enough: the figure counts every cycle with a 2 ms loop at two turns a second. It stays fragile, because the next thing anybody adds to loop() slows it down again, and nothing tells you when it crosses the line.

Why does it count backwards rather than just missing clicks?

A late read still sees CLOCK LOW, but by then DATA has fallen too, so the sketch concludes DATA fell first. That is the wrong direction. A read later still, after CLOCK has risen again, misses the cycle altogether.

Where this goes next

Let CLOCK's fall interrupt the sketch, so the count never waits for loop().

Counting with an interrupt

Edit this page — content/books/rotary-encoder/why-polling-misses-steps.mdx

Community

Questions about this product

See what other owners have asked, and read their solutions.

Ask a question ↗

Rotary Encoder

Loading discussions…

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse Modules and blocks on the forum