ESP32 CSI Node firmware v0.6.8
Tag: v0.6.8-esp32
Date: 2026-05-23
Branch: adr-110-esp32c6 (PR #764)
ADR: ADR-110 P9 + Wave 4 — ESP-NOW mesh offset smoothing landed in firmware
What's new vs v0.6.7
ESP-NOW cross-board sync offset is now EMA-smoothed inside the firmware
(c6_sync_espnow.c). Every consumer of c6_sync_espnow_get_epoch_us() —
including future ADR-029/030 multistatic CSI fusion — gets bounded-jitter
timestamps without re-implementing the filter host-side.
- Filter: fixed-point EMA
y += (raw − y) >> 3, α = 1/8 - Effective window: ≈ 8 samples = 0.8 s at the 10 Hz beacon rate
- Suppression: 3.95× stdev (411.5 µs → 104.1 µs), 4.70× peak-to-peak
- Drift preservation: tracks +30 µs/min crystal skew within 2 µs/min of raw
- Throughput: 99.56 % cross-board match over 5 min, 0 TX failures
- ADR-110 §2.4 ≤100 µs multistatic alignment target empirically met
New API surface:
int64_t c6_sync_espnow_get_offset_us_smoothed(void)— raw access to the EMAc6_sync_espnow_get_epoch_us()now returns timestamps stamped from the
smoothed offset once the follower has heard a leader beacon- Diag log line gains
smoothed=…field alongsideoffset_us=…
Raw offset getter c6_sync_espnow_get_offset_us() stays unchanged for
diagnostics. v0.6.6/v0.6.7 fleets see identical wire behavior — the
smoothing only affects what get_epoch_us() returns to in-firmware
consumers.
Binary cost: +32 bytes (one int64 + one bool + the getter). C6 build
still 45 % partition slack on 4 MB single-OTA.
Full empirical evidence in WITNESS-LOG-110 §A0.10.
Known gap (tracked in WITNESS §A0.11)
CSI frames don't yet carry the synced timestamp in the wire bytes — the
ADR-018 frame format has no timestamp field. Mesh substrate is shipped
and quantified; surfacing the synced clock into per-frame timestamps
needs either an ADR-018 v2 (breaking) or a separate UDP sync packet
(backwards-compatible). Likely path: separate sync packet, ships in a
future release once ADR-029/030 multistatic fusion lights up.
Build artifacts
| Target | Binary | Size | Partition slack |
|---|---|---|---|
esp32s3 (8 MB)
| esp32-csi-node-s3-8mb.bin
| 1093 KB | 47 % |
esp32c6 (4 MB)
| esp32-csi-node-c6-4mb.bin
| 1019 KB | 45 % |
SHA-256 sums in SHA256SUMS.txt.
Flash
Same flash command as v0.6.7 — only the app slot changed.
# ESP32-S3 on COM9 (8 MB)
python -m esptool --chip esp32s3 -p COM9 -b 460800 write_flash \
0x0 bootloader-s3.bin \
0x8000 partition-table-s3.bin \
0xf000 ota_data_initial-s3.bin \
0x20000 esp32-csi-node-s3-8mb.bin
# ESP32-C6 on COM12 (4 MB)
python -m esptool --chip esp32c6 -p COM12 -b 460800 write_flash \
0x0 bootloader-c6.bin \
0x8000 partition-table-c6.bin \
0xf000 ota_data_initial-c6.bin \
0x20000 esp32-csi-node-c6-4mb.binValidation
Verified live on COM9 + COM12 ESP32-C6 boards during the loop:
- 99.56 % cross-board ESP-NOW match over 5 min (2701 beacons, 0 TX fail)
- EMA suppression 3.95× confirmed against raw measurement
- Leader election fires correctly (lower-EUI wins)
- Sync offset preserves crystal drift trajectory