These have been red since the initial commit. All four failed for the same reason, and in every case the test was wrong rather than the detector. EventDetector requires two conditions to hold on the SAME frame, and one of them is a rolling-window statistic that takes time to respond. The tests ignored that lag, so they described situations the detector cannot see - and could not have seen at any point in its history. Braking (3 tests). Each filled the accel window with a CONSTANT value, then stepped the GPS speed down. The speed drop is therefore true on exactly one frame, and on that frame the accel std dev is still ~0.75 against a 1.2 threshold, because the window is full of the constant. By the time the window recovers (frame 4), prevSpeedMps has caught up and the drop is 0. The two conditions never coincide and no BRAKING is possible. Constant accelerometer output right up to the instant of a brake is not physical either: the IMU is sampled continuously while GPS speed lags, so the shaking precedes the reported drop. The tests now establish that variability during the cruise phase. Stopping. The second episode ran for stoppingFrames + 5. But stopping requires the accel std dev to be BELOW a threshold, and the window still held the five moving samples from the acceleration burst between episodes. Those take 8 frames to drain far enough for the std dev to fall under 0.15, leaving 17 of the 21 frames the event needs. The first episode is unaffected because the window starts empty. The episode is now long enough to cover the settling time. Verified by simulating the detector's exact arithmetic against both the old and new inputs before touching the file: the old profiles produce no events, the new ones produce BRAKING/HIGH, BRAKING/HIGH, BRAKING/MEDIUM and two STOPPING. No production code changed - the detector behaves consistently and defensibly. Assertions are unchanged; only the stimulus is now something a bicycle could actually produce. Suite is 41 tests, 0 failures.
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 serves as the HMI for the micrOBU hardware, handling V2X message display, sensor data collection, trip recording, and OBU communication over USB-C, Wi-Fi (dev), and Bluetooth (upcoming).
Platform: Android (Kotlin) · Min SDK: 29 (Android 10) · Target SDK: 36
What it does
Real-time V2X monitoring; subscribes to the OBU's MQTT broker and displays live CAM, DENM, SPAT, MAP, and CPM messages grouped by topic with pretty-printed JSON and TX/RX badges.
DENM transmission; triggers DENM use cases (e.g. stationary vehicle warning hln-sv) on the OBU via the consider it Use Case API (v2x-uca/input/denmtrg) with a single tap.
Sensor monitoring; live readout of phone GNSS, accelerometer, gyroscope, magnetometer, and barometer alongside OBU GNSS for cross-reference.
Trip recording; foreground service records all sensor streams and detects cycling events (braking, turning, stopping) using orientation-independent signal processing. Works fully offline with no OBU connected.
Trip review; past trips displayed on an OpenStreetMap layer with detected events overlaid as coloured pins. Tap any pin for event details.
CSV export; every sensor sample written to a timestamped CSV in real time during a session. 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, V2X Monitor, Sensors, Recording, Trip History, Settings…)
ui/navigation/ Navigation graph and bottom nav bar
viewmodel/ MqttViewModel, SensorViewModel, TripRecordingViewModel
data/mqtt/ MQTT repository, Paho client, exponential-backoff reconnection
data/transport/ USB tethering detection and gateway IP resolution
data/db/ Room database (sessions, trips, detected events)
data/ SensorRepository, TripRepository, CsvExporter
domain/detection/ EventDetector, RunningStats sliding window (orientation-independent)
service/ TripRecordingService (foreground service)
Connectivity
The app uses a phased transport strategy. The MQTT client, topic subscriptions, and all UI are identical across transports; only the underlying network path changes.
| Phase | Transport | Status |
|---|---|---|
| Phase 01 | Wi-Fi | Complete |
| Phase 02 | USB-C tethering | Active |
| Phase 03 | Bluetooth BLE | Future |
The MQTT broker runs on the OBU hardware (Mosquitto 2.0.11, port 1883). In Phase 02, Android USB tethering exposes the OBU as a virtual Ethernet interface at 192.168.42.x. The app auto-detects the gateway IP on plug-in.
Key dependencies
| Library | Purpose |
|---|---|
| Jetpack Compose + Material3 | UI |
| Eclipse Paho MQTT | OBU communication |
| Room | Local database |
| Hilt | Dependency injection |
| OSMDroid | Trip review map |
| DataStore | Settings persistence |
| FusedLocationProviderClient | GNSS |
Getting started
- Open in Android Studio (Hedgehog or newer).
- Connect a device running Android 10+ (API 29).
- Build and run the
appmodule. - For Phase 02 testing: plug the phone into the OBU via USB-C, enable USB tethering on the phone, and the app will detect the interface and connect automatically. Broker IP can be overridden manually in Settings → Connection.
- For standalone trip recording: no OBU required. Go to the Record tab and tap Record.
The Wi-Fi transport (Phase 01 broker at 192.168.3.202) remains available in developer builds and can be toggled in Settings → Developer.
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 future antenna coordination all run on the phone to keep the OBU lightweight and power-efficient.
V2X communication uses ITS-G5 (IEEE 802.11p / DSRC) at 5.9 GHz. The app communicates with the OBU exclusively via the consider it MQTT API v6 (processed JSON messages); no ASN.1 encoding in the app.
Owner: HAW Hamburg