Home > Tutorials > Rotary Encoder Menu Reference: An Event and State-Machine Approach

Rotary Encoder Menu Reference: An Event and State-Machine Approach

Build a rotary-encoder menu as a small state machine with three separate stages: turning the encoder changes a position, pressing commits or cancels, and rendering draws only what changed. Qualify every event first, and define what happens when a click is missed or bounced.

Separate the three stages

Most encoder-menu bugs come from mixing three jobs in one place. Turning moves a cursor. Pressing selects. Drawing shows the current state. When those are tangled, a bounce in the encoder can trigger a redraw mid-selection, or a slow draw can cause a turn to be dropped. Keeping them separate makes each one testable on its own.

Treat movement as qualified events

The official Arduino Debounce example points out that contact bounce can turn one physical transition into multiple state changes, and it qualifies a changed state using elapsed-time checking before accepting it. An encoder’s contacts behave the same way: unqualified, one detent can register as several steps. The attachInterrupt() reference adds a second guardrail, to keep interrupt service routines short, so an encoder interrupt should set a flag or adjust a counter and leave the menu logic and drawing to the main loop.

Pick the right pins for the board

The attachInterrupt() reference lists pins 2 and 3 as interrupt-capable on the UNO Rev3 and notes that mappings vary across boards; it recommends digitalPinToInterrupt(pin). So do not hard-code a pin number. Confirm the actual board’s interrupt-capable pins first.

Bound the values

A menu position should have a defined range and a defined behaviour at the ends, either wrap or clamp. Leaving it unbounded lets a run of qualified steps push the cursor off the menu. Direction detection depends on the specific encoder’s contact ordering, so treat rotation counting as something to verify against your encoder’s datasheet rather than a solved decoder.

Stage Input Its job Guard rail
Movement qualified encoder transitions maintain a bounded position value debounce each transition; reject impossible jumps
Selection a qualified press commit or cancel the current item one action per qualified event
Rendering current menu state draw only when state changes decouple drawing speed from input handling
Symptom Likely stage First thing to check
One click jumps several items movement is each transition debounced before counting?
Direction reverses unexpectedly movement encoder contact ordering, against its datasheet
Menu flickers or redraws constantly rendering draw only on state change
Clicks sometimes do nothing selection or ISR is the ISR short, with work deferred to the loop?

Designing the menu, in order

  1. Confirm the board’s interrupt-capable pins and use digitalPinToInterrupt().
  2. Qualify every encoder transition with elapsed-time debounce before counting it.
  3. Keep the ISR short: set a flag or adjust a counter, nothing more.
  4. Give the menu a bounded position with explicit wrap-or-clamp behaviour.
  5. Separate selection from rendering, and redraw only on change.
  6. Test the missed-click and double-count cases deliberately, since those are where menus break.

Reader questions

Should I poll the encoder or use interrupts?

Interrupts are good at catching short bursts, provided the routine is short. Polling is simpler if your loop is fast enough to catch every transition without missing one.

How do I stop one click registering as several?

Apply the Debounce pattern: qualify the changed state with elapsed-time checking before accepting it, exactly as the official example does.

Is a switch debounce example enough for a quadrature encoder?

It handles bounce, but it is not a validated quadrature decoder. Direction detection depends on the encoder’s contact ordering and electrical behaviour, so check your encoder’s datasheet.

What should happen at the end of the menu?

Define it: either wrap to the other end or clamp. Leaving it undefined is how cursors wander off the menu.

The qualified-event idea here mirrors the counting logic in the wheel pulse-sensing reference.

Documentation

Documentation note: the sources used here cover interrupt usage and switch debounce only. No independent testing was done, and the encoder’s contact ordering and electrical specifications are not covered. This is user-interface logic, not a sole safety control.


About The Author

Ibrar Ayyub

I am an experienced technical writer holding a Master's degree in computer science from BZU Multan, Pakistan University. With a background spanning various industries, particularly in home automation and engineering, I have honed my skills in crafting clear and concise content. Proficient in leveraging infographics and diagrams, I strive to simplify complex concepts for readers. My strength lies in thorough research and presenting information in a structured and logical format.

Follow Us:
LinkedinTwitter
Scroll to Top