The problem
Each scanner shares one radio between 2.4 GHz Wi-Fi, 5 GHz Wi-Fi and BLE while also reporting to the master, and the master declares a node down after 6,000 ms of silence. A full dual band sweep measured 5,120 ms on hardware, leaving 880 ms for everything else. Records had to move over ESP-NOW first and then over a wired I2C backplane on the final board without anything above the transport caring. Records seen before the GPS has a fix have no real date, and the file must not pretend they do. The ESP32-C5 was new, and the client had already traced an I2C slave bug in the Arduino core, so the work is native ESP-IDF 5.5.
What I did
Scanning moved to channel by channel with a yield whenever a heartbeat is due. The full sweep now takes 31.3 seconds with BLE sharing the radio, but the longest time away from the home channel is 1,650 ms. BLE starts only after enrolment, because starting it earlier starved Wi-Fi under coexistence and one scanner took 110 seconds to enrol. The master beacon went from 1 Hz to 4 Hz.
The I2C protocol itself was fixed, not just the code: an ACK command so an abandoned read cannot lose records, batch size fixed at STATUS time so master and scanner agree on framing, the reply built when the register byte lands, 400 kHz required because an 8 record batch takes 33 ms at 100 kHz. Each board reads its slot from four strap pins, each read twice with a pull up and a pull down to catch a floating pin, and a bad read fails closed. The client's first field run produced 87,487 rows and I read the file rather than the summary.
13,082 rows were dated 2020 from before the GPS set the clock, 36 network names had invalid UTF-8, and the uniqueness table had filled at 33,197 devices. Rows now wait for a real GPS date and are otherwise written with an empty timestamp, names are validated, and the table moved to PSRAM with room for 65,536 devices.
Decisions that shaped it
- One transport interface. Swapping ESP-NOW for the backplane changed nothing above it.
- Fix the protocol, bump the version, as the spec required for any change.
- On ESP-NOW the master only broadcasts and never registers peers, which removes the 20 peer limit. A 20 node build proved it.
- Read the field data row by row. The summary hid three problems.
Result
About 8,100 lines including host tests. 31,183 records over the air with zero lost and zero bad CRC, 22,343 over I2C with zero read errors.



