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
2026-09-10 15:17:28 +02:00
2026-08-10 11:38:01 +02:00
2026-06-03 14:51:31 +02:00
2026-06-03 14:51:31 +02:00

MicrOBU Android App

Android companion app for the micrOBU; a compact V2X on-board unit developed by HAW Hamburg and consider it GmbH for vulnerable road users (cyclists, e-bike riders, pedestrians).

The app is the HMI for the OBU hardware and, on one of the two supported hardware paths, the entire V2X protocol stack. It handles V2X message display, use case detection, sensor collection, trip recording, and OBU communication over USB-C.

Platform: Android (Kotlin) / Min SDK: 29 (Android 10) / Target SDK: 36 / Version: 0.5.0 (Phase 03)

Project goal

Demonstrate V2X communication with at least one C2C-CC bicycle safety use case working on the ESP32-C5, specifically intersection movement assist: a car approaching an intersection on a path that conflicts with the rider's.

That use case is driven entirely from the periodic position and kinematics vehicles broadcast. It needs no traffic light state and no intersection lane geometry, which is why several scope decisions in this repository look deliberately narrow.

Two hardware paths

The project began against the consider it CiT One and later added the ESP32-C5 as a second option. Both paths are supported at runtime and selected by the rider in Settings > OBU Hardware.

consider it CiT One ESP32-C5
What it is Complete V2X on-board unit Development board acting as a plain radio
Transport IP over USB tethering, MQTT Framed binary protocol over USB CDC serial
Reaches the phone as Processed JSON Raw ASN.1 UPER bytes
V2X stack lives On the OBU On the phone, except the radio and GeoNetworking
Own CAM generated by The OBU, autonomously The phone, transmitted on the phone's clock
DENM trigger Available (manual test tool) Not available
Needs a network Yes, internally No

Both paths converge at CamUseCaseRepository, which normalises whatever arrived into the domain Cam type. Everything above that point, including the entire use case detection engine and all UI, is shared and transport-agnostic. That is what makes the ESP32-C5 a drop-in second OBU rather than a fork of the application.

What it does

Real-time V2X monitoring; live CAM, DENM and SPATEM with a station list, active hazards, live signal phase, and a map view. On the CiT One path the topic viewer additionally shows whatever the broker publishes, grouped by topic with pretty-printed JSON and TX/RX badges.

CAM-based use case detection; correlates the rider's own state with a short per-station history of received CAMs to evaluate five C2C-CC bicycle safety use cases (IMA-B, IMA-S, RTW-B, LTW-B, SMVA/BCW-B) and raises alerts under the three-tier Info / Awareness / Warning model. None of these use cases generates a DENM.

Phone-generated CAM; on the ESP32-C5 path the app builds a CAM from live GNSS and IMU, UPER-encodes it, and pushes it down the serial link for the board to broadcast over ITS-G5.

DENM transmission; CiT One path only. Triggers the stationary vehicle profile (hln-sv, causeCode 94) via the consider it Use Case API. This is a manual antenna and range test tool. It is never triggered by a detected event or a use case alert, and the control is hidden entirely on the ESP32-C5 path.

Trip recording; foreground service records all sensor streams and detects cycling manoeuvres (braking, turning, stopping) using orientation-independent signal processing. Works fully offline with no OBU connected. The detected manoeuvres are neither shown nor stored - their only effect is to raise the CAM transmit rate through the manoeuvre on the ESP32-C5 path.

Trip review; past trips displayed as a route on an OpenStreetMap layer, with duration and distance.

CSV export; every sensor sample written to a timestamped CSV in real time. Trip exports additionally include the V2X messages received and their RSSI. Shareable via the standard Android share sheet.

Architecture

MVVM with Repository pattern throughout. Jetpack Compose for all UI (no XML layouts). Hilt for dependency injection.

ui/screens/         Compose screens (Dashboard, Record, Trips, V2X Monitor, Settings...)
ui/navigation/      Navigation graph and bottom nav bar
viewmodel/          MqttViewModel, SensorViewModel, TripRecordingViewModel
data/mqtt/          MQTT repository, Paho client, exponential-backoff reconnection
data/transport/     UsbSerialTransport, SerialFrame, UsbNetworkDetector, ObuHardware
data/cam/           CamUseCaseRepository; where both hardware paths converge
data/db/            Room database (sessions, trips, detected events, V2X messages)
data/               SensorRepository, TripRepository, CsvExporter, TripExporter
domain/asn1/        BitReader/BitWriter and the CAM, DENM and SPATEM UPER codecs
domain/usecase/     UseCaseDetectionEngine, UseCaseDetectionConfig, AlertLevel, GeoMath
domain/detection/   EventDetector, RunningStats sliding window (orientation-independent)
domain/cam/         Cam, CamParser, PhoneCamBuilder, CamTransmitConfig
service/            TripRecordingService, CamTransmitLoop, CamPinger
obu-firmware/       ESP32-C5 firmware (serial link, GeoNetworking, 802.11 OCB, raw TX)
asn1/               Vendored ETSI ASN.1 modules the codecs are verified against

The domain/ packages contain no Android imports. That is what makes the 41-test JVM suite possible without an emulator or instrumentation.

Connectivity

USB-C on both hardware paths. Bluetooth is not implemented and remains an open question in the requirements.

Transport Path Status
USB-C tethering (IP + MQTT) CiT One Active
USB-C serial (framed binary) ESP32-C5 Active
Wi-Fi CiT One Developer builds only
Bluetooth Either Not implemented

On the CiT One path the MQTT broker runs on the OBU (Mosquitto 2.0.11, port 1883); Android USB tethering exposes it as a virtual Ethernet interface at 192.168.42.x and the app auto-detects the gateway IP on plug-in. On the ESP32-C5 path there is no network layer at all: a private framed protocol runs over the board's native USB-C port as a CDC-ACM device.

Verification

The app hand-encodes and decodes ETSI messages bit by bit on the ESP32-C5 path, which is the highest-risk code in the project. Round-trip tests through the project's own codecs structurally cannot catch a shared mistake about a field's bit width, and this project shipped exactly that bug three times (CurvatureCalculationMode, the GeoNetworking reserved bytes, yawRateConfidence). Phone and ESP32 agreed with each other and with nothing else.

Verification therefore uses an independent oracle: asn1tools compiled from the ETSI modules vendored in asn1/.

  • Golden-byte fixtures assert exact encoder output, with expected values produced by the oracle rather than by this encoder.
  • Bulk replay compares every field over real captures: 79,042 SPATEMs and 1,885 DENMs, zero mismatches.
  • Off-air confirmation: 26 of this project's own CAMs captured back by an independent receiver, all accepted.

Never regenerate a golden fixture from this project's own encoder output. See asn1/README.md.

Documentation

Document Audience
docs/MicrOBU-User-Guide.docx Riders. Setup, screens, what the alerts mean, troubleshooting
docs/MicrOBU-Technical-Documentation.docx Supervisors and stakeholders. Architecture, message path, verification, results, decisions
docs/01-requirements-traceability.md Requirements chapters 0 to 13 mapped to implementation and evidence
05-obu-bench-test-2026-08-25.md Bench campaign T1 to T9, measured results
04-transmit-setup.md Transmitter bring-up, radio configuration diagnosis, the TX bypass
obu-firmware/FLASHING.md Toolchain setup, flashing, phone-to-board bring-up checklist
asn1/README.md ASN.1 module provenance and the fixture regeneration rule
docs/references.bib Standards references as BibTeX

Key dependencies

Library Purpose
Jetpack Compose + Material3 UI
Eclipse Paho MQTT CiT One path communication
usb-serial-for-android ESP32-C5 path communication (custom probe table for Espressif VID/PID)
Room Local database
Hilt Dependency injection
OSMDroid Trip review and live V2X map
DataStore Settings persistence
FusedLocationProviderClient GNSS

Getting started

  1. Open in Android Studio and build the app module. Gradle 8.10.2.
  2. Connect a device running Android 10+ (API 29).
  3. Choose your hardware in Settings > OBU Hardware.

CiT One path. Plug the phone into the OBU via USB-C, enable USB tethering on the phone, and the app detects the interface and connects automatically. Broker IP can be overridden in Settings > Connection.

ESP32-C5 path. Flash obu-firmware/ (see obu-firmware/FLASHING.md), then plug the phone into the board's native USB-C port, not the UART bridge port used for flashing. Tap Connect and grant the USB permission. Use the CAM Pinger on the V2X screen to verify the link and radio without starting a trip.

Standalone trip recording. No OBU required. Go to the Record tab and tap REC.

The Wi-Fi transport (broker at 192.168.3.202) remains available in developer builds via Settings > Developer.

Firmware and app must be flashed and installed together: SERIAL_LINK_MAX_PAYLOAD is 512 on both sides and a mismatch silently rejects every large frame.

Status and known limitations

Bench verified against live ITS-G5 traffic on 2026-08-25: 2868 frames over 305 seconds, zero decode failures, zero USB errors, zero crashes. Not yet road validated.

  • Requirement 11.6, Phase A success criteria, is not met. The bench proves reception. It cannot prove the use case behaves correctly with two genuinely moving stations, because nothing on the bench moves. This is the main open evidence gap for the project's central claim.
  • No message signing. ETSI TS 103 097 is out of scope. Secured frames are rejected rather than mis-parsed.
  • Detection thresholds are untuned engineering estimates, not calibrated against real intersection data.
  • 512-byte serial payload cap. Roughly 70% of real road RSU SPATEMs would be dropped as oversize. Accepted deliberately: the intersection use case is CAM-driven and needs none of it.
  • No automatic reconnect after USB re-enumeration; requires a manual Connect.
  • Station IDs rotate, so they cannot identify a physical unit over time.
  • No backend, no login. Everything is on-device.
  • No MAPEM decoder, so signal groups cannot yet be associated with the rider's lane.

Project context

The micrOBU project is funded under the ZIM program (BMWK) and targets micromobility users in Hamburg. The companion app offloads processing from the compact OBU hardware to the smartphone; GNSS fusion, event detection, and on the ESP32-C5 path the full ASN.1 encoding and decoding all run on the phone to keep the OBU lightweight and power-efficient.

V2X communication uses ITS-G5 (IEEE 802.11p) at 5.9 GHz. On the CiT One path the app communicates via the consider it MQTT API v6 (processed JSON). On the ESP32-C5 path the app performs its own ASN.1 UPER encoding and decoding against the ETSI modules vendored in asn1/.

Owner: HAW Hamburg

S
Description
No description provided
Readme
21 MiB
Languages
Kotlin 64.3%
C 19.5%
C++ 12.6%
Python 2.6%
CMake 0.5%
Other 0.5%