The BT-off custom slave (2.12.12) can't survive the C6's OTA rollback
protection: it flashes, the C6 boots it, reverts to 2.12.11, host
reflashes -> ~30s reboot loop, worse than the wedge. BLE-coex
hypothesis is UNTESTABLE via OTA (custom slaves won't stick; needs
direct C6 UART flash, out of scope tonight). Reverted to the
self-healing 2.12.11 baseline: ~5-13min MTBF, 15s auto-recovery.
That is the shippable overnight state.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The wedge clock is immune to every network-layer change (~300s
regardless of bus width, clock, power-save, DHCP). A slave-side
periodic starver remains; the C6's BLE controller (BT_LE_SLEEP_ENABLE=y
in stock slave, shares the radio/RF arbiter with Wi-Fi) is the prime
suspect. DeskLock uses no Bluetooth, so the stock slave carries a
coexistence scheduler we can delete outright. Custom slave 2.12.12
(CONFIG_BT_ENABLED=n, ESP_HOSTED_CP_BT=n), OTA'd over SDIO. If the
metronome stops, BLE coexistence was starving the SDIO peripheral.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The wedge metronome (~300s: 297/297/322/437s across bus widths,
clocks, and power modes) matches FRITZ DHCP T1 renewal at half of a
10-min lease. Every wedging build ran an active dhcp client. Static
addressing removes the renewal TX path entirely; if the metronome
dies, root cause #2 is the DHCP renew packet through hosted's write
path. (2.9.7 experiment abandoned: slave image bootloops/rolls back
on this C6; version matrix documented for upstream.)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1-bit bus alone did not stop the sdio_write timeout wedge (recurred
at ~5min uptime). Modem power save is the new prime suspect: the C6
napping mid-SDIO-transaction matches the minutes-scale periodicity.
Always-powered appliance -> PS_NONE costs ~50mW and removes the
entire failure class if correct. SDIO clock also halved to 10MHz.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Night-watch caught the instability in the act at 7min uptime:
E H_SDIO_DRV: sdio_write_task: Failed to send data: 258 (timeout)
E H_SDIO_DRV: Unrecoverable host sdio state -> SW_CPU_RESET
i.e. esp-hosted-mcu#167. Known mitigation: 1-bit SDIO bus (#148),
still ~80x voice bandwidth. Also: recovery reboots no longer ring
the gong (esp_reset_reason gate) - a butler doesn't bong at 3am.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Root cause of the dead data path (assoc/scan/RPC fine, zero data
frames): esp-hosted 1.4.x is formally incompatible with IDF 5.5
(esp-hosted-mcu#47) and the factory C6 slave firmware was ancient
(couldn't even answer a version RPC). Waveshare's examples pin 1.4.* —
do not follow them.
Fix:
- host: espressif/esp_hosted ^2.12 (+ esp_wifi_remote 1.6)
- slave: 2.12.11 network_adapter.bin embedded in the app (c6_ota.c
streams it to the C6 over the SDIO RPC channel at boot when the
reported version is < 2.x; ~10s, no wires, idempotent)
- RAM diet for hosted 2.x's footprint (MEMPOOL_PREFER_SPIRAM,
reduced WIFI_RMT buffers) — without it internal SRAM famine
boot-loops in xTaskCreateStaticPinnedToCore before app_main
- conservative 20MHz SDIO clock for first verified data path
Verified on hardware: DHCP lease (even that healed), 8/8 pings to
router and tower-of-joy at 1-5ms, WebSocket to the gateway connected,
device registered at /devices as desklock-p4 with fw version.
Also: wifi_diag.c L1 SoftAP diagnostic mode (DESKLOCK-DIAG) with
review fixes, boot ping ladder in net.c, static-IP fallback,
face_status() line, docs for the whole failure taxonomy.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- gateway: GET /devices registry (per-IP connect count, timestamps,
device/fw from new 'hello' protocol event); graceful utterance
errors -> error event; uvicorn ws-ping-timeout 120s
- firmware: hello on WS connect; static 192.168.86.53 fallback after
15s without DHCP; scan-on-boot removed (kept for diagnostics);
ping_interval 8s; fonts regenerated --no-compress (compressed
glyphs render blank with LV_USE_FONT_COMPRESSED off); 8MB factory
partition; montserrat_28; LVGL lock timeout semantics fixed
- docs: sauron endpoint plan, power ladder, versioning policy
Known issue: 'Outside' AP association succeeds but no L2 traffic
flows (no DHCP, no ARP) — worked once at 22:05 then never again;
ToJ side verified clean. Investigation ongoing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The device now runs the complete face contract on glass: seven states
with glyph expressions (custom 140px face font), matrix rain as pooled
label streams (22px DejaVu+Noto katakana font), rage kaomoji frames
(64px), orbit arc + elapsed counter wait cues, blink/talk/thought
animations, idle clock (SNTP, Europe/Amsterdam), and the power ladder
(active/ambient 35%/dormant 5% with rain parked).
Voice path: ES7210 mic capture task streams 16k PCM over
esp_websocket_client to the gateway; reply PCM buffers to PSRAM and
plays via the audio task; touch-to-talk (tap to speak, tap to send).
Protocol mapping per docs: thinking->effort, audio->speaking,
error event->rage (auto-composes after 2 loops), WS loss->error face.
Bring-up fixes: 8MB factory partition (fonts overflowed 1.5M),
bsp_display_lock(0) is try-lock in this adapter (use UINT32_MAX),
gong synth yields to feed IDLE0, wifi scan diagnostics (found SSID
case mismatch), esp_websocket_client pinned ~1.3 (1.4 needs IDF>5.5).
Verified on hardware: boots, connects to Wi-Fi and holds an open
WebSocket to the gateway. Sauron (iMac TTY endpoint + ops console)
planned in architecture.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
User verdict: audible and butler-non-intrusive on the bare 2W speaker.
Recorded as the house sound signature in architecture.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Additive gong synthesis: seven inharmonic partials with per-partial
decay (fundamental 165Hz rings 2.6s, highs die in 0.2s), a detuned
pair for shimmer, 45ms distant attack, and three feedback-comb
reflections (95/210/370ms, net gain .93) for a dense stone-space tail.
Normalized to -12dBFS at codec volume 58: quiet but voluminous.
Synthesized into PSRAM at boot (~4s), plays 5s, blocking is fine for
bring-up — audio moves to its own task in phase 2.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
C5->E5 half-second chime with fade-out at boot, right after the face
renders. Codec opens in slave mode, esp_codec_dev write path works.
Phase 1 bring-up is now complete: display, touch, audio, 200MHz PSRAM.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- firmware boots to a rendered LVGL face in 1.57s (eyes + smile + tag)
- sdkconfig: CONFIG_IDF_EXPERIMENTAL_FEATURES=y unlocks SPIRAM 200MHz
(silently degraded to 20MHz before -> MIPI-DSI underrun -> LVGL lock
starvation -> task watchdog); mirrors official 08_lvgl_demo_v9 config
- architecture.md: power management section (user prime concern) —
active/ambient/dormant/night ladder, levers, hard edges, <=300ms wake
- AGENTS/CLAUDE: replace stale bring-up warnings with verified commands
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
DeskLock gives the Tatlock butler a face and voice on a Waveshare
ESP32-P4-WIFI6-Touch-LCD-3.4C round display in the living room.
- firmware/: ESP-IDF project targeting esp32p4 with the Waveshare XC BSP
- gateway/: FastAPI voice bridge (faster-whisper STT, Tatlock chat, Piper TTS)
- docs/architecture.md: component design and device<->gateway WS protocol
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>