Documentation
Automotive Service
Source:
services/automotive/README.md
The automotive service owns a VehicleStateSourceIf and publishes complete SI-normalized VehicleState snapshots onto the OpenRoadCode ZeroMQ telemetry bus.
Applications such as Car TUI consume the public vehicle-state topic. They do not own the OBD-II adapter or simulation source.
Data flow
simulation ------------------------------\
> VehicleStateSourceIf
ELM327 -> Elm327ObdAdapter -> Obd2Manager /
|
v
AutomotiveRuntime
|
VehicleStatePublisher
|
ZeroMqPublisher
|
ZeroMQ broker
|
MessageDispatcher
|
VehicleBusState
|
Car TUI / other apps
The telemetry contract remains SI regardless of how a UI displays values. Metric/imperial conversion belongs at the presentation layer and uses common.units.
Runtime configuration
The automotive service is configured through the same runtime TOML used by the other producer services.
Simulation example:
[services.automotive]
enabled = true
rate_hz = 10.0
[services.automotive.input]
source = "simulation"
[services.automotive.publish]
enabled = true
source = "simulated-vehicle"
Physical serial ELM327 example for Linux/Raspberry Pi:
[services.automotive]
enabled = true
rate_hz = 10.0
[services.automotive.input]
source = "device"
device = "elm327"
transport = "serial"
port = "/dev/rfcomm0"
baud = 38400
timeout_s = 1.0
slow_poll_interval_s = 5.0
[services.automotive.publish]
enabled = true
source = "obd2"
Termux uses the Android Bluetooth bridge over localhost TCP rather than a local serial device:
[services.automotive]
enabled = true
rate_hz = 10.0
[services.automotive.input]
source = "device"
device = "elm327"
transport = "tcp"
host = "127.0.0.1"
tcp_port = 35000
timeout_s = 2.0
slow_poll_interval_s = 5.0
[services.automotive.publish]
enabled = true
source = "automotive-service-android"
Elm327Device owns the serial transport and Elm327TcpDevice owns the TCP
transport. Both feed the same Elm327ObdAdapter, Obd2Manager,
AutomotiveRuntime, and public VehicleState contract. This keeps the
Raspberry Pi/Linux and Termux compositions symmetric above the transport
boundary.
The manager polls RPM, vehicle speed, throttle, accelerator position, engine load, and manifold pressure on each snapshot. Slower-changing values such as barometric pressure, airflow, coolant/intake temperature, fuel level, and module voltage use slow_poll_interval_s.
Gear estimation
The automotive runtime can augment each published VehicleState with an
estimated transmission_gear. The estimator compares engine speed and road
speed against a learned ratio profile.
By default the service looks for vehicle_gears.learned.toml. A different
profile can be supplied with --gear-profile. If the profile does not exist,
gear estimation is safely disabled.
Learn a manual-transmission profile with:
python -m scripts.automotive.learn_gears
The ratio estimator identifies forward gears only. RPM and road speed alone cannot reliably distinguish neutral or reverse, and the estimator intentionally returns an unknown gear during shifts, clutch slip, very low speed, or a poor ratio match.
transmission_gear is part of the public openroad.vehicle.state schema.
After deploying a wire-contract change, restart long-running supervised
producer processes so an older service does not continue publishing the prior
schema.
Start locally
Start the ZeroMQ broker first:
python3 -m messaging.zeromq.broker_cli
Simulation:
python3 -m services.automotive.automotive_service_cli \
--config config/runtime.simulated.toml
Physical vehicle:
python3 -m services.automotive.automotive_service_cli \
--config config/runtime.toml
For a serial Bluetooth ELM327 on Linux/Raspberry Pi, /dev/rfcomm0 must already exist and be connected before starting the service. Termux instead uses the configured Android bridge TCP endpoint.
The service publishes to [messaging].publisher_endpoint at the configured rate_hz.
Consumer example
Car TUI already subscribes to vehicle telemetry through its shared VehicleBusState. With the broker and automotive service running, start it normally:
python3 -m apps.carTui.main
The Vehicle screen updates as new VehicleState messages arrive. No automotive simulation or OBD-II object is constructed inside Car TUI.
Design rule
Producer services own hardware and simulation sources. Applications consume messaging contracts. This keeps the consumer path identical between bench simulation and the vehicle:
simulation source --\
> AutomotiveRuntime -> ZeroMQ -> application
physical source ---/
Switching between simulation and physical hardware therefore changes service composition, not application code or the wire contract.