Files
MicrOBU/obu-firmware/NOTES.md
T
Ashin Walpola d2fd222a62 Sign ITS messages on the ESP32-C5 with vanetza-idf, over USB or BLE
obu-firmware is now a port of the colleague's standalone VRU station
(microbu-esp32c5/firmware, kept beside this repository and gitignored): the
vanetza-idf C-ITS stack with the TS 103 097 security entity, credentials in
NVS, the station-link v1 protocol over the native USB port (frame type 0x10
in the existing 0xAA55 framing) and over a BLE GATT peripheral, and its
ITS-G5 radio adapter. The phone still builds CAM and VAM; the board adds
GeoNetworking/BTP and signs with the provisioned authorization ticket. The
private key never leaves the board. Builds with ESP-IDF 6.0.2 only, which
vanetza-idf pins for the radio's private driver ABI. The previous C firmware
stays on disk unbuilt; a full-flash backup of the bench board is kept in
firmware-backups/ (gitignored).

Changed against the colleague's firmware, marked MicrOBU: in the sources:
- Reception unchanged for the app. vanetza-idf drops what it cannot verify
  (unsigned traffic, every RSU), so each captured frame also goes through the
  previous gn_unwrap.c and reaches the phone as link opcode V2X_RX (0x85),
  whose body is the old SERIAL_MSG_V2X_RX payload.
- Unsigned transmission still possible, with the previous geonet.c header;
  the phone chooses per message.
- Console on UART0 (CH343 port); the native USB port carries only link frames.
- BLE advertising pauses while the USB link is in use: BLE and ITS-G5 share
  one RF front end.
- NVS 80 KB (app at 0x20000). At 24 KB, with Wi-Fi settings the previous
  firmware left behind, the BLE bond could not be stored and the phone had to
  pair on every connection.
- Bench fixes: the radio queue is drained before the first PoTi (no RX and
  ~177 queue drops before); the station loop waited pdMS_TO_TICKS(5) = 0
  ticks at 100 Hz and starved the idle task; the 2.4 KB RX capture buffer is
  off the Wi-Fi task stack; BLE notifications longer than the MTU are dropped
  instead of cut short, MTU 517; serial writes are skipped with no USB host.
- Manual country policy and TX-power read-back from the previous radio setup;
  logs for BLE encryption changes and the number of stored bonds.

Verified on the bench board (COM3) with the phone over USB and BLE: CAM and
VAM, signed and unsigned, go out; reception of the sim car and the RSU's
CAM/SPATEM/MAPEM continues; the board survives app restarts and reconnects.
See docs/06-signed-its-vam-ble.md.
2026-09-23 17:27:54 +02:00

4.5 KiB

obu-firmware

Since 2026-09-23: signed ITS on vanetza-idf

This firmware is a port of the colleague's standalone ESP32-C5 VRU station (microbu-esp32c5/firmware, its own git repository kept beside this one and gitignored here). From it: the vanetza-idf C-ITS stack (BTP, GeoNetworking, the TS 103 097 security entity with credentials in NVS), the station-link protocol v1 (link_protocol.*, link_service.*) over the native USB port (serial_link.*, frame type 0x10 in the same 0xAA55 framing as before) and over BLE GATT (simple_ble.*), and the radio adapter (c5_radio.*, otm_tx_custom.c). Build with ESP-IDF 6.0.2; see FLASHING.md.

The phone builds CAM or VAM, configures the station, provisions credentials, sends PoTi and hands each message over as a BTP-DATA.request; the firmware adds GN/BTP, signs with the authorization ticket, and transmits. The phone side is Esp32Link.kt in the app.

Changed or added for this project (search for MicrOBU: in the sources):

  • Reception stays as it was. vanetza-idf decapsulates with itsGnSnDecapResultHandling = STRICT, so it drops unsigned traffic (the bench sim car) and everything signed under a root other than the provisioned demo root (every RSU). Every captured frame therefore also goes through the previous firmware's gn_unwrap.c and reaches the phone as link opcode V2X_RX (0x85), whose body is exactly the old SERIAL_MSG_V2X_RX payload (Station::forward_raw). The app's receive side is unchanged.
  • Unsigned transmission is still possible. The colleague's station refuses unsecured requests. Here a request with GN security profile 1 goes out with the previous firmware's geonet.c header and the position of the last PoTi (Station::unsecured_request); the app's "Sign outgoing messages" setting decides per message.
  • Console on UART0. ESP_LOG stays on the CH343 bridge port (COM3 on the bench); the native port carries only link frames. The colleague's single-port board routes the log into LOG frames there (CONFIG_MICROBU_LOG_OVER_LINK, off here).
  • BLE pauses while USB is in use (CONFIG_MICROBU_BLE_USB_IDLE_MS, 3 s): BLE and ITS-G5 share the C5's one RF front end. Whether a live BLE connection disturbs 5.9 GHz is still unmeasured (TODO.md).
  • A serial write no longer stalls the station task when no USB host is present (BLE-only use).
  • A notification longer than the ATT MTU is dropped instead of silently truncated.
  • The Wi-Fi RX callback's 2.4 KB capture buffer is static rather than on the driver task's stack (the previous firmware's commit 04b0076 fixed the same risk).
  • Manual country policy and the TX-power read-back from the previous firmware's radio bring-up.
  • The activity LED (GPIO27 on the colleague's XIAO board) is off unless configured.

Credentials: the app ships assets/demo-chain.vcr, a throwaway chain generated 2026-09-23 with the colleague's vidf_issue (root 6E7D0374FB021901, AA B3312F29844299E0, AT B80B49387A4C12EB valid two years, permissions psid 36 SSP 010000 and psid 638 SSP 01). The colleague's own demo chain only grants psid 638 and so cannot sign CAMs. Not EU-registered: receivers that verify against the EU trust list drop what it signs.

Time: the signature's generationTime comes from the PoTi timestamp, which follows the app's ItsTime convention (UTC-based, no leap seconds). The colleague's VBS adds the 5 leap seconds. Which one is right is the open question documented in ItsTime.kt.

Earlier notes (Phase 2, superseded)

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.

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).