For Agents
Living index of themes for this project. Each H2 is a topic; bullets are wikilinks to related notes. Updated by
obsidian-documenterwhen documenting work. Read byhistorianat bootstrap. Topics kept alphabetical.
Buttons & Switch Wiring
- NC convention — green NO, red NC, the industrial start/stop convention. Chosen for failure behaviour: a broken wire on an NO button is silent (reads as “never pressed”, indistinguishable from idle); NC fails loud (reads as permanently pressed). A stop that silently disables itself is the failure being designed out. The SS-5GL is SPDT, so NO/NC is a wiring choice, not a purchase
- Wiring — final — both buttons GPIO → contact → GND on internal pull-ups, zero external components. Green HIGH at rest / LOW pressed; red LOW at rest / HIGH pressed, inverted in one line of firmware. Red’s 73 µA continuous is irrelevant on mains and design-breaking on a cell
- A normally-closed switch bleeds current continuously — the general form of the same rule
Cross-Project
- esp32 — the cross-project ESP32 silicon/toolchain/wiring reference; read before touching the board
- esp32-battery-deep-sleep — where this project’s NC-bias,
ext1polarity and WiFi-latency findings live in generalised form - inventory — the XIAO C3s, SS-5GL switches and JST-PH parts are on order, not owned
- lightaim — the other ESP32 human-input project; single-button UX precedent
- hardcut — same instinct as “a software stop is not a stop”: a 74HC74 hardware interlock makes the dangerous state physically unreachable rather than merely checked in firmware
Firmware / Protocol Design
- Wire Format — the device has no opinion — ⭐ the load-bearing decision. The device sends
{"button", "held_ms", "seq"}on release and nothing else; all gesture semantics live server-side. Rationale: gesture meaning becomes editable config — a long-press added two months later is a Python dict edit, not a reflash of a device that has to be physically opened.seqgives free lost-press detection;held_mscatches a stuck or broken switch (a 45 000 ms “press” is a fault, not a press) - Double-press does not tax every press — fire on release immediately, then linger ~350 ms (~0.004 mAh); the daemon buffers 300 ms ONLY for buttons that actually have a double binding, so single-binding buttons pay zero added latency
Mechanical & Enclosure
- Mechanical Constraint — why a XIAO — the enclosure is so full of pushbutton contact blocks that a standard devkit only fits flipped on its back, pins up. Hence the 0.6-inch-pitch XIAO ESP32C3 (21 × 17.5 mm)
- Breadboards — the 5-column geometry rule — the same 0.6-inch pitch also fixes the breadboard row problem
Power & Low Power
- USB, always on — mains, always on. The payoff is latency, not convenience: staying associated drops press-to-action from ~500 ms to ~30 ms
- The Abandoned Battery Design — keep it, it is correct — ⭐ kept deliberately. NC to 3.3 V + 1 MΩ pull-DOWN, internal pulls off: standby 73 µA → 3.3 µA, and it forces both pins to the same edge because
esp_sleep_enable_ext1_wakeup()takes ONE trigger level for the whole pin mask, not per-pin. Alternative: the ESP-IDF 5.x per-IO API - Latency Budget — naive vs tuned — ~2.6 s → ~500 ms via RTC-slow-memory BSSID/channel cache, static lease, literal IP, no TLS on LAN. ⚠️ Full-scan fallback is mandatory given documented router reflash / DFS churn
Safety & Failure Modes
- The “contactor” misread — 🔴 “contactor” was misread as a load-switching contactor, producing a full mains-safety design for a project with no mains and no load (it was the contact blocks behind the pushbuttons). The topology is kept because the principle generalises: ⛔ red must stay hardwired in series with the coil so it physically breaks the circuit regardless of firmware — a software stop is not a stop
Transport & Networking
- Transport — WiFi to a LAN-bound Python listener — ⚠️ deliberately breaks tailnet-service-exposure-convention: an ESP32 cannot join the tailnet, so the listener binds a real LAN address instead of loopback-behind-a-Tailscale-node. The bearer token (401 otherwise) is therefore the entire security model. ⭐ Rejected: ESP-NOW to a USB dongle on
telep-mainframe, which would have sidestepped both the tailnet problem and the 98.9 % 2.4 GHz congestion — shown to the user and declined, not missed - tailnet-service-exposure-convention — the house rule being broken
Unresolved
- Unresolved — ❓ (1) Is the enclosure plastic or metal? It decides XIAO C3 (onboard antenna) vs a 32UE + U.FL external antenna, and the 32UE is the scarce antenna-capable stock — only 5 exist. Go look. ❓ (2) Toggle state ownership — proposed, NOT confirmed: the daemon queries the real target and inverts what it finds, with a stored-boolean fallback that logs loudly, because a stored boolean desyncs the moment anything else changes the target