obu-firmware builds against vanetza-idf from microbu-esp32c5/external, but that tree was gitignored, so a clone of this repository could not build the firmware it ships. It is now committed here as ordinary files in its own folder, microbu-esp32c5/: the colleague's commit cf4b99f plus the V2X2MAP bridge's signature verification (--trust) used on the bench. Nothing is fetched from or pushed to the colleague's repository; this repository and its remotes carry everything. The folder's own .gitignore keeps build output, downloaded components and private key material out, as it did there; the committed file set is identical to that repository's tracked files. The ESP32-C5 is still flashed from obu-firmware/, which only takes vanetza-idf from microbu-esp32c5/, so the two stay separate folders. FLASHING.md says how to take a newer version of the colleague's tree (copy it over the folder, rebuild, test, commit).
VRU ITS-S station-internal link (phone ⇄ micrOBU)
Project interface IF-PROJ-NF-BLE-001 made concrete, plus its DEC-VBS-004 companions. One VRU ITS-S (ETSI EN 302 665 clause 4.5.1) is split over two physical units at the NF-SAP (DEC-VBS-003):
| Unit | Hosts |
|---|---|
| Phone (app) | VRU basic service, VAM encode/decode, HMI, PoTi, credential manager (TS 102 941 client) |
| micrOBU (ESP32-C5, this firmware) | BTP-B, GeoNetworking SHB/GBC, SN-SAP security entity (TS 103 097 signing, ticket pool, identifier change, SN-DECAP), ITS-G5 access |
Everything that crosses the link is a standards-defined primitive
(BTP-DATA.request/indication of TS 102 723-11 / TS 103 836-5-1 Annex A; the
PoTi data set of EN 302 890-2 Tables 2 and 3; the SF-SAP identifier-change
primitives of TS 102 723-9 Tables 10 to 21; the legacy/reference MF-SET parameter
semantics of TS 103 175 Table 11) or
one of three project-defined management messages (station configuration,
credential provisioning with a VCR1 bundle, status). No SN-, MN- or MI-SAP
primitive crosses the link (ACT-015).
The control/reference transport is the micrOBU's wired native USB Serial/JTAG port. Encrypted BLE GATT is an experimental alternative and shall not replace the wired architecture until ACT-021's controlled coexistence experiment shows that concurrent BLE does not disturb C5 PHY/CCA observation, ITS-G5 RF behavior, timing, reliability, or power measurements. The message layer below is transport independent. Nothing here is an ETSI on-air format or a conformance claim for the phone-to-micrOBU link.
Implementations: firmware firmware/main/link_protocol.*,
ble_framing.* and ble_link.* (C++); phone-side protocol module
microbuapp/station-link/ and Android central
microbuapp/app/src/main/java/com/hawhamburg/micr0bu/data/transport/BleStationLinkTransport.kt
(Kotlin); bench reference station-link/python/microbu_link/
(Python, with serial and BLE adapters used by the phone emulator, TTCN SUT
bridge and unit tests). Golden
vectors and fragmentation are checked across the Python, C++ and Kotlin
implementations.
Message layer (version 1)
A reassembled message is at most 512 octets. All integers are little-endian (the app's existing serial framing convention); ETSI-encoded payloads inside (UPER VAMs, COER certificates, GeoNetworking octets) are opaque and keep their own byte order.
octet 0 opcode
octet 1 flags bit 0: FRAGMENT_FIRST, bit 1: FRAGMENT_MORE (see Fragmentation); other bits 0
octets 2-3 sequence u16, see below
octets 4- body opcode specific
Sequence: every request the phone sends carries its own counter; the micrOBU
answers with a RESULT (or the typed reply named below) carrying the same
sequence. Messages the micrOBU originates (indications, events, status) carry the
micrOBU's own counter and are never answered, except SF_IDCHANGE_EVENT which the
phone answers with SF_IDCHANGE_EVENT_RESPONSE echoing the event's sequence.
Phone → micrOBU
| Opcode | Name | Reply | Standard basis |
|---|---|---|---|
| 0x01 | STATION_CONFIGURE | RESULT + station info | project management (MIB values the micrOBU cannot know) |
| 0x02 | POTI_UPDATE | none (RESULT only on error) | EN 302 890-2 Table 2 (NF-SAP) and Table 3 (SF-SAP) minimum data sets, plus speed/heading for the GN position vector |
| 0x03 | BTP_DATA_REQUEST | RESULT | TS 102 723-11 clause 5 / TS 103 836-5-1 Annex A.2; VBS PCI TS 103 300-3 Table 4 |
| 0x04 | CREDENTIALS_PROVISION | RESULT + apply report (last segment) | DEC-VBS-004: one VCR1 bundle (vanetza_idf/credentials.hpp) |
| 0x05 | CREDENTIALS_ERASE | RESULT | project |
| 0x06 | SF_IDCHANGE_SUBSCRIBE | RESULT + subscription handle | TS 102 723-9 Tables 10/11 |
| 0x07 | SF_IDCHANGE_UNSUBSCRIBE | RESULT | Tables 14/15 |
| 0x08 | SF_IDCHANGE_EVENT_RESPONSE | none | Table 13 |
| 0x09 | SF_IDCHANGE_TRIGGER | RESULT | Tables 16/17 |
| 0x0A | SF_ID_LOCK | RESULT + lock handle | Tables 18/19 |
| 0x0B | SF_ID_UNLOCK | RESULT | Tables 20/21 |
| 0x0C | STATUS_REQUEST | STATUS | project |
micrOBU → phone
| Opcode | Name | Standard basis |
|---|---|---|
| 0x80 | RESULT | project (the library's Result plus link codes) |
| 0x81 | BTP_DATA_INDICATION | TS 103 836-5-1 Annex A.3 with the GN-DATA.indication parameters it preserves |
| 0x82 | SF_IDCHANGE_EVENT | TS 102 723-9 Table 12 |
| 0x83 | MF_SET_REQUEST | TS 103 175 Table 11 (DCC F-Params of Table 13) |
| 0x84 | STATUS | project |
Bodies
RESULT (0x80): u8 result, u8 detail_length, detail[]. result is
vanetza_idf::Result (0 accepted, 1 invalid_argument, 2 unsupported, 3
wrong_entry_point, 4 security_unavailable, 5 resource_limit, 6 rejected, 7
time_regression, 8 identity_change_pending) or a link code: 0x10
unknown_opcode, 0x11 malformed, 0x12 not_configured (no STATION_CONFIGURE
yet), 0x13 busy, 0x14 no_credentials. detail depends on the request:
| Request | detail |
|---|---|
| STATION_CONFIGURE | u8[8] gn_address (as on the wire, TS 103 836-4-1 clause 6.3), u8[8] identifier (HashedId8 of the current authorization ticket, zeros when unsecured), u8 credentials_loaded, u8 tickets |
| CREDENTIALS_PROVISION (last segment) | u8 roots, u8 authorities, u8 tickets accepted (ApplyReport) |
| SF_IDCHANGE_SUBSCRIBE | u64 subscription |
| SF_ID_LOCK | u64 lock_handle |
| others | empty |
STATION_CONFIGURE (0x01): u8 station_type (TS 102 894-2 StationType, 2 =
cyclist), u8 security (0 unsecured, 1 secured: itsGnSecurity), u8 address_configuration (0 AUTO with the given MID, 1 ANONYMOUS: MID from the
ticket digest, TS 103 836-4-1 clause 10.2.1.4), u8[6] mid (AUTO only),
u8 beaconing (0/1, GN-MGMT beacons), u16 channel_number (ITS-G5 channel,
180 = 5 900 MHz), u8 transmit_power_dbm, u8 radio (0 off, 1 receive only, 2
transmit and receive — laboratory transmission stays an explicit choice), u8 default_traffic_class (raw TC octet used when a request carries none), u8 default_lifetime (raw GN Lifetime octet). Configuring (re)creates the stack on the micrOBU;
a request before it is answered not_configured.
POTI_UPDATE (0x02): u64 timestamp (TimestampIts, TAI ms since
2004-01-01; also synchronises the micrOBU's ITS clock), i32 latitude, i32 longitude (1/10 µdeg, WGS84), u16 semi_major, u16 semi_minor (cm, 95 %
horizontal confidence ellipse), u16 orientation (0.1°, semi-major axis from
north), u8 flags (bit 0 altitude present, bit 1 speed present, bit 2 heading
present, bit 3 position accuracy indicator), i32 altitude (cm), u16 speed
(0.01 m/s), u16 heading (0.1°). The micrOBU answers only when it rejects the
update (a RESULT with time_regression or invalid_argument); accepted
updates are silent so a 10 Hz PoTi does not double the link traffic.
BTP_DATA_REQUEST (0x03) (TS 103 300-3 Table 4 order):
u8 btp_type 0 BTP-A, 1 BTP-B
u16 destination_port
u16 destination_port_info (BTP-B) or source_port (BTP-A)
u8 gn_packet_transport_type 1 SHB, 2 GBC (3 TSB, 4 GAC, 5 GUC are refused: unsupported)
u8 gn_communication_profile 0 unspecified, 1 ITS-G5, 2 LTE-V2X
u8 gn_security_profile 0 not given (station configuration), 1 unsecured, 2 secured
u8 gn_traffic_class raw TC octet (TS 103 836-4-1 clause 9.7.5); 0xFF = station default
u8 gn_maximum_packet_lifetime raw Lifetime octet (clause 9.6.4); 0xFF = station default
u8 gn_maximum_hop_limit 0 = station default
u16 gn_repetition_interval_ms 0 = no repetition
u16 gn_repetition_maximum_ms
u32 its_aid ITS-AID (TS 102 965), the SN-ENCAP input
u8 permissions_length SSP octets (TS 103 097 / TS 102 965) …
u8 permissions[]
u8 context_length SN-ENCAP context_information (vam_cluster = 01) …
u8 context[]
if GBC: u8 shape (0 circle, 1 rectangle, 2 ellipse), i32 latitude, i32 longitude,
u16 distance_a, u16 distance_b (m), u16 angle (°)
u16 length of the FL-SDU (the VAM)
u8 fl_sdu[length]
The micrOBU hands this to NF_SAP::BTP_DATA_request_submit. RESULT.result is
the library's acceptance (accepted = submitted for protocol processing, not
proof of transmission). A secured request whose signing the entity refuses
(no valid ticket, identifier change pending) is visible in STATUS counters.
BTP_DATA_INDICATION (0x81): u8 btp_type, u16 destination_port, u16 destination_port_info (or source_port), u8 gn_packet_transport_type, u8 gn_traffic_class, u8 gn_remaining_packet_lifetime (raw, 0xFF absent), u8 gn_remaining_hop_limit (0xFF absent), u8[8] source_gn_address, u32 source_timestamp (TST ms), i32 source_latitude, i32 source_longitude,
u8 security_report (0 unsecured, 1 + VerificationReport ordinal: 1
Success, 2 False_Signature, …), u32 its_aid (0 when absent), u8 permissions_length, permissions[], u8 certificate_present, u8[8] certificate_id, u8 area_present, [u8 shape, i32 lat, i32 lon, u16 a, u16 b, u16 angle], u16 length, received_fl_sdu[].
CREDENTIALS_PROVISION (0x04): u16 total_length, u16 offset, u8 segment_length, segment[]. The segments of one bundle arrive in order;
offset + segment_length == total_length ends it. The micrOBU decodes the bundle,
applies it through the security entity's checks (security::apply), saves it
in the NVS credential store when every item was accepted, and rebuilds the
station with it. Keys travel in the clear: the transport must be a trusted
one (a paired, encrypted BLE link; a USB cable on the bench).
SF_IDCHANGE_SUBSCRIBE (0x06): u8 subscriber_data_length,
subscriber_data[]. SF_IDCHANGE_UNSUBSCRIBE (0x07): u64 subscription.
SF_IDCHANGE_EVENT (0x82): u64 subscription, u8 command (0 PREPARE, 1
COMMIT, 2 ABORT, 3 DEREG), u8[8] id (HashedId8 of the next ticket), u8 subscriber_data_length, subscriber_data[]. SF_IDCHANGE_EVENT_RESPONSE
(0x08): u64 subscription, u8 return_code — sent for PREPARE and COMMIT
with the event's sequence; the service's response timeout (500 ms) applies
across the link. SF_IDCHANGE_TRIGGER (0x09): empty. SF_ID_LOCK (0x0A):
u8 duration_seconds. SF_ID_UNLOCK (0x0B): u64 lock_handle.
MF_SET_REQUEST (0x83): u32 fac_id, u8 command_ref, u8 count, then
count × (u8 f_param_no, u32 value) with TS 103 175 Table 13 numbers (0
CHANNEL_NUMBER, 1 AVAILABLE_RESOURCE). The micrOBU sends it only for measurements
its access layer actually made; it never invents a channel load (ACT-021).
STATUS (0x84): u32 uptime_ms, u8 configured, u8[8] gn_address,
u8[8] identifier, u8 change_pending, u8 tickets, u32 signed_messages,
u32 refused_no_ticket, u32 refused_change_pending, u32 refused_permission, u32 sign_failed, u32 verified, u32 rejected
(SN-DECAP rejections of every kind), u32 requests_accepted, u32 requests_refused, u32 indications, u32 radio_submitted, u32 radio_failed, u32 radio_received, u32 radio_dropped, u32 link_rx_frames, u32 link_crc_errors, u32 link_malformed, u32 poti_updates, u64 its_time (current micrOBU ITS clock, ms; 0 before the first
POTI_UPDATE). The micrOBU sends it every second unsolicited and on STATUS_REQUEST.
Fragmentation
A message longer than the transport's unit (ATT_MTU − 3 for a notification; a GATT client's long write handles 512 B values itself) is sent as fragments, each with the full 4-octet header and the same sequence: the first with FRAGMENT_FIRST, every one but the last with FRAGMENT_MORE. The receiver concatenates the bodies. The serial transport carries whole messages and never sets these bits.
Transports
Serial (bench and conformance tests)
Native USB Serial/JTAG of the micrOBU's ESP32-C5 (VID 303A, PID 1001), the app's Phase 03
framing (obu-firmware/main/serial_link.h, SerialFrame.kt):
[0xAA][0x55][type:1][length:2 LE][payload][crc16:2 LE]
CRC-16/CCITT-FALSE (poly 0x1021, init 0xFFFF) over type + length + payload. Frame types of this firmware:
| Type | Payload |
|---|---|
| 0x10 | one link message (above), ≤ 512 octets |
| 0x11 | test channel request (PC → micrOBU) / reply (micrOBU → PC), ≤ 1 536 octets, only in a test build |
| 0x7F | UTF-8 log line from the firmware (ESP_LOG output; the console stays free of binary frames) |
The maximum frame payload is 1 536 octets (a mirrored GNPDU can exceed 512). Frames the phone does not know are ignored; text outside frames (ROM boot messages) is skipped by the sync/CRC search.
BLE GATT (production transport)
The transport uses the Nordic UART Service UUID layout so standard BLE tools can inspect it. It is a byte transport for the project message layer, not the Nordic UART wire protocol:
| GATT object | UUID | Properties | Direction |
|---|---|---|---|
| Primary service | 6e400001-b5a3-f393-e0a9-e50e24dcca9e |
primary | — |
| RX/request | 6e400002-b5a3-f393-e0a9-e50e24dcca9e |
write, write without response | phone → micrOBU |
| TX/notification | 6e400003-b5a3-f393-e0a9-e50e24dcca9e |
notify | micrOBU → phone |
The peripheral advertises the service and the name micrOBU-XXXX, where the
suffix is derived from its Bluetooth MAC. GATT access requires an authenticated,
encrypted connection. The ESP32 uses LE Secure Connections, bonding and a
fresh six-digit passkey printed to the USB log (DisplayOnly); NimBLE persists
the bond in NVS. Android presents the normal system pairing UI and retains the
bond. This protects the credential bundle in transit; it is not application-
layer encryption.
Android requests an ATT MTU of 517; the ESP32 preference is 512. The Android
writer uses ATT_MTU - 3, capped at 512, after negotiation (509 octets with
the current ESP32 configuration) and falls back to 20 octets.
The ESP32 notification path deliberately emits values of at most 20 octets so
it also works at the mandatory default MTU. Every value uses the fragmentation
flags above and repeats the link header; a complete message is limited to 512
octets. Writes are acknowledged and serialized. Notifications are enabled
before the Android adapter reports READY. The USB test channel is never
exposed over BLE.
Test channel (test firmware only, USB only)
CONFIG_MICROBU_TEST_CHANNEL compiles a software lower tester and the
diagnostic hooks the ETSI campaigns need, behind frame type 0x11. It is not
part of the phone interface and never exists on BLE. Requests: u8 command,
reply u8 result, u8 record_count, records [kind u8][length u16 LE][bytes]:
| Command | Body | Effect |
|---|---|---|
| 0x01 MIRROR | u8 mode (0 off, 1 mirror, 2 divert) |
every AL_DATA.request the stack makes is copied (mirror) or kept from the radio (divert) and queued as kind-1 records |
| 0x02 INJECT | u8[6] source, u8[6] destination, gnpdu[] |
AL_DATA.indication from the lower tester into the stack |
| 0x03 GN_REQUEST | u8 traffic_class, payload[] |
GN-DATA.request SHB with next header "Any" (the Security ATS GN-MGMT/GENMSG cases; N-SAP, not a phone primitive) |
| 0x04 DRAIN | – | returns the queued records: kind 1 AL_DATA.request (the GNPDU as submitted), kind 2 BTP-DATA.indication ([type][dest port][port info][SDU]), kind 3 GN-DATA.indication payload |
| 0x05 RESET | – | drops the queue and the mirror mode |
The queue is bounded (32 records, 8 KiB); an overflow is reported as
resource_limit on the next DRAIN and the records lost are counted.
Phone emulator, SUT bridge and campaigns (PC side)
python/microbu_link/ is the phone side of the message layer (codec, serial
transport, request/reply client, the small VBS of vbs.py), shared by:
python/phone_emulator.py— the phone half of the VRU ITS-S on a PC: configures the micrOBU, provisions aVCR1bundle, feeds PoTi (static or a bicycle on a circle), assembles VAMs with asn1tools from the ETSI ASN.1 of the submodule and hands them over with the PCI of TS 103 300-3 Table 4 under the generation rules of clause 6.4, subscribes to the identifier-change events (stop on PREPARE, new StationId after COMMIT), prints the indications, status and log lines;--divert --pcaprecords what the micrOBU signs for an independent verifier. Its default synthetic trajectory is a 20 m-radius circle at 2 m/s centred at 53.5652470 N, 10.0082390 E over the Außenalster's open water;--lat,--lon,--radiusand--speedoverride it. Radio emission remains opt-in through--radio txrx. The center is not itself a transmitted position: generated positions lie on the circle perimeter. The firmware's standalone BOOT-button VAM is a separate fixed payload and does not use these phone-emulator coordinates.python/sut_bridge.py— the SUT process of the vanetza-idf ETSI adapters (hex-line protocol ofhil_sut.hpp) with the micrOBU behind the phone interfaces: BTP-DATA.request, PoTi and credentials over the link, the software lower tester over the USB test channel, CAM/DENM stimuli assembled on the PC.--listen host:portserves the protocol over TCP;python/sut_tcp_relay.pyinside WSL is the SUT executable that forwards it (WSL interop drops piped stdin of Windows executables).
# Windows: the micrOBU on COM11, the campaign pool packed as a bundle
python sut_bridge.py --port COM11 --bundle pool.vcr --listen 0.0.0.0:7788
LinkClient is transport-independent. SerialTransport and BleTransport
implement the same TransportAdapter.write(frame_type, payload) / close()
boundary and feed the same receive callback; configure, PoTi, credential,
BTP-DATA, status and event code is therefore identical. Select only the adapter
at the command line:
cd implementation\station-link\python
python -m pip install -r requirements.txt
# Safe first BLE run: hand VAMs to an unsecured station, with ITS-G5 RF off.
python phone_emulator.py --ble --unsecured --radio off --period 1000 --duration 10 --verbose `
--report ble-vam-session.json
# Select a particular advertisement when several micrOBUs are present.
python phone_emulator.py --ble micrOBU-1234 --unsecured --radio off --duration 10
For the signed run, replace --unsecured with --bundle <path-to-chain.vcr>
(or use credentials already retained in the micrOBU NVS). Change --radio off
to --radio txrx only for the intended laboratory RF test. The USB test
channel has no BLE equivalent, so --mirror, --divert and --pcap are
rejected with --ble; all station-link/VAM behavior above that adapter is the
same.
Instead of an offline --bundle, --pki-url makes the emulator obtain an
enrolment credential and authorization ticket from tools/local_pki.py, build
the same VCR1 bundle, and provision it before starting the VBS. Supply
--pki-root, --pki-ea, --pki-aa, --pki-canonical-key, --pki-its-id,
--pki-issue-tool, --pki-client-tool, and --pki-bundle-tool together; use
--pki-hours to override the 24-hour AT validity. The message flow and retained
host-side proof are documented in docs/idf/evidence/enrolment-authorization-01
and the EA/AA section of docs/idf/test-campaigns.md in vanetza-idf.
local_pki.py is a separate localhost HTTP process, normally bound to
127.0.0.1; it is not imported into or hosted by the emulator. Consequently,
the lab authority can later be replaced by a hosted PKI without changing the
phone flow: set --pki-url to the hosted access point and keep the same S3/S4
HTTP interface.
End-to-end hardware workflow: Online PKI, Transmission & Reception
This workflow runs the full online TS 102 941 enrolment/authorization and 5.9 GHz ITS-G5 over-the-air transmission between two ESP32-C5 boards:
- COM4 (TX): micrOBU running firmware microbu_firmware.bin
- COM5 (RX): independent receiver running 2x2map-0.3.0 bridge firmware
1. Initialize the lab PKI hierarchy (run once)
`powershell $dir = "C:\microbu-pki-wt\pki-lab" New-Item -ItemType Directory -Force -Path "$dir\chain", "$dir\issued" & C:\Strawberry\c\bin\openssl.exe ecparam -name prime256v1 -genkey -noout -out "$dir\root.pem" & C:\Strawberry\c\bin\openssl.exe ecparam -name prime256v1 -genkey -noout -out "$dir\canonical.pem"
$issue = "C:\bachelorthesis_micrOBU\implementation\external\vanetza-idf\build-host-sec\vidf_issue.exe" & $issue root --key "$dir\root.pem" --name "lab root" --id ROOT --out "$dir\chain" & $issue authority --issuer "$dir\chain\ROOT.oer" --issuer-key "$dir\root.pem" --name "lab EA" --id EA --out "$dir\chain" & $issue authority --issuer "$dir\chain\ROOT.oer" --issuer-key "$dir\root.pem" --name "lab AA" --id AA --out "$dir\chain" `
2. Start the localhost PKI server (Terminal 1)
powershell python C:\microbu-pki-wt\implementation\external\vanetza-idf\ports\esp_idf\tools\local_pki.py
--issue-tool C:\bachelorthesis_micrOBU\implementation\external\vanetza-idf\build-host-sec\vidf_issue.exe --ea C:\microbu-pki-wt\pki-lab\chain\EA.oer
--ea-key C:\microbu-pki-wt\pki-lab\chain\EA.vkey --ea-enc-key C:\microbu-pki-wt\pki-lab\chain\EA.ekey
--canonical-key C:\microbu-pki-wt\pki-lab\canonical.pem --aa C:\microbu-pki-wt\pki-lab\chain\AA.oer
--aa-key C:\microbu-pki-wt\pki-lab\chain\AA.vkey --aa-enc-key C:\microbu-pki-wt\pki-lab\chain\AA.ekey
--dir C:\microbu-pki-wt\pki-lab\issued --port 8092
3. Start the independent v2x2map receiver (Terminal 2)
powershell python C:\microbu-pki-wt\tools\vendor\v2x2map-0.3.0\bridge\its_g5_bridge.py
--port COM5 --node-id V2X2MAP:001122334455
--no-mqtt --open-browser
--pcap-out C:\microbu-pki-wt\rx_v2x2map.pcap
`
- Automatically opens the live dashboard at http://127.0.0.1:8080.
- --pcap-out <file.pcap> continuously records all received 802.11 frames to disk.
- Alternatively, click Record and Stop in the web dashboard: a direct Download PCAP button appears to download the file, which is also saved under ools/vendor/v2x2map-0.3.0/bridge/recordings/.
4. Transmit signed VAMs via Phone Emulator (Terminal 3)
powershell python C:\microbu-pki-wt\implementation\station-link\python\phone_emulator.py
--port COM4 --radio txrx
--mirror --pcap C:\microbu-pki-wt\tx_vams.pcap
--pki-url http://127.0.0.1:8092/ --pki-root C:\microbu-pki-wt\pki-lab\chain\ROOT.oer
--pki-ea C:\microbu-pki-wt\pki-lab\chain\EA.oer --pki-aa C:\microbu-pki-wt\pki-lab\chain\AA.oer
--pki-canonical-key C:\microbu-pki-wt\pki-lab\canonical.pem --pki-its-id vidf-vru-station
--pki-issue-tool C:\bachelorthesis_micrOBU\implementation\external\vanetza-idf\build-host-sec\vidf_issue.exe --pki-client-tool C:\microbu-pki-wt\implementation\external\vanetza-idf\ports\esp_idf\tools\pki_client.py
--pki-bundle-tool C:\microbu-pki-wt\implementation\external\vanetza-idf\ports\esp_idf\tools\credential_bundle.py --duration 30
- --mirror --pcap tx_vams.pcap ensures the transmitter saves an exact PCAP capture of all generated and signed GNPDUs.
- Omit --duration 30 to transmit continuously until Ctrl+C.
Where recordings are saved
| Recording | Destination | Description |
|---|---|---|
| Transmitter (TX) PCAP | --pcap | Local 802.11 PCAP captured via the USB test channel (--mirror copies all frames while the radio transmits). |
| Receiver (RX) PCAP (CLI) | --pcap-out | Continuous capture of all over-the-air frames received by the ESP32-C5 on COM5. |
| Receiver (RX) PCAP (Web) | ools/vendor/v2x2map-0.3.0/bridge/recordings/ | Saved automatically when clicking Record and Stop in the web dashboard, with a direct Download PCAP button in the browser. |
# WSL: the security campaigns with the compiled AtsSecurity (docs/idf/test-campaigns.md of the submodule)
python3 run_etsi.py --titan ... --binary /root/vidf-ats-security/AtsSecurity \
--config tests/etsi_security_gn.cfg --expected-cases tests/etsi_security_gn_cases.json \
--sut /usr/bin/python3 --sut-args "/root/sut_tcp_relay.py --connect <windows host>:7788" \
--pool /root/vidf-pool --out /root/vidf-results/security-gn-microbu
Retained runs of 2026-09-14: experiments/raw/microbu-link-20260914/
(manifests in experiments/manifests/test-runs/microbu-link-20260914-*.yaml).
BLE verification (2026-09-16)
The retained host verification is described by
experiments/manifests/test-runs/microbu-link-20260916-ble-host-01.yaml:
- Python/C++ protocol and framing suite: 8 tests passed, including the common serial/BLE adapter contract and a 512-octet message split into 32 default-MTU values, reassembly, malformed interleaving rejection and recovery.
- Kotlin/JVM station-link suite: 4 tests passed against the Python/C++ golden bytes, including fragmentation, recovery and credential segmentation.
- Android
:app:compileDebugKotlin: passed with the BLE central and manifest permissions included. - ESP-IDF 6.0.2 ESP32-C5 build: passed with NimBLE and the encrypted GATT
peripheral; the binary is
0x1d8780bytes and leaves 38% of the app partition.
No ESP32 serial device or Pixel 9 was attached to this laptop during this run,
so pairing, notification exchange, the <= 2 s reconnection requirement and the
connection-loss-rate requirement were not measured. TEST-AC-SYS-MICROBU-013 and
TEST-AC-SYS-MICROBU-014 intentionally remain planned hardware tests.