CurvatureCalculationMode is the one extensible ENUMERATED in CAM:
ENUMERATED {yawRateUsed(0), yawRateNotUsed(1), unavailable(2), ...}
UPER encodes an extensible ENUMERATED as an extension bit followed by the root
index - 1 + 2 = 3 bits. All three of our encoders wrote only the 2-bit index,
shifting yawRate and the entire low-frequency container one bit early for any
standards-compliant receiver.
It went unnoticed because every end of this project shared the mistake: the
Kotlin codec was ported bit-for-bit from cam.c, so phone and ESP32 agreed
perfectly with each other and with nothing else. Confirmed against the ETSI
ASN.1 in the C-ITS-Parser checkout, where rasn marks this type - and only this
type - #[non_exhaustive].
Fixed in all three copies of the encoder (app CamUperCodec.kt,
obu-firmware/main/cam.c, obu-cam-transmistter/main/cam.c) plus the decoder,
which now rejects rather than misreads a set extension bit. Frame size is
unchanged at 43 bytes. Transmitter reflashed and the phone decodes its CAMs.
Also in this change:
- serial_link: skip send_frame entirely when no USB host is attached, and raise
the tx mutex timeout above the worst-case hold. With the phone unplugged every
write blocked its full timeout while holding the lock, so forwarded CAM_RX
traffic starved the 1 Hz heartbeat - observed as "tx mutex timeout, dropping
frame" on the console, and it would have tripped the phone's link watchdog.
Verified gone on hardware.
- Log decoded and failed CAMs in CamUseCaseRepository. "The app shows nothing"
had two indistinguishable causes; a silent `?: return` made this bug much
harder to find than it needed to be.
- Remove the ESP32 send-only/send-and-receive toggle. Reception can't be
disabled in firmware (raw TX only works while promiscuous), so it was an
app-side filter pretending to be a radio control.
- V2X monitor follows the serial link state on the ESP32 path instead of MQTT,
which is permanently disconnected there; CAM intake is gated on the link being
up, and engine state is cleared when it drops.
- About screen: 0.5.0, Phase 03.
- Track obu-cam-transmistter, the bench CAM transmitter. Its cam.c is compiled
(unlike obu-firmware's reference copy) and must stay bit-identical to the other
two - this commit is what that coupling costs when it's broken.
- Document the two-toolchain split: this project builds on IDF 5.5.4, obu-firmware
on the pinned 6.1. Exporting both in one shell fails confusingly.
2.6 KiB
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:
$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, thephy_11p_set/phy_change_channel(5900,...)register hack, GPIO polling, TX loopmain/denm.c/.h- ASN.1 UPER encoding of a minimal DENMmain/geonet.c/.h- GeoNetworking Basic/Common/SHB headers + BTP-Bmain/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).