Initial state: one communication path for different jobs
Telemetry, control commands and larger spatial information shared assumptions about frequency and reliability. A telemetry frame was about 75 bytes, a control frame about 20 bytes, and the SBC decision loop ran at 30 Hz — one cycle every 33.3 ms.
fixed binary frame, sequence and timestamp
small reliable command with acknowledgement
from 33.3 ms to 16.7 ms per cycle
Diagnosis: reliability and frequency are not the same requirement
Fresh telemetry loses value quickly, so a late packet is often worse than a dropped one. A drive command is different: ordering and acknowledgement matter. Spatial data is heavier and changes more slowly. Treating all three streams identically created avoidable overhead and coupled independent failure modes.
No backlog: telemetry should deliver the newest state, not the history of a queue
In a real-time path, FIFO can preserve samples that are technically valid but already obsolete. If the receiver stalls briefly, replaying t−3, t−2 and t−1 only delays arrival at state t. An older telemetry sample may therefore be skipped so the receiver can operate on the newest valid frame.
sample → serialize → queue → transport → queue → parse → decision
sample → current frame → transport → validate → current state
Decision: separate the channels
Fixed frames and bit packing: the parser reads offsets instead of interpreting message descriptions
The 75 → 28 B and 20 → 9 B reductions are not merely compression. The representation changed to a fixed binary frame with known-width fields and bit-packed flags. The receiver reads predictable offsets and validates version, sequence, timestamp and checksum.
Why the watchdog stays on the ESP32
The Linux computer can restart, stall or lose its link. The emergency reaction therefore cannot depend on the same path that may have failed. ESP32 tracks the age of the last accepted command and stops outputs locally after the configured timeout.
if (now_ms - last_valid_control_ms > CONTROL_TIMEOUT_MS) {
drive_left = 0;
drive_right = 0;
state = SAFE_STOP;
}Result and interpretation
Smaller frames reduced serialization and parsing work, while independent channels prevented large or reliable transfers from blocking fresh telemetry. In the measured system, the decision loop increased from 30 Hz to 60 Hz. The key improvement was architectural: each data class received the transport and failure policy it actually needed.
Reference firmware, SBC code and protocol notes.
The attached package contains an Arduino example, a Python SBC package, tests, protocol documentation and a license. Review constants, pins and safety thresholds before use on hardware.