Files
MicrOBU/obu-cam-transmistter/NOTES.md
T
Ashin Walpola 73d4477f1e Move the capture tooling into the repo
live_capture.py and dump_pcap.py were untracked files inside the third-party
its-g5-receiver-firmware checkout, so the tools every on-air measurement depends
on were versioned nowhere and would vanish with a fresh clone of that project.
They are ours rather than that project's, so they now live in capture/, with the
setup notes rewritten as its README: which port the sniffer speaks on and why,
how to capture, how to check a capture, and how to flash the sniffer board.

live_capture.py carries the fix made on 2026-09-14. The sniffer's console
converts LF to CRLF on its way out, and that applies to every 0x0a byte of the
binary pcap stream, not only to log text, so every inserted CR shifts the rest
of the stream. A 787 KB capture parsed cleanly for only 82 of about 2000
records, and DENMs turned up on nonsense BTP ports because their payloads carry
0x0a often. undo_crlf() reverses it on the raw stream before any framing, which
is exact; afterwards a capture parsed to EOF and DENMs read as port 2002.
Captures taken before that date are truncated at their first corrupted record,
so anything measured from them is worth re-checking.

capture/recordings/ is gitignored, since captures are data rather than source.
The host tests now read both that directory and the older one in the receiver
checkout, so no capture has to be moved while it is being written.

dump_pcap.py reads the same console and still needs the same treatment; that is
recorded in TODO.md.
2026-09-14 13:38:38 +02:00

100 lines
4.4 KiB
Markdown

# OBU transmit firmware - Phase 2 (in progress: HLN-SV DENM beacon)
Started. See `docs/04-transmit-setup.md` in the project root for build/flash
steps and how to validate this against your own sniffer.
## Toolchain: use a dedicated terminal (ESP-IDF 5.5.4)
This project builds against the **global** ESP-IDF 5.5.4, NOT the 6.1 checkout
that `obu-firmware` uses. Keep one terminal per toolchain and never export both
in the same window - the second export inherits the first's
`IDF_PYTHON_ENV_PATH` and then fails every dependency check (`click`,
`esptool`, `cryptography`, ... "not met"). That is env-var bleed, not a broken
install: do **not** run `install.bat` to "fix" it, that damages one of the two
environments.
| Terminal | Export | Project |
|---|---|---|
| Transmitter | `C:\Espressif\frameworks\esp-idf-v5.5.4\export.ps1` | this one |
| OBU | `...\micrOBU_workspace\its-g5-receiver-firmware\esp-idf\export.ps1` | `obu-firmware` |
If a terminal has already been used for the other IDF, clear the state first:
```powershell
$env:IDF_PYTHON_ENV_PATH = $null; $env:IDF_PATH = $null
```
Also note `build/` here was regenerated from scratch (its CMake cache still
referenced an older source path under `micrOBU_workspace/v2x-obu-esp32c5/`,
which makes `idf.py fullclean` refuse to run). If that error reappears, delete
`build/` manually rather than fighting it.
## CAM encoding
`main/cam.c` IS compiled here (unlike `obu-firmware`'s copy, which is a
reference only). It must stay bit-identical to `obu-firmware/main/cam.c` and
the app's `CamUperCodec.kt` - all three encode the same wire format, and a
one-bit divergence in any of them is invisible on the bench but wrong against
real equipment. See the `CurvatureCalculationMode` comment in that file.
Implements one profile so far: **HLN-SV** (aftermarket stationary recovery
vehicle), causeCode 94 (stationaryVehicle), subCauseCode 0, active while the
hazard-light GPIO is grounded. No location/alacarte containers.
- `main/main.c` - entry point, the `phy_11p_set`/`phy_change_channel(5900,...)`
register hack, GPIO polling, TX loop
- `main/denm.c` / `.h` - ASN.1 UPER encoding of a minimal DENM
- `main/geonet.c` / `.h` - GeoNetworking Basic/Common/SHB headers + BTP-B
- `main/dot11p.c` / `.h` - 802.11 OCB (QoS Data, broadcast) frame + LLC/SNAP
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
2004-01-01), fixed (non-rotating) pseudonym MAC, SHB instead of GeoBroadcast
(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 capture
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.