The messaging package provides transport-independent publish/subscribe interfaces, versioned domain contracts, message dispatch, and the current ZeroMQ transport. Applications should consume public domain messages rather than reaching directly into hardware-owning controllers when telemetry is available on the bus.
If you only want to consume telemetry, start with Subscribe to one topic below. A new consumer should not need to understand ZeroMQ socket internals, hardware adapters, or the producer implementation.
The broker is intentionally dumb. Topic ownership, schema validation, units, and typed decoding belong to messaging/contracts.
The intended boundary is:
A subscriber therefore does not need to know whether a message came from physical hardware, a simulator, a replay tool, or another process.
From the repository root, use three terminals.
Terminal 1, start the broker:
Terminal 2, start the navigation service with simulated hardware:
The service emits:
openroad.navigation.positionopenroad.navigation.motionopenroad.navigation.attitudeopenroad.navigation.imuIt also serves acknowledged navigation commands such as stationary calibration and heading reset, route start/cancel, and local route playback. See Navigation command IDD for wire fields and failures, and services/navigation/README.md for runtime ownership and startup.
Terminal 3 can run an existing consumer, for example:
or the CarTUI:
The same subscriber code works when the simulated navigation controller is replaced by real hardware in the service.
Default local connections are:
tcp://127.0.0.1:5556tcp://127.0.0.1:5557The broker is the process that binds the TCP ports. Publishers and subscribers connect to it. Use constants from messaging.zeromq.endpoints in application code instead of copying port numbers.
For application code, prefer MessageDispatcher. It owns the blocking receive loop, decodes payloads, and invokes handlers on worker threads.
The three pieces a normal subscriber supplies are simply:
Register every topic before calling start().
One dispatcher can subscribe to many topics over one subscriber connection:
A single MessageDispatcher per application is normally preferable to one subscriber thread per topic.
For a latest-value application cache, use the shared thread-safe consumers in common.telemetry: NavigationBusState and VehicleBusState. This keeps message callbacks independent of Tk, curses, or another application package.
| Topic | Contract | Purpose |
|---|---|---|
openroad.vehicle.state | automotive vehicle state | Engine, speed, gear, ECU control, pressures, temperatures, fuel and electrical state |
openroad.vehicle.trip.state | automotive Trip state | Accumulated timing, distance, speed, fuel, boost, high-load and position analytics |
openroad.navigation.position | navigation position | Geographic position, altitude, GPS fix and accuracy metadata |
openroad.navigation.motion | navigation motion | Heading, ground/vertical speed and turn rate |
openroad.navigation.attitude | navigation attitude | Heading, pitch and roll |
openroad.navigation.imu | navigation IMU | Acceleration, linear acceleration and angular velocity vectors |
Public telemetry contracts use SI units unless explicitly documented otherwise. Presentation layers should perform conversions such as m/s to mph or Pa to PSI. Shared presentation conversions live in common.units so applications do not duplicate unit math.
Domain publishers accept normalized state and hide wire encoding from producers. For navigation, normal runtime ownership belongs to the navigation service:
The service owns one NavigationControllerIf and fans each state sample out to position, motion, attitude, and IMU contracts. The same controller instance handles acknowledged navigation commands, so applications never need a second hardware-owning controller.
The lower-level NavigationStatePublisher remains available to producers and component tests that intentionally own a controller.
PUB/SUB is intended for continuously changing state. Commands that require acknowledgement use an explicit request/reply service instead of being disguised as telemetry messages. Navigation currently follows this pattern for stationary calibration and heading reset. Applications depend on ui.navigation.NavigationRequestHandlerIf; the current process transport is ZeroMqNavigationRequestHandler talking to the navigation service.
See services/navigation/README.md for examples.
MessageDispatcher handlers run on worker threads. Do not directly modify Tk, curses, or another toolkit's UI objects from a handler. Update a thread-safe state/cache in the handler, then render or marshal that state from the UI thread.
common.telemetry.VehicleBusState and common.telemetry.NavigationBusState implement this pattern for the current vehicle and navigation contracts.
ZeroMQ PUB/SUB is asynchronous. A publisher may send messages before a subscriber's subscription has propagated, and those early messages can be dropped. For deterministic manual tests use this startup order:
Continuous telemetry publishers naturally tolerate late subscribers because another sample will arrive shortly.
The bus is intended for continuously changing telemetry. Do not assume PUB/SUB provides queue durability or replay semantics. If a future domain requires guaranteed delivery, history, or command acknowledgement, that requirement should be designed explicitly rather than quietly inferred from telemetry behavior.
MessageDispatcher can receive an application error callback:
Decode errors and handler failures are reported with the associated topic. Transport receive failures use the synthetic topic name receive.
A public contract should normally contain:
docs/messaging/message_bus_idd.mdKeep hardware parsing out of messaging contracts and keep UI formatting out of them. The bus carries domain data, not implementation details or presentation choices.
Useful checks from the repository root include:
Component-test CLIs under messaging/component_test are useful for live diagnostics but supplement rather than replace automated tests.
See docs/messaging/message_bus_idd.md for framing, topology, lifecycle, contract rules, and topic ownership details.