Record the on-air verification and how to run the bench beacon
Both firmware fixes are now confirmed over the air, with the sniffer board capturing and asn1tools judging the result. GN lifetime: our station transmits 0x05 (1 s), the value both bench stations use, where the August captures show 0x83 (3200 s) for the same frames. 397 CAMs from station 999999 decode and re-encode byte-identically, so the whole transmit chain is right on the wire, not only in the host tests. yawRateConfidence: the bench beacon, flashed to a spare board, sends CAMs that decode and re-encode byte-identically as well (72 of 72 from station 0x0BADC0DE). NOTES.md now says how to flash that beacon and how to check what it sends, including that it shares the phone pinger's MAC and the two are told apart by station ID. The new firmware also runs on the OBU with the phone attached: CAM, DENM and SPATEM from the bench stations all keep decoding in the app now that messages are cut to the length their header declares. Signed reception stays open. The CiT One transmits unsigned and nothing else here signs, so it needs real roadside traffic, the CiT One switched to signed mode, or a replay firmware on a spare board. Two bench facts worth not rediscovering are recorded too: opening COM3's console resets the OBU and drops the phone's USB link, and every capture taken before today is truncated at its first corrupted record, because the receiver's console inserts a CR before every 0x0a byte of the binary pcap stream. live_capture.py now undoes that, a change that lives in the receiver repo and is not part of this commit.
This commit is contained in:
@@ -9,20 +9,24 @@ Engineering to-do list. The reviewer-facing open items live in
|
|||||||
|
|
||||||
`geonet.c` now writes GN lifetime `0x05` (1 s) instead of `0x83`, which decoded to 3200 s. Changed
|
`geonet.c` now writes GN lifetime `0x05` (1 s) instead of `0x83`, which decoded to 3200 s. Changed
|
||||||
in both `obu-firmware` and `obu-cam-transmistter`. Both still build (IDF 6.1 / 5.5.4), and the
|
in both `obu-firmware` and `obu-cam-transmistter`. Both still build (IDF 6.1 / 5.5.4), and the
|
||||||
compiled `geonet_wrap_shb` stores the new byte, but it has not been seen on air yet. Nothing else
|
compiled `geonet_wrap_shb` stores the new byte. Confirmed on air 2026-09-14. Nothing else
|
||||||
reads this byte (`gn_unwrap.c` ignores it, the app never sees GN headers), so the app does not
|
reads this byte (`gn_unwrap.c` ignores it, the app never sees GN headers), so the app does not
|
||||||
need updating alongside the firmware.
|
need updating alongside the firmware.
|
||||||
|
|
||||||
Needs: the phone with the app, the OBU ESP32-C5, and a **second** ESP32-C5 running
|
Needs: the phone with the app, the OBU ESP32-C5, and a **second** ESP32-C5 running
|
||||||
`its-g5-receiver-firmware` to capture with.
|
`its-g5-receiver-firmware` to capture with.
|
||||||
|
|
||||||
- [ ] Flash `obu-firmware` (see `obu-firmware/FLASHING.md`).
|
- [x] Flash `obu-firmware` (done 2026-09-14 on COM3; flash backed up first to
|
||||||
- [ ] Connect the phone, let it send CAMs, and confirm the CAM Pinger's `tx fail` counter stays 0.
|
`Documents/micrOBU_workspace/firmware-backups/COM3-2026-09-14-before-secured-rx.bin`).
|
||||||
- [ ] Capture with the receiver into `its-g5-receiver-firmware/recordings/`.
|
- [x] Capture with the receiver (COM8) into `its-g5-receiver-firmware/recordings/`.
|
||||||
- [ ] Run `python obu-firmware/test/pcap_gn_tally.py its-g5-receiver-firmware/recordings/<capture>.pcap`.
|
- [x] `pcap_gn_tally.py` on capture_20260914_132126.pcap: our station sends SHB, port 2001,
|
||||||
The rows for the phone's pseudonym MACs must show SHB, port 2001, lifetime `0x05`, exactly
|
lifetime `0x05`, same as both bench stations. It was `0x83` in the August captures.
|
||||||
like every other station's CAMs.
|
- [x] Real-station CAMs/DENMs/SPATEM still reach the app (logcat: `handleCamUper`,
|
||||||
- [ ] While the phone is connected: real-station CAMs/DENMs still reach the app (RX path unchanged).
|
`handleDenmUper`, `handleSpatUper` all decoding, 2026-09-14).
|
||||||
|
- [x] Our own CAMs decode on air: 397 frames from station 999999 decode with asn1tools and
|
||||||
|
re-encode byte-identically.
|
||||||
|
- [ ] Confirm the CAM Pinger card's `tx fail` / oversize / CRC counters are 0 (needs a look at the
|
||||||
|
phone; not readable from the PC).
|
||||||
|
|
||||||
Partial check possible with one board and no phone: flash it, `idf.py -p COMx monitor`, and look
|
Partial check possible with one board and no phone: flash it, `idf.py -p COMx monitor`, and look
|
||||||
for `OCB @ 5900 MHz - TX/RX armed`. That proves the new build boots and brings the radio up, not
|
for `OCB @ 5900 MHz - TX/RX armed`. That proves the new build boots and brings the radio up, not
|
||||||
@@ -33,11 +37,12 @@ that it transmits correctly.
|
|||||||
Its `cam.c` (compiled into that firmware) wrote `yawRateConfidence` as 3 bits / 7 instead of
|
Its `cam.c` (compiled into that firmware) wrote `yawRateConfidence` as 3 bits / 7 instead of
|
||||||
4 bits / unavailable(8), the bug the app fixed on 2026-08-20. Fixed in it and in obu-firmware's
|
4 bits / unavailable(8), the bug the app fixed on 2026-08-20. Fixed in it and in obu-firmware's
|
||||||
reference copy; asn1tools now decodes the CAM and re-encodes it byte-identically, and it builds on
|
reference copy; asn1tools now decodes the CAM and re-encodes it byte-identically, and it builds on
|
||||||
IDF 5.5.4. No board runs this firmware right now (the production OBU runs obu-firmware), so this
|
IDF 5.5.4. Since 2026-09-14 the spare board on COM10 runs it as a bench beacon:
|
||||||
only matters if it is flashed again:
|
|
||||||
|
|
||||||
- [ ] After flashing it: capture, run `pcap_gn_tally.py`, and decode the CAM payload with
|
- [x] Done 2026-09-14: flashed on COM10 and captured on COM8. All 72 CAMs from station
|
||||||
asn1tools (`py -3.11`, modules in `asn1/`).
|
195936478 (0x0BADC0DE) decode with asn1tools and re-encode byte-identically, so the
|
||||||
|
4-bit yawRateConfidence is right on air. COM10 now runs this beacon rather than
|
||||||
|
obu-firmware - reflash it if the spare is needed as an OBU again.
|
||||||
|
|
||||||
### Signed-message reception and exact payloads (added 2026-09-11)
|
### Signed-message reception and exact payloads (added 2026-09-11)
|
||||||
|
|
||||||
@@ -45,17 +50,21 @@ obu-firmware's `gn_unwrap.c` now unwraps TS 103 097 signed packets (signature no
|
|||||||
reported as V2X_RX flags bit1) and cuts every message to the length its header declares, dropping
|
reported as V2X_RX flags bit1) and cuts every message to the length its header declares, dropping
|
||||||
the 8 bytes the chip's RX appends to each frame, which were forwarded to the phone until now.
|
the 8 bytes the chip's RX appends to each frame, which were forwarded to the phone until now.
|
||||||
Verified on the host (`obu-firmware/test/host`: chain, replay of all recordings against asn1tools,
|
Verified on the host (`obu-firmware/test/host`: chain, replay of all recordings against asn1tools,
|
||||||
50M-iteration fuzz) and built on IDF 6.1, but not flashed: the production OBU still runs the
|
50M-iteration fuzz) and flashed to the production OBU on 2026-09-14. The remaining gap is signed
|
||||||
2026-09-10 build. Needs the OBU with this build, the phone, and signed traffic - real vehicles or
|
traffic to receive: real vehicles or
|
||||||
RSUs, since the bench CiT One sends unsigned. A second ESP32 running the receiver firmware is
|
RSUs, since the bench CiT One sends unsigned. A second ESP32 running the receiver firmware is
|
||||||
optional, but shows what was on air at the time.
|
optional, but shows what was on air at the time.
|
||||||
|
|
||||||
- [ ] Flash obu-firmware (this also carries the GN lifetime fix above).
|
- [x] Flash obu-firmware (done 2026-09-14, COM3).
|
||||||
|
- [x] Unsigned bench traffic still decodes in the app, with messages now cut to their declared
|
||||||
|
length (CAM, DENM and SPATEM all decoding in logcat after the flash).
|
||||||
- [ ] Near signed traffic: signed CAMs/DENMs appear in the app, and a simultaneous capture shows
|
- [ ] Near signed traffic: signed CAMs/DENMs appear in the app, and a simultaneous capture shows
|
||||||
them on air (`pcap_gn_tally.py` lists them as `secured`).
|
them on air (`pcap_gn_tally.py` lists them as `secured`). NOT possible at this bench: the
|
||||||
- [ ] Unsigned bench traffic still decodes in the app as before (messages now arrive 8 bytes
|
CiT One transmits unsigned (`ItsGnSecurity = 0`) and nothing else here signs. Needs a drive
|
||||||
shorter).
|
past real RSUs, the CiT One switched to signed mode if its API allows, or a replay firmware
|
||||||
- [ ] The heartbeat's oversize counter still counts over-long messages (e.g. road SPATEMs).
|
on a spare board that re-transmits the recorded signed frames.
|
||||||
|
- [ ] The heartbeat's oversize counter still counts over-long messages. Not exercised at the
|
||||||
|
bench: the SPATEMs here are ~340 bytes on air, far below the cap.
|
||||||
|
|
||||||
## Set up host testing
|
## Set up host testing
|
||||||
|
|
||||||
@@ -92,6 +101,19 @@ Suggested order after the host tests exist:
|
|||||||
Dropped: building vanetza as a GN/BTP oracle. Real captures (`pcap_gn_tally.py`), the host
|
Dropped: building vanetza as a GN/BTP oracle. Real captures (`pcap_gn_tally.py`), the host
|
||||||
round-trip test and `asn1tools` for UPER cover what it would have checked.
|
round-trip test and `asn1tools` for UPER cover what it would have checked.
|
||||||
|
|
||||||
|
## Follow-ups found 2026-09-14
|
||||||
|
|
||||||
|
- [ ] **Commit the `live_capture.py` fix in the receiver repo.** Its console inserts a CR before
|
||||||
|
every LF, which also hits every 0x0a byte of the binary pcap stream, shifting pcap record
|
||||||
|
headers and frames. A 787 KB capture parsed cleanly for only 82 of ~2000 records, and DENMs
|
||||||
|
showed up on nonsense BTP ports. `undo_crlf()` now reverses it on the raw stream before
|
||||||
|
framing; afterwards a capture parsed to EOF and DENMs read as port 2002. This is the
|
||||||
|
"occasional byte inserted mid-frame" in the older recordings, so **every capture taken before
|
||||||
|
2026-09-14 is truncated at its first corrupted record** - re-measure anything derived from
|
||||||
|
them. The change is in the `its-g5-receiver-firmware` repo, uncommitted.
|
||||||
|
- [ ] **Do not open COM3's console while the phone is attached.** Opening it toggles DTR/RTS on the
|
||||||
|
CH343 and resets the OBU, which drops the phone's USB link and needs a manual Connect.
|
||||||
|
|
||||||
## Follow-ups found 2026-09-11
|
## Follow-ups found 2026-09-11
|
||||||
|
|
||||||
- [ ] **App: show the signed flag.** `V2xRxFrame.parse` in `SerialFrame.kt` only reads bit0 of
|
- [ ] **App: show the signed flag.** `V2xRxFrame.parse` in `SerialFrame.kt` only reads bit0 of
|
||||||
|
|||||||
@@ -51,3 +51,49 @@ Known gaps, tracked as TODOs in the source: no real GNSS (lat/long hardcoded
|
|||||||
0), no real time source (detectionTime/referenceTime hardcoded 0, decodes as
|
0), no real time source (detectionTime/referenceTime hardcoded 0, decodes as
|
||||||
2004-01-01), fixed (non-rotating) pseudonym MAC, SHB instead of GeoBroadcast
|
2004-01-01), fixed (non-rotating) pseudonym MAC, SHB instead of GeoBroadcast
|
||||||
(no multi-hop forwarding), unsecured (no IEEE 1609.2 signing).
|
(no multi-hop forwarding), unsecured (no IEEE 1609.2 signing).
|
||||||
|
|
||||||
|
## Running it as a bench beacon
|
||||||
|
|
||||||
|
This firmware needs no phone: it beacons a CAM every second by itself
|
||||||
|
(`TX_INTERVAL_MS`) from station `0x0BADC0DE` (195936478), stationType 5
|
||||||
|
(passengerCar), at the hardcoded bench position, under the fixed MAC
|
||||||
|
`02:00:00:00:00:01`, on 5900 MHz. That makes it the quickest way to put known,
|
||||||
|
repeatable traffic on air, and it is how the 4-bit `yawRateConfidence` encoding
|
||||||
|
was confirmed over the air on 2026-09-14.
|
||||||
|
|
||||||
|
A board with only one USB-C port is fine. This firmware's console is on UART0,
|
||||||
|
so such a board shows no log output, but nothing here needs the console.
|
||||||
|
|
||||||
|
Flash it from the toolchain terminal (ESP-IDF 5.5.4, see the table above):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd C:\Users\Ashin\AndroidStudioProjects\MicrOBU\obu-cam-transmistter
|
||||||
|
idf.py -p COM10 -b 921600 flash
|
||||||
|
```
|
||||||
|
|
||||||
|
Or flash the existing build without any toolchain terminal:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd obu-cam-transmistter\build
|
||||||
|
C:\Espressif\python_env\idf5.5_py3.11_env\Scripts\python.exe -m esptool --chip esp32c5 -p COM10 -b 921600 write_flash --flash_mode dio --flash_freq 80m --flash_size 2MB 0x2000 bootloader/bootloader.bin 0x8000 partition_table/partition-table.bin 0x10000 obu_firmware.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
It starts beaconing as soon as it boots, so there is nothing to start by hand,
|
||||||
|
and unplugging it is how you stop it.
|
||||||
|
|
||||||
|
**It transmits under the same MAC as the phone's CAM pinger**, so on air the two
|
||||||
|
are told apart by station ID (195936478 here, 999999 for the pinger), never by
|
||||||
|
source address.
|
||||||
|
|
||||||
|
To see what it is sending, capture on the sniffer board and decode:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd its-g5-receiver-firmware
|
||||||
|
py -3.11 live_capture.py COM8
|
||||||
|
py -3.11 ..\obu-firmware\test\pcap_gn_tally.py recordings\capture_<timestamp>.pcap
|
||||||
|
```
|
||||||
|
|
||||||
|
The tally lists it as SHB / port 2001 / lifetime `0x05`. For the message itself,
|
||||||
|
decode the payload with `asn1tools` against `asn1/cam_1_4_1.asn` +
|
||||||
|
`asn1/cdd_1_3_1_1.asn`; re-encoding must return the identical bytes. On
|
||||||
|
2026-09-14, 72 of 72 frames did.
|
||||||
|
|||||||
Reference in New Issue
Block a user