Firmware on the device¶
Firmware handles audio, cards, sensors, connectivity, power, and physical confirmation. It does not run the local response service or execute host commands.
States and controls¶
The state machine covers boot diagnostics, setup, idle/home, recording, submitting, response playback, pending confirmation, offline, error, and safe sleep or shutdown. At boot it reports the reset reason, selected profile, and detected chip, flash, and PSRAM without exposing credentials.
| Context | BOOT gesture | Result |
|---|---|---|
| Idle | Short press | Cycle cards |
| Idle | Hold then release | Record bounded audio and submit |
| Pending action | Short press | Confirm once |
| Pending action | Long press | Cancel |
V2 configures BOOT on GPIO0 and PWR on GPIO18 as active-low inputs with internal pull-ups. It polls at 10 ms and accepts a state change only after 30 ms. A BOOT hold reaches the recording intent at 700 ms; its release submits the intent. A short BOOT press renders the next diagnostic card. A 2 second PWR hold releases GPIO17, the battery power latch. PWR must be released once after boot before it can request shutdown, preventing the power-on hold from immediately cutting power. USB can keep the board powered after that action.
V2 BOOT holds capture up to 10 seconds of 16 kHz mono PCM into PSRAM. Release signs and sends the WAV to an enrolled gateway, then renders the returned card. The WAV buffer is released after the request and is never written to flash. Interaction card refreshes use one display worker, so e-paper refresh time does not block button polling.
V2 renders monochrome state illustrations for ready, recording, sending, processing, confirmation, and cancellation. After a returned card it plays a very quiet completion tone. Gateway failure cards and transport failures use a lower, distinct tone. These cues require the V2 audio path and do not change the persisted display card.
firmware/assets/icons/*.svg is the editable source for state artwork.
Run ./dev previews --write-header after editing an SVG. That regenerates the
tracked firmware header and 200 x 200 black-and-white PNGs under ignored
firmware/previews/. ./dev previews --check rejects a stale generated
header. The PNGs are layout previews, not e-paper waveform or ghosting
simulations.
State artwork¶
The published previews are generated from the same SVG sources that produce the firmware header. They contain only display artwork, no labels from a source sheet or embedded text.
| Ready | Recording | Sending |
|---|---|---|
![]() |
![]() |
![]() |
| Processing | Confirmation | Cancelled |
|---|---|---|
![]() |
![]() |
![]() |
What has been checked on V2¶
The current V2 image was built from the SVG-generated header and flashed through the backup-first helper. The device reports the V2 profile, 8 MB flash, 8 MB PSRAM, working e-paper initialization, active controls, and Wi-Fi connectivity. The flash verifier matched the bootloader, partition table, and application bytes after installation.
Cards and refresh¶
Home shows time, environment, battery estimate, Wi-Fi, and service state. Answer shows concise wrapped output. Tools shows configured integration status. Confirmation shows the exact operation, target, expiry, and controls. Offline and error cards keep the last useful content where possible.
Layouts are bounded for 200 x 200 monochrome output. Partial refreshes are followed by configurable full refreshes to manage ghosting. A stale confirmation card must never remain actionable.
Connectivity, audio, and power¶
Secure provisioning requires a private per-device proof of possession and never logs it. Public builds stop setup when it is absent. Wi-Fi credentials use ESP-IDF NVS; NVS encryption remains gated until a hardware-specific key protection scheme and eFuse slot are provisioned. Service credentials stay off public firmware. Requests and audio are bounded and timed out.
Gateway discovery uses authenticated UDP broadcast. Set
INKMATE_GATEWAY_INTERFACE in ignored .env to select one LAN on a multi-homed
host. scripts/start-device-gateway.sh derives the interface's current IPv4
address and subnet. HTTP binds to that address. UDP receives broadcasts on the
host socket but accepts and replies only to senders in that subnet, then signs
the nonce, URL, and UTC time with the enrolled device secret. Firmware and
tracked configuration never store a fixed LAN IP address.
Enrolling a V2 device¶
Run the helper once from a trusted gateway host:
./scripts/enroll-v2-device.sh desk-v2
docker compose -f compose.device.yaml up --build
./scripts/build-firmware.sh v2
The helper creates ignored firmware/sdkconfig.private and .env files with a
unique device ID and secret. It refuses to overwrite either enrollment value.
If .env already contains the selected gateway enrollment, use
./scripts/configure-v2-enrollment.sh [DEVICE_ID] to create only the ignored
firmware overlay. The optional ID is required when more than one enrollment is
configured. An enrolled V2 build is written to ignored firmware/build/v2-private.
scripts/start-device-gateway.sh starts the same service and records an unused
local port in ignored .env if 8080 is already occupied.
compose.device.yaml uses host networking on Linux so UDP discovery receives
the selected LAN broadcast. Do not add a LAN IP to source, tracked
configuration, or firmware defaults.
When INKMATE_STT_BACKEND=faster-whisper, the device gateway stores the local
model in a Docker volume. The first start downloads the selected model; later
starts reuse it. No audio request is retained after the gateway returns a card.
Use scripts/verify-audio-capture.sh /dev/ttyDEVICE during bench verification. It records only filtered capture diagnostics under ignored hardware-reports/; it does not write audio to disk.
Docked mode favors responsiveness; battery mode limits radio and audio windows. Battery percentage is unavailable until calibrated. Automatic deep sleep and OTA reboot remain off until every applicable battery reset, wake, and rollback scenario passes.
OTA eventually uses a validated image, inactive partition, health confirmation, and rollback. A successful download alone does not make a battery reboot safe. USB ROM-download recovery must remain possible if provisioning, NVS, or OTA state is corrupt.





