Both firmware fixes are now confirmed over the air, with the sniffer board
capturing and asn1tools judging the result.
GN lifetime: our station transmits 0x05 (1 s), the value both bench stations
use, where the August captures show 0x83 (3200 s) for the same frames. 397 CAMs
from station 999999 decode and re-encode byte-identically, so the whole
transmit chain is right on the wire, not only in the host tests.
yawRateConfidence: the bench beacon, flashed to a spare board, sends CAMs that
decode and re-encode byte-identically as well (72 of 72 from station
0x0BADC0DE). NOTES.md now says how to flash that beacon and how to check what
it sends, including that it shares the phone pinger's MAC and the two are told
apart by station ID.
The new firmware also runs on the OBU with the phone attached: CAM, DENM and
SPATEM from the bench stations all keep decoding in the app now that messages
are cut to the length their header declares.
Signed reception stays open. The CiT One transmits unsigned and nothing else
here signs, so it needs real roadside traffic, the CiT One switched to signed
mode, or a replay firmware on a spare board.
Two bench facts worth not rediscovering are recorded too: opening COM3's
console resets the OBU and drops the phone's USB link, and every capture taken
before today is truncated at its first corrupted record, because the receiver's
console inserts a CR before every 0x0a byte of the binary pcap stream.
live_capture.py now undoes that, a change that lives in the receiver repo and
is not part of this commit.
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.