Count and surface RX-queue drops on the ESP32-C5's promiscuous path
wifi_promisc_rx_cb() fed s_rx_queue with a 0-timeout xQueueSend() and never checked whether it succeeded, so a burst of captured frames arriving faster than rx_forward_task could drain them vanished with no counter anywhere - none of oversizeDrops/txFailures/rxCrcErrors caught it. Added a rxQueueDrops counter, threaded it through the STATUS heartbeat as a new trailing uint16 (old firmware/app on either side still parse fine), and surfaced it on the CAM Pinger card. Confirmed on the bench: flashed to the production OBU (COM3) and installed the matching app build on the phone, then watched the counter over logcat against obu-cam-transmistter's ~3.3 Hz beacon - it is real (0 -> 89 -> 90 across two sessions) but bursty around connect/reconnect rather than a continuous overflow under steady single-station traffic.
This commit is contained in:
@@ -5,6 +5,71 @@ Engineering to-do list. The reviewer-facing open items live in
|
||||
|
||||
## Waiting on hardware
|
||||
|
||||
### Confirm the RX queue drop counter explains the bench-session frame drops / map flicker (added 2026-09-22)
|
||||
|
||||
Investigated the user's report of "OBU mode keeps dropping a few frames" and "v2x screen comes
|
||||
and goes" while bench-testing against `obu-cam-transmistter`. Found a real, previously invisible
|
||||
drop path: `obu-firmware/main/main.c`'s `wifi_promisc_rx_cb()` calls `xQueueSend(s_rx_queue, ...,
|
||||
0)` (queue depth 8) without checking the return value, so a burst of promiscuously-captured
|
||||
frames arriving faster than `rx_forward_task` can drain them (each drain can legitimately block up
|
||||
to ~400ms under USB/UART contention) silently vanishes. None of the existing `EspLinkStatus`
|
||||
counters (`oversizeDrops`/`txFailures`/`rxCrcErrors`) caught this class of drop.
|
||||
|
||||
This plausibly also explains the map symptom: `UseCaseDetectionEngine.pruneStale()` drops a remote
|
||||
station's marker after `staleRemoteMs` (3 s) with no CAM update. Measured 2026-09-22 via
|
||||
`tools/cit_one_rx_watch.py --host 192.168.40.201` against `obu-cam-transmistter`'s bench beacon
|
||||
(stationID 195936478 / 0x0BADC0DE): **75 CAMs in 25 s, ~3 Hz**, not the 1 Hz this note assumed
|
||||
earlier — faster than assumed means more promiscuous captures per second and a shorter fuse on
|
||||
`staleRemoteMs`, both of which make the queue-overflow theory more likely, not less.
|
||||
|
||||
Fixed to be **visible**, not yet fixed to **not drop**: added a `rxQueueDrops` counter, checked
|
||||
`xQueueSend`'s return value (`main.c`), wired it through the STATUS heartbeat as a new trailing
|
||||
`uint16` field (`serial_link.c/.h`, `SerialFrame.kt`'s `EspLinkStatus`), and surfaced it on the
|
||||
CAM Pinger card (`MqttTopicViewerScreen.kt`, string `mqtt_cam_pinger_fw_counters`). Host build
|
||||
untouched (serial_link.c/main.c aren't in the host test's standard-headers-only set); IDF build
|
||||
verification is the remaining pre-flash check. Deliberately did NOT bump `s_rx_queue`'s depth from
|
||||
8 — no real burst-size data yet, and guessing a bigger number against an unmeasured memory budget
|
||||
is exactly the kind of assumption [[microbu-hw-review]] flags as needing verification first, not
|
||||
capacity that's cheap to reason your way into.
|
||||
|
||||
Needs: a phone attached to the production OBU's native USB port, watching the CAM Pinger card,
|
||||
while `obu-cam-transmistter` (or real traffic) beacons.
|
||||
|
||||
- [x] `idf.py build` succeeds (obu-firmware, IDF 6.1) — clean, both changed files compiled with no
|
||||
warnings, 17% flash free.
|
||||
- [x] Reflashed the production OBU on **COM3** 2026-09-22 (hash verified). Boot log confirms the
|
||||
new build (`21e0149-dirty`, compiled Sep 22 2026 14:14:09), clean boot, OCB @ 5900 MHz
|
||||
TX/RX armed, `serial_link up ... 1 Hz heartbeat`, no panic. Incidentally answers part of the
|
||||
"measure the OBU's actual transmit power" item below: this boot logged
|
||||
`tx power: 72 quarter-dBm = 18.00 dBm (20.00 requested)` — the driver **is** clamping below
|
||||
the requested 20 dBm at 5900 MHz, as that item suspected but had not measured.
|
||||
- [x] 25 s of steady-state console (no phone attached, `obu-cam-transmistter` beaconing nearby):
|
||||
silent — no crash, no `oversize`/`rx queue full`/`crc` warnings. Inconclusive on its own
|
||||
(successful forwards aren't logged, and nothing was attached to trigger the ~400 ms UART
|
||||
stalls the theory needs), but at least rules out a crash-on-boot regression.
|
||||
- [x] Confirmed the wider bench RF path independently via the CiT One OBU broker
|
||||
(`py -3.11 tools/cit_one_rx_watch.py --host 192.168.40.201`): heard `obu-cam-transmistter`'s
|
||||
beacon cleanly, 75/25 s, GN source `14:00:02:00:00:00:00:01`, position in the expected
|
||||
St. Georg route area. This is a *different* receiver from the production OBU though — it
|
||||
shows the beacon is genuinely on air, not that COM3 forwards every one of it without drops.
|
||||
- [x] **Confirmed on real hardware, 2026-09-22.** Installed the updated debug APK (previous build
|
||||
on the phone was from 2026-09-15, predating this fix entirely) on the Pixel 9 Pro (adb over
|
||||
Wi-Fi), relaunched against the freshly-reflashed COM3, and read `rx queue drop` via `adb
|
||||
logcat -s UsbSerialTransport`. The counter mechanism works end-to-end and **the bug is
|
||||
real**: `rxQueueDrops` was 0 at the last flash (14:22), read as 89 at first reconnect
|
||||
(14:48, ~26 min later), and 90 at a second reconnect (14:52). No `oversizeDrops`,
|
||||
`txFailures`, or `rxCrcErrors` moved at all, and zero `decode FAILED` lines — this queue is
|
||||
the only place frames are going missing.
|
||||
Nuance: over a clean ~4.5 min window in between (14:48→14:52) with `obu-cam-transmistter`
|
||||
actively beaconing at a measured **~3.33 Hz** (matches the CiT One's 75/25 s independently)
|
||||
and 490+ CAMs decoding cleanly with steady cadence and no gaps, the counter did **not**
|
||||
move — it only ticked at connect/reconnect moments. So this is a low-rate, bursty drop (matches
|
||||
the user's own "a few frames" framing), not a continuous overflow under steady single-station
|
||||
traffic; it may be specific to WiFi/PHY activity around association or reconnect rather than
|
||||
raw beacon rate. Worth a longer, quieter-boot capture before sizing a `s_rx_queue` bump.
|
||||
Did **not** independently confirm the map-flicker connection this session — that needs eyes
|
||||
on the app's V2X screen while watching this same counter live, not just logcat.
|
||||
|
||||
### On-device check of the full-screen V2X live map (added 2026-09-15)
|
||||
|
||||
The live map moved out of the V2X Monitor's view-mode row into its own full-screen destination
|
||||
@@ -51,6 +116,27 @@ Partial check possible with one board and no phone: flash it, `idf.py -p COMx mo
|
||||
for `OCB @ 5900 MHz - TX/RX armed`. That proves the new build boots and brings the radio up, not
|
||||
that it transmits correctly.
|
||||
|
||||
### Measure the OBU's actual transmit power (added 2026-09-14)
|
||||
|
||||
Nothing in this project has ever measured it. `main.c` asks for 20 dBm
|
||||
(`esp_wifi_set_max_tx_power(80)`, 0.25 dBm units) and the build's ceiling is the same
|
||||
(`CONFIG_ESP_PHY_MAX_TX_POWER=20`), but a request is a ceiling, not a guarantee: the driver clamps
|
||||
it to its own calibrated table, and 5900 MHz is above the range this chip is rated for, so the
|
||||
table actually in use is channel 177's. The firmware now reads the value back and logs it at boot,
|
||||
which records what the driver admits to, not what leaves the antenna.
|
||||
|
||||
- [ ] Flash and `idf.py -p COM3 monitor`, then note the `tx power:` line. A value below 80 means
|
||||
the driver clamped the request, which the code alone cannot tell you.
|
||||
- [ ] Relative check with the second ESP32-C5 on `its-g5-receiver-firmware`: capture at a measured
|
||||
distance in a straight line, read the RSSI the receive path already reports, and record
|
||||
distance and RSSI together. This gives a comparable number between builds and antennas,
|
||||
which is what matters for range work, without any lab equipment.
|
||||
- [ ] Only a spectrum analyser or a calibrated reference receiver gives real radiated power. Worth
|
||||
it only if the range result looks wrong, or if the thesis needs an absolute figure.
|
||||
|
||||
For context: ETSI allows up to 33 dBm EIRP on the ITS band, and production OBUs sit around
|
||||
20 to 23 dBm, so the requested figure is in the right region if the PA really keys it there.
|
||||
|
||||
### obu-cam-transmistter yawRateConfidence fix (added 2026-09-11)
|
||||
|
||||
Its `cam.c` (compiled into that firmware) wrote `yawRateConfidence` as 3 bits / 7 instead of
|
||||
@@ -80,6 +166,58 @@ optional, but shows what was on air at the time.
|
||||
shorter).
|
||||
- [ ] The heartbeat's oversize counter still counts over-long messages (e.g. road SPATEMs).
|
||||
|
||||
### CiT One custom CAM injection over `v2x/tx/v2/cam` (added 2026-09-14)
|
||||
|
||||
The haw-002 unit now runs the special firmware: Cohda's own CAM transmission disabled, and a
|
||||
V2X-Gateway build that accepts a `SendV2XMessage` (schemas.consider-innovation.de/its-s/
|
||||
v2x_interface.proto) carrying a UPER CAM on `v2x/tx/v2/cam`. `tools/cit_one_cam_tx.py` builds
|
||||
and publishes those from a PC; its `--self-test` passes offline, proving only that the bytes
|
||||
match `CamEncodeGoldenTest.kt` and that the protobuf wrapper round-trips. Nothing about what
|
||||
the OBU does with them is established.
|
||||
|
||||
Reach the broker over Wi-Fi or Ethernet for now - the USB-peripheral-mode link needs the phone
|
||||
to be USB host on a `172.25.1.0/24` interface with no DHCP server, which Android cannot
|
||||
configure from inside an app.
|
||||
|
||||
Needs: the CiT One haw-002 on the same network as a PC, and a second ESP32-C5 running
|
||||
`its-g5-receiver-firmware` sniffing G5CC (`-c 5900`) to capture with.
|
||||
|
||||
Bench run 2026-09-14, PC -> haw-002 (192.168.3.201), captured on the RSU (192.168.3.202,
|
||||
**not** .2.202 - that address does not route). `tools/cit_one_rx_watch.py` decodes what a unit
|
||||
hears. Result: the injection path works end to end, with one blocker found.
|
||||
|
||||
- [x] Publishes without the broker refusing the topic. 1.00 Hz, confirmed by subscribing to
|
||||
`v2x/tx/v2/cam` on the OBU itself.
|
||||
- [x] The RSU hears our CAMs on air, 1.00 Hz, matching what we publish.
|
||||
- [x] `ItsPduHeader` **is** expected in the payload - we send it included and it decodes.
|
||||
- [x] BTP destination port 2001. GN source address `08:00:26:93:92:01:91:dc`, the OBU's.
|
||||
- [x] **Our CAM content goes out intact**: position, speed (417), heading (639), width (7) and
|
||||
length (18) arrive byte-exact. The gateway does not touch the content.
|
||||
- [x] **The gateway overwrites `stationID`** with the OBU's own (999999 -> 4033890855, which
|
||||
matches `own_info.stationID` on `v2x/rx/obu_gnss`). This is what the "OBU owns identity"
|
||||
decision wants, so `--follow-obu-identity` is not needed on this unit.
|
||||
- [x] ~~BLOCKER: Cohda's own CAM is still transmitting.~~ Fixed 2026-09-14 by disabling CAM in
|
||||
a second conf file: the RSU now hears only our stream, 0 CAMs with the stack's
|
||||
unavailable dimensions over 30 s. Note the stack restart gave the unit a new identity
|
||||
(stationID 4033890855 -> 2553426533, GN source `08:00:26:...` -> `08:00:a2:...`), which is
|
||||
expected under `ItsGnLocalAddrConfMethod = 2` (anonymous, random at boot).
|
||||
- [x] Re-checked: 41 published / 41 heard over 40 s, 1.02 Hz both ends, inter-arrival a steady
|
||||
1.0 s. 100% delivery, no gateway rate limiting. An earlier 0.40 Hz sample was the stack
|
||||
still settling after the restart and did not persist.
|
||||
Two topics the v6 API does not document, found by subscribing to `#` on haw-002:
|
||||
|
||||
- `v2x/loopback/cam` - a `RecvV2XMessage` (btpHeader.type=2) carrying each CAM the unit
|
||||
transmits, 1:1 with what we publish and **after** the gateway's stationID rewrite. This is the
|
||||
TX confirmation we were going to ask consider it for: it makes "did my CAM go out, and under
|
||||
which identity" answerable on the transmitting unit alone, without an RSU or a second ESP32.
|
||||
- `v2x/rx/obuinfo` at 10 Hz - the protobuf `OwnStationInfo` (binary twin of `obu_gnss`;
|
||||
field 2 decodes to the same stationID, field 10 to the same heading). Output only, so it is
|
||||
not the content-feed input we speculated about.
|
||||
|
||||
- [ ] Wire `v2x/loopback/cam` into `cit_one_rx_watch.py` as a local TX check.
|
||||
- [ ] Sanity-check the rate: `--rate 4` should produce 4 CAMs/s on air, since `ItsDCCEnabled = 0`
|
||||
on this unit.
|
||||
|
||||
## Set up host testing
|
||||
|
||||
- [x] Install MSYS2 UCRT64 gcc (done 2026-09-11: gcc 16.2.0, GNU Make 4.4.1; chosen over WSL,
|
||||
|
||||
Reference in New Issue
Block a user