Mission
Build this
Build a legible display for Gate Keeper’s newest count, with clear waiting, current, stale, and recovered states.
Borrowed parts are labelled below and have a fallback. The registered device ID is fixed; sensing and behaviour decisions remain yours.
Bill of materials
Parts
| Source | Part | Fallback |
|---|---|---|
| ● kit | Four-digit TM1637 display | — |
| ● kit | RGB LED and 220 Ω resistors ×3 | — |
| ● kit | Push button | — |
Disconnect USB before rewiring. Motors, the relay, and the servo need appropriate power and a shared ground. Never drive an actuator from the ESP32 3V3 pin.
Backup assignment · your invitation
Turn a distant event stream into a trustworthy public object
The Turnstile Tally does not count passages. Gate Keeper does. Your device receives that shared count, remembers what it knows about it, and makes freshness visible beside the number.
This sounds like a display project, but its real subject is distributed truth. What should the digits show before the first message arrives? What if Wi-Fi disappears? What if the source restarts at zero? A resilient reader distinguishes all of those stories.
Gate Keeper owns the count. Your button, display, restart, and recovery may acknowledge or present it, but they never alter the authoritative total.
Meet the idea
A number without its age is only half a message
MQTT delivers messages while devices are connected. After your tally boots, it may need to wait for Gate Keeper’s next event unless you deliberately and correctly preserve the last accepted count locally.
Store two pieces of information in memory: the newest accepted count and when it arrived. The digits answer “what was the value?” while the light answers “how much should I trust it now?”
A real zero, no value yet, and an old remembered zero are different states. Designing distinct outputs for them prevents a neat display from making a false claim.
Value
The newest valid cumulative count received from Gate Keeper.
Freshness
Time since that valid message arrived, compared with your written stale threshold.
Authority
The device allowed to create or change the underlying fact. Here, that is Gate Keeper.
- in the worldA passage occursGate Keeper decides whether the distance story is one event.
- the partGate KeeperThe source increments its cumulative accepted count.
- electricalMQTT messageA count arrives now, later, twice, or not at all.
- in the codeValue + arrival timeThe tally validates and remembers both.
- on the spineDisplay statusDigits present the count; the tally publishes its own state.
The tally adds presentation and freshness, never a second competing count.
The lovely trick
Waiting, current, stale, and recovered are four different stories
Before the first valid count, dashes are more truthful than zero. While messages are recent, the display is current. After silence exceeds your threshold, the last value may remain visible only if the light clearly marks it stale.
Recovery deserves a brief state of its own. It tells a nearby viewer that the old number has just regained fresh evidence, especially if the source count changed or reset while disconnected.
- 01WaitingNo accepted count has arrived; show dashes or another non-number.then, first valid message,
- 02CurrentShow the newest count with the fresh colour.then, freshness expires,
- 03StaleKeep or hide the remembered value, but mark its age unmistakably.then, new valid message,
- 04RecoveredA new valid count arrived after staleness; reconcile before returning current.
Back to the start: After a short visible recovery, return to current and continue measuring age.
The display is a small state machine driven by message arrival and elapsed time.
Gate Keeper may restart or establish a new baseline. Do not silently add an offset to make the public number look continuous. Show a recovery or reset indication and preserve the source value exactly.
Wiring
Make four display states before subscribing to live data
Start with a local rehearsal: show waiting, current, stale, and recovered using fixed example values. Ask another person what each means. Only then connect the states to message arrivals and clocks.
The power rule: The reference TM1637 module uses 3V3. Each RGB channel needs its 220 Ω resistor. The button closes to ground and uses the ESP32 internal pull-up.
Bench referenceOpen the exact wiring map
| ESP32 pin | Part | Part marking | Carries |
|---|---|---|---|
3V3 | TM1637 display | VCC | 3V3 power — verify the module pin order |
GND | TM1637 display | GND | Ground |
GPIO 18 | TM1637 display | CLK | Digital in/out |
GPIO 19 | TM1637 display | DIO | Digital in/out |
GPIO 25 | RGB LED | R via 220 Ω | Digital in/out — common-cathode reference |
GPIO 26 | RGB LED | G via 220 Ω | Digital in/out |
GPIO 27 | RGB LED | B via 220 Ω | Digital in/out |
GND | RGB LED | common cathode | Ground |
GPIO 14 | Push button | one side | Digital in/out — configure INPUT_PULLUP; local acknowledgement only |
GND | Push button | other side | Ground |
Verify the TM1637 module pin order before power. A bare seven-segment display is not pin-compatible and needs appropriate current limiting and a verified driver circuit.
- 3V3 power
- Ground
- Digital in/out
The button acknowledges a local message or explanation. It must never publish a replacement count or send a command that changes Gate Keeper.
Give it character
What should honesty look like from three metres away?
Digits may carry the exact value while colour carries trust. You can choose another redundant cue, but a visitor should never need colour vision alone to distinguish waiting from stale.
Your team decides:
- How long may a count remain current after its last event message?
- What appears before the first valid value: dashes, a word-like pattern, or a blank with a labelled light?
- Does stale data remain visible, blink slowly, or disappear?
- How are reset, overflow beyond four digits, malformed input, and recovery shown?
Write these decisions in plain language before turning them into code. A clear rule is easier to test, explain, and change.
Your field adventure
Can first-time viewers tell what the display really knows?
Test interpretation, not just wiring. Use a fixed rehearsal in which the digits sometimes show zero, sometimes show an old value, and sometimes have no value at all.
- Define the four outputs.
Draw waiting, current, stale, and recovered before coding. Include a genuine current zero.
- Rehearse message history.
Run cold boot, first message, several updates, planned silence, backward reset, overflow, and recovery.
- Invite five fresh viewers.
Ask what the number means and whether it is current. Do not explain until each answer is recorded.
- Repair the hardest distinction.
Change one label, light, blink, or digit pattern, then repeat that scene with new trials.
| Scene | Digits | Freshness cue | Viewer interpretation | Correct? | Confusion / revision |
|---|---|---|---|---|---|
| no message yet | ___ | ___ | ___ | yes / no | ___ |
| current real zero | 0 | ___ | ___ | yes / no | ___ |
| remembered value is stale | ___ | ___ | ___ | yes / no | ___ |
When it gets dramatic
A tidy number can hide a messy history
The display begins at zero after every boot
Zero is being used as a default before evidence arrives. Use an explicit waiting state and a separate has-value flag.
The tally never becomes stale
You may be updating the arrival clock inside the display loop. Change it only when a valid Gate Keeper message arrives.
The count jumps backward
The source probably reset or rebased. Preserve the received value, mark recovery or reset, and document the event rather than inventing continuity.
The same message makes the display flicker
MQTT delivery or reconnect may repeat a value. Re-render only when state changes, while still accepting the new arrival time if your rule permits.
Pressing the button changes the public total
The reader has accidentally become a second authority. Restrict the control to local acknowledgement, brightness, or help text.
Choose your direction
What kind of distributed-systems team will you become?
The freshness designers
Make age, staleness, and recovery understandable without hiding the last useful value.
The restart historians
Explore cold boot, local persistence, source reset, and the honest limits of continuity.
The public interpreters
Focus on zero, waiting, stale, and overflow through careful first-viewer trials.
Each direction is real engineering. Pick the question that keeps your team curious.
A calm way through the build
Collect four small wins
- tt-01 says hello and shows dashes.
No source message means no invented count.
- A fixed example can visit all four display states.
Test the visual language before MQTT.
- One Gate Keeper message becomes a current value.
Store the value and its arrival time together.
- Planned silence becomes stale and a new message recovers.
Add the viewer test after the state logic is frozen.
When a new step fails, return to the last small win. The fault is now somewhere in the few wires or lines you just added.
The garden handshake
Share what you found
These names are the rigid part of the project. They let another team find your work without knowing what you called the variables in your code.
Tally operating state
garden/entrance/tally/tt-01/statusUnit: enum
Listen beyond your own device.
garden/entrance/counter/gk-01/countDisplay the newest valid cumulative count exactly as Gate Keeper publishes it. Persist it locally or wait for a new event after boot; the device must not query or replay the browser API.
Finish line
Ready to introduce to the garden
tt-01 stays online and publishes its own waiting, current, stale, recovered, or error status.
The reject feed stays clear after the final code starts.
The tally reads Gate Keeper and never creates, offsets, or resets its authoritative count.
Waiting, real zero, stale memory, recovery, source reset, and overflow have documented behaviour.
Five first-time viewers are tested and the build log reports interpretation errors plus one revision.
The display and LED are safely current-limited, controls are local only, and the device label can be scanned.
The Turnstile Tally earns trust by showing not only a number, but the boundary between what it knows, remembers, and is still waiting to learn.