Skip to content

Local Wi-Fi configuration

Local configuration

Set the password for the ears' own AP from either phone app under Settings → Wi-Fi, or Set up recovery Wi-Fi in recovery. Pair over bonded BLE with a fresh headset click; enter 8–63 printable ASCII characters or generate a password. Blank NVS starts without an AP password and can be provisioned this way. Recovery can also accept a normal app directly over BLE without Wi-Fi setup.

A private Espurr cat ears → Private configuration AP password menuconfig value remains an optional fallback; keep generated configs/passwords private. The separate versioned AP record overrides legacy settings and private defaults. The browser has no password editor. Passwords are stored in NVS, omitted from public JSON/logs and not saved by the provisioning UI. NVS is not encrypted. See the storage contract.

With a valid effective password (saved NVS settings take precedence over Kconfig), triple-click on a single-button profile, or hold v2 button 1 for five seconds, to switch between BLE control and Wi-Fi configuration. Boot-held recovery also requests Wi-Fi. After a boot hold, release and make the next press a five-second hold to run LED diagnostics while keeping the AP available. A shorter first press cancels that sequence. Join WPA2 AP espurr-XXXXXX, then open http://192.168.4.1/. The page is embedded and works without internet. No mDNS or captive-portal discovery is required. Wi-Fi is initialized on first configuration use and stopped on exit. The AP closes after five minutes with no associated Wi-Fi client by default (remote.timeout_s, editable on phone). It stays open while any client is connected, even with the browser closed; after the last disconnect, a fresh timeout begins. HTTP polling does not reset that timer. Active OTA suspends expiration and a failed upload gets a fresh window once the transfer ends and no client remains. LED/motion operation continues while configuring. Radio initialization briefly clears/pauses the ears to avoid flash-sensitive RMT activity.

Failed AP teardown retains exclusive Wi-Fi ownership and retries on the supervisor's existing 250 ms cycle. HTTP must stop before Wi-Fi; once HTTP stops successfully, only Wi-Fi shutdown is retried. BLE and a new AP entry wait until cleanup completes. A configuration request during incomplete teardown does not cancel cleanup; request configuration again after it completes. Active OTA prevents closure, including retries.

Apply & Save validates the entire submission before applying it. Runtime application and NVS save failures are distinct: a save failure may leave a new setting active but unsaved, explicitly shown as applied/save failed. Button changes coalesce after ten idle seconds. Stored records use a split storage layout: controls, saved palettes, calibration and hardware are compared/written separately, with a final marker selecting one coherent snapshot. Legacy records migrate on their next save. JSON import/export remains schema 22; the NVS selection marker is schema 23. Calibration is bound to the target, sensor, bus pins and address; a different identity clears calibration while preserving other settings. Older calibration is attributed to the original Atom MPU6886 bus. Version 1 records migrate in memory, inferring long-edge length from a 2:1 ratio (18 becomes 12+6), while preserving saved directions, colours, credentials and matching-IMU calibration. Version 2 records also load; missing background fields use defaults. Version 4 settings already use normalised controls; missing dithering defaults to the new Kconfig setting. Versions 1–3 stored physical brightness percentages. Migration converts both controls directly to the current 100% ceiling: 20% main remains 20%, and 2% background remains 2%. Schema 4–7 controls use the former 40% ceiling; schema 8–12 use 80%. Migration preserves requested PWM within 0.5 percentage points. Main 0% suppresses the foreground; an independently configured background can remain visible. The power switch turns both layers off. The new record is written on the next settings save. Missing/corrupt fields default safely, and older firmware will not overwrite an unknown newer schema during rollback. No automatic whole-NVS erase occurs.

runtime owns the render/output task, 50 Hz effect calculations, LED reconfiguration/transmission and settings writes. The IMU owner publishes a latest sample; input and transport callbacks submit work to the owners. NVS writes happen between completed LED transactions, holding the latched frame briefly. Radio initialization, OTA writes and rollback metadata use a render pause with black output (OTA may latch progress between completed writes). settings, input, imu, effects, web, ota and ble contain their respective logic; main starts them and performs the boot health check.

The JSON API accepts bodies up to 4096 bytes, with bounded nesting and strict types, ranges, known fields and duplicate-key rejection. Browser writes require the local AP origin and the exact content type below. There are no CORS permissions.

Endpoint Behaviour
GET /api/catalogue immutable catalogue JSON and hash header
GET /api/settings coherent catalogue values, state revision and catalogue hash
PUT /api/settings sparse named-control patch plus catalogue hash; apply/save result; application/json
GET /api/status motion, current estimate, heap, frame count, settings/OTA status
POST /api/control JSON action: exit or reboot
POST /api/ota raw application binary; application/octet-stream

The page fetches the catalogue once on load, renders common controls and parameters for the selected effect, then sends only edited values on Apply & Save. PUT bodies are {"catalogue_hash":"...","values":{"brightness":15,"power":true}}. A stale hash is refused; application and save success are separate fields. Full-settings JSON remains an NVS format, independent of this HTTP API. Palette libraries, overlays, calibration, topology, output tuning and large diagnostics are configured on phone. The browser computes no SHA-256 and keeps no persistent descriptor cache.