cereal is the inter-process communication backbone of openpilot. Every piece of data that crosses a process boundary — camera metadata, CAN frames, model predictions, control commands, driver monitoring state — is serialised as a cerealDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/commaai/openpilot/llms.txt
Use this file to discover all available pages before exploring further.
Event message and exchanged over named sockets. There is no shared global state between processes; all coordination is explicit and typed.
What is cereal?
cereal is a thin layer on top of two open standards:- Cap’n Proto — a schema language and zero-copy binary serialisation format. All message types are defined as Cap’n Proto structs in the
cereal/directory of the openpilot repository. - msgq — a shared-memory pub/sub transport developed by comma. Unlike ZeroMQ, msgq uses POSIX shared memory segments so subscribers can read the latest message without a system call in the common case.
Event (defined in cereal/log.capnp). Every Event has a logMonoTime (nanoseconds since boot) and a valid flag. The payload is a union field — exactly one of the many named message types is set per event. Service names (e.g. modelV2, carState) map directly to fields in the Event union.
The full list of services, their expected publishing frequency, and their queue sizes are defined in cereal/services.py.
Transport: msgq shared memory
msgq creates a named shared-memory segment for each service. The publisher writes serialised Cap’n Proto bytes into the segment using a ring buffer. Subscribers map the same segment and read directly from it. APoller allows a subscriber to sleep until one of several sockets has a new message, avoiding busy-waiting.
ZeroMQ bridging is also available (cereal/messaging/bridge_zmq.cc and msgq_to_zmq.cc) for tools that need to consume or inject messages over a network, but all on-device communication uses the native msgq backend.
Queue sizes are set per service in cereal/services.py:
| Constant | Size | Used for |
|---|---|---|
QueueSize.BIG | 10 MB | Video frames, raw model outputs, can bus |
QueueSize.MEDIUM | 2 MB | High-frequency streams: controlsState, sendcan |
QueueSize.SMALL | 250 KB | Most services |
Schema files
| File | Contains |
|---|---|
cereal/log.capnp | The main Event union and all driving/system message structs |
cereal/car.capnp | Car-specific types: CarState, CarControl, CarParams |
cereal/custom.capnp | Reserved slots for fork-specific extensions without breaking compatibility |
cereal/deprecated.capnp | Structs retained only for log backwards-compatibility |
steeringAngleDeg is explicitly in degrees).
Key message types
The table below describes the most commonly referenced services. Frequencies are nominal; seecereal/services.py for the authoritative list.
| Service | Frequency | Description |
|---|---|---|
carState | 100 Hz | Vehicle state: speed, steering angle, gear, button events, CAN validity |
carControl | 100 Hz | Actuator commands: torque, steering angle, acceleration, HUD control |
carParams | 0.02 Hz (once) | Static car fingerprint: safety model, longitudinal/lateral capability flags |
carOutput | 100 Hz | What the car interface actually sent to the car (mirrors applied actuators) |
modelV2 | 20 Hz | Driving model output: predicted path, lane lines, lead vehicle, desired curvature/accel, meta |
drivingModelData | 20 Hz | Alternative model data encoding (same neural network run as modelV2) |
cameraOdometry | 20 Hz | Visual odometry pose estimate from the driving model |
driverStateV2 | 20 Hz | Raw driver monitoring model output: face pose, eye/blink probabilities, phone prob |
driverMonitoringState | 20 Hz | Processed driver monitoring: alert level, lockout status, RHD detection |
selfdriveState | 100 Hz | Engagement state (enabled, active, state enum), current alert text and sound |
onroadEvents | 1 Hz / on change | List of active OnroadEvent entries with their type flags |
controlsState | 100 Hz | Control loop internals: curvature, PID terms, long control state, lateral controller log |
longitudinalPlan | 20 Hz | Longitudinal trajectory: target acceleration, shouldStop, hasLead, fcw |
driverAssistance | 20 Hz | Lane departure warning flags: leftLaneDeparture, rightLaneDeparture |
liveCalibration | 4 Hz | Device pitch/yaw calibration status and rpyCalib angles |
liveParameters | 20 Hz | Learned vehicle parameters: steer ratio, tire stiffness, angle offset |
livePose | 20 Hz | Fused pose estimate from locationd |
liveDelay | 4 Hz | Estimated lateral and longitudinal actuator delays |
pandaStates | 10 Hz | Panda health: ignition state, safety model, fault flags, controlsAllowed |
peripheralState | 2 Hz | Peripheral info: fan speed RPM, panda type |
deviceState | 2 Hz | System health: thermal status, free space, memory usage, fan speed desired |
managerState | 2 Hz | Process liveness table: each registered process, whether it is running and should be |
can | 100 Hz | Raw CAN frames from all buses |
sendcan | 100 Hz | CAN frames to be sent to the vehicle via panda |
radarState | 20 Hz | Radar lead vehicle tracks and error flags |
accelerometer | 104 Hz | IMU accelerometer readings (m/s²) |
gyroscope | 104 Hz | IMU gyroscope readings (rad/s) |
gpsLocationExternal | 10 Hz | External GNSS fix (u-blox or Qualcomm) |
roadCameraState | 20 Hz | Road camera frame metadata: frame ID, gain, exposure |
driverCameraState | 20 Hz | Driver camera frame metadata |
wideRoadCameraState | 20 Hz | Wide road camera frame metadata |
Python API
Thecereal.messaging Python module wraps msgq and Cap’n Proto into a convenient high-level API. The two primary classes are SubMaster (subscribe to one or more services) and PubMaster (publish to one or more services).
Subscribing to messages
SubMaster manages a set of subscriber sockets, handles frequency tracking, and exposes a simple dict-like interface. The update() call blocks until any of the polled sockets has a message (or the timeout fires), then updates all non-polled sockets non-blocking.
SubMaster also tracks liveness. You can check whether all subscribed services are publishing at the expected rate:
Polling on a specific service
When one service drives your loop rate (e.g.modelV2 at 20 Hz), pass it as the poll argument. Other services are read non-blocking on each iteration.
Publishing messages
PubMaster opens a publisher socket for each named service. Use messaging.new_message to create a typed message builder, fill in the fields, and call pm.send.
Reading raw socket messages
For one-shot reads withoutSubMaster, use the low-level socket functions:
Creating a message
messaging.new_message allocates a new log.Event builder with logMonoTime set to the current monotonic clock and valid = False by default.
Cap’n Proto schema conventions
Every message inlog.capnp follows these conventions:
- All physical quantities use SI units unless the field name includes a unit suffix (e.g.
steeringAngleDeg,vCruiseKph). - All numeric identifiers (
@0,@1, …) are permanent — fields are never renumbered. This ensures old log files remain parseable. - Adding new fields to existing structs is safe. Removing or reordering fields breaks compatibility.
- Fork-specific additions belong in
cereal/custom.capnpusing the reserved struct slots to avoid conflicting with upstream schema changes.
The
logMonoTime field on every Event is nanoseconds since boot (CLOCK_MONOTONIC). It is set by the publisher at the time of message creation and is used by loggerd to order log entries and by SubMaster to check service liveness.Related references
System overview
Architecture, hardware, data flow, and design principles for the full openpilot system.
Process reference
Per-process documentation with pub/sub topic lists for every daemon.
