Phase 03: ESP32-C5 serial link hardening + bench diagnostics

Enumeration:
- Merge the library's stock probe table instead of replacing it, so adding
  Espressif 0x303A/0x1001 doesn't drop every other supported device
- Select the ESP32-C5 by VID/PID rather than list position
- Log USB interface descriptors to distinguish CDC data from the JTAG interface
Lifecycle:
- Don't close the shared port in MqttViewModel.onCleared() - the foreground
  recording service outlives the ViewModel and would beacon into a dead port
- Handle ACTION_USB_DEVICE_DETACHED so the UI stops reporting a stale link
- Implement the STATUS heartbeat on both sides (1 Hz) plus a phone-side watchdog
- Surface write failures and firmware drop counters on the CAM Pinger card
Protocol:
- Assert DTR/RTS on open (unverified on hardware - see FLASHING.md step 5)
- Raise SERIAL_LINK_MAX_PAYLOAD 160 -> 512 on both sides; real third-party CAMs
  exceed 160 and were being silently dropped at the resync branch
- Move the enlarged buffers off task stacks; serialize send_frame with a mutex
Firmware and app must be updated together - a 512/160 mismatch fails silently.
This commit is contained in:
Ashin Walpola
2026-08-10 11:38:01 +02:00
parent 33c4ec5998
commit 64fb78590e
20 changed files with 3076 additions and 596 deletions
+9 -2
View File
@@ -111,7 +111,11 @@ static void tx_radio_task(void *arg)
continue;
}
uint8_t gn_payload[160];
// static, not stack: these are sized off SERIAL_LINK_MAX_PAYLOAD (raised to 512), so on
// the stack they'd be ~1.2 KB of this task's 4 KB. Safe as statics - tx_radio_task is a
// singleton, created once in app_main. Both wrap functions bounds-check against the size
// passed in and return <= 0 on overflow, so an oversized CAM is rejected, not written past.
static uint8_t gn_payload[SERIAL_LINK_MAX_PAYLOAD + 64];
int gn_len = geonet_wrap_shb(item.data, item.len, pseudonym_mac, STATION_TYPE,
BENCH_LATITUDE_TENMICRODEG, BENCH_LONGITUDE_TENMICRODEG,
BTP_PORT_CAM, gn_payload, sizeof(gn_payload));
@@ -120,7 +124,7 @@ static void tx_radio_task(void *arg)
continue;
}
uint8_t frame[300];
static uint8_t frame[SERIAL_LINK_MAX_PAYLOAD + 192];
int frame_len = dot11p_build_frame(gn_payload, gn_len, pseudonym_mac, frame,
sizeof(frame), false);
if (frame_len <= 0) {
@@ -132,6 +136,9 @@ static void tx_radio_task(void *arg)
// during Phase 2 bring-up (see git history for the tx_custom.c A/B test that led here).
esp_err_t err = esp_wifi_80211_tx(WIFI_IF_STA, frame, frame_len, true);
if (err != ESP_OK) {
// Also counted into the heartbeat so the phone can see it - from the app's side a CAM
// that reached the radio but didn't go out otherwise looks identical to one that did.
serial_link_note_tx_failure();
ESP_LOGW(TAG, "esp_wifi_80211_tx failed: %d", err);
} else {
ESP_LOGI(TAG, "CAM sent (%d bytes) @ %d MHz", frame_len, TX_FREQ_MHZ);