For Agents
Reverse-chronological session log. Newest entries at top, grouped by date (
## YYYY-MM-DD). Each bullet: one piece of work, short summary, wikilinks to docs touched. Updated byobsidian-documenteron every project doc write. Read byhistorianat bootstrap.
2026-09-02
- Created the project. A repurposed industrial start/stop pushbutton station — ~6 × 12 cm black enclosure, gutted, ESP32 inside, green and red buttons retained, any function assignable to either. Design phase only: no code, no repo, nothing built. Buttons follow the industrial convention — green NO, red NC — chosen for failure behaviour, not aesthetics: a broken wire on an NO button reads as “never pressed” and is indistinguishable from a working idle button, while NC reads as permanently pressed. A stop that silently disables itself is the failure to design out — buttonbox
- Power decided: mains/USB, always on. The battery design was worked out at length and then abandoned. The payoff is latency, not convenience — a board that stays associated drops press-to-action from ~500 ms (the tuned battery figure) to ~30 ms. Consequence for wiring: both buttons GPIO → contact → GND on internal pull-ups, zero external components; green reads HIGH at rest / LOW pressed, red reads LOW at rest / HIGH pressed and is inverted in one line of firmware. Red’s 73 µA continuous through the ~45 kΩ pull-up is design-breaking on a cell (7× the whole MCU sleep budget) and irrelevant on mains — Wiring — final
- ⭐ Recorded the abandoned battery-era wiring because it is the correct answer and will be needed again. NC contact to 3.3 V with a 1 MΩ pull-DOWN, internal pulls disabled. It buys two things and the second is the non-obvious one: standby falls 73 µA → 3.3 µA (below the MCU’s own sleep floor), and it forces both pins to the same edge — because
esp_sleep_enable_ext1_wakeup()takes ONE trigger level for the entire pin mask, not per-pin, so a wake-on-LOW and a wake-on-HIGH button cannot coexist in oneext1config. Alternative is the ESP-IDF 5.x per-IO API (esp_sleep_enable_ext1_wakeup_io()) — one resistor vs an IDF version floor. General form already in A normally-closed switch bleeds current continuously — The Abandoned Battery Design — keep it, it is correct - ⚠️ Transport decided AND it deliberately breaks a house convention: WiFi → a new LAN-bound stdlib-Python listener, bearer-token authenticated (401 otherwise). tailnet-service-exposure-convention requires loopback-only binding behind a dedicated Tailscale node; an ESP32 cannot join the tailnet, so the listener must bind a real LAN address. The break is knowing and accepted, which makes the bearer token the entire security model. Rejected alternatives recorded, because “why not ESP-NOW” is the obvious future question: ⭐ ESP-NOW to a USB dongle on
telep-mainframewould have sidestepped both the tailnet problem and the 98.9 % 2.4 GHz congestion — it was shown to the user and declined, not missed — Transport — WiFi to a LAN-bound Python listener - ⭐ Wire format decided, and it is the load-bearing architectural choice: the device has no opinion. It sends
{"button": "...", "held_ms": N, "seq": N}on release and nothing else; all gesture semantics — short vs long vs double press, and toggle behaviour — live server-side in the daemon. Rationale: gesture meaning becomes editable config, so adding a long-press two months later is a Python dict edit, not a reflash of a device that has to be physically opened. Two free diagnostics fall out:seqgives lost-press detection, andheld_mscatches a stuck or broken switch — a 45 000 ms “press” is a fault, not a press, which is exactly what a broken NC wire produces. Double-press does not tax every press: fire on release immediately, then linger ~350 ms (~0.004 mAh, irrelevant), and the daemon buffers 300 ms ONLY for buttons that actually have a double binding — Wire Format — the device has no opinion - Latency budget recorded (naive ~2.6 s → tuned ~500 ms) even though it is moot on mains, because it applies to any battery ESP32 doing WiFi: boot 300 + AP scan ~1000 + associate 200 + DHCP ~500 + DNS 50 + TLS ~500 + POST 50. Tuned by caching BSSID and channel in RTC slow memory (survives deep sleep, and unlike NVS is not a flash write), a static DHCP lease, POSTing to a literal IP, and dropping TLS on a LAN-only path. ⚠️ Must keep a full-scan fallback — the vault has receipts for router reflash / channel / DFS churn (2026-08-26-router-reflash-mac-5ghz-dfs-and-bambu-offline, 2026-08-15-router-wifi-tuning-htmode-band-penetration) and a device that only tries its cached BSSID goes permanently offline after a router reboot — Latency Budget — naive vs tuned
- Mechanical constraint drove a board purchase. The enclosure is so full of pushbutton contact blocks that a standard ESP32 devkit only fits flipped on its back with pins facing up — which is why 3× XIAO ESP32C3 (
100.510.43, 0.6-inch pitch, 21 × 17.5 mm) are on the 2026-09-02 HESTORE cart. ⚠️ On order, NOT owned — ON ORDER — the 2026-09-02 Cart as Submitted - ❓ Two items left explicitly UNRESOLVED. (1) Is the enclosure plastic or metal? It decides XIAO C3 (onboard antenna) vs a 32UE with a U.FL external antenna — and the 32UE is the scarce antenna-capable stock, only 5 exist. Go look. (2) Toggle state ownership — proposed but NOT confirmed: the daemon queries the real target and inverts what it actually finds, with a stored-boolean fallback for targets with no status endpoint, logging loudly when it falls back. Rationale is that a stored boolean desyncs the moment anything else changes the target — Unresolved
- 🔴 Recorded a misread as a lesson. “Contactor” was initially read as a load-switching contactor, prompting a full mains-safety design (red hardwired NC in series with the coil, aux-contact sensing, relay in parallel with green). It was actually the contact blocks behind the pushbuttons — no mains, no load. The safe topology is kept for a future real-contactor project 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 — The “contactor” misread
- Initialized activity log and project folder.