May 28, 2026 · 2 min read

Making ESP32 radio firmware observable

A practical state model for radios, storage, an LCD, and a browser interface sharing one constrained device.

One device, several truths

An ESP32-C6 can observe BLE, parse Wi-Fi frames, drive a display, stream browser updates, and log to storage. The hard part is preventing each surface from inventing its own version of what the device is doing.

We treat detection as a stream of domain events, then reduce those events into a bounded state model. The LCD and browser render the same state; neither owns it.

C6BLE OBSWIFI OBSSTOREINTERFACE
Radio observations become bounded domain state before any interface decides how to present them.

Bounded by design

Unbounded contact history is a desktop assumption. Embedded state needs explicit capacity, expiry, and replacement rules.

ccontact_upsert(&(contact_update_t) {
  .kind = CONTACT_REMOTE_ID,
  .identity = frame->uas_id,
  .rssi = frame->rssi,
  .seen_at_ms = now_ms,
});

The reducer owns deduplication and expiry. Interfaces receive snapshots, which makes a 500 ms WebSocket push and a 30 Hz LCD refresh coexist without coupling the radio task to either one.

Instrument the quiet moments

Boot stages, scan health, storage state, GPS permissions, and stale contacts deserve the same attention as a positive detection. Useful firmware explains what it is waiting for.

  • Use state names that describe operator meaning, not only driver state.
  • Keep histories bounded and surface their expiry.
  • Log transitions, not high-frequency noise.
  • Design the recovery interaction with the failure path.

That discipline scales better than adding another status icon after every field report.