ADR-001: CAN Identifier Scheme¶
| Field | Value |
|---|---|
| Date | 2026-09-29 |
| Status | Proposed |
| Deciders | Holger Zahnleiter |
| Supersedes | — |
| Superseded by | — |
Context and Problem Statement¶
All artifacts share one CAN bus (mission statement, principle One bus: Classic CAN 2.0B, 29-bit identifiers, 1 Mbps). The subsystems known so far are motor control, battery management, and distance sensors. More will follow.
On CAN, the identifier decides arbitration: the lower value wins the bus. The identifier therefore determines the priority of a message, and it must be unique bus-wide. Without a shared scheme, each artifact would pick identifiers on its own, collide with others, and give its own messages an accidental priority.
Questions to decide:
- How is the 29-bit identifier divided?
- How is priority expressed, so that an emergency stop is never delayed by other traffic?
- Which unit owns and coordinates the values?
Decision Drivers¶
- Mission statement, principle One bus: Classic CAN 2.0B, 29-bit identifiers.
- An emergency message must win arbitration against every other message, regardless of its subsystem.
- Subsystems define their messages in detail on their own. Only the ranges are coordinated centrally.
- Adding a subsystem or a message must not change existing identifiers.
- An identifier must be readable by humans and testable.
Considered Options¶
- Priority class first, then subsystem, instance, and message number.
- Subsystem ranges: each subsystem gets a contiguous identifier range, priority is chosen within it.
- Sender address in the style of SAE J1939: the identifier names the sending node.
Decision Outcome¶
Chosen option: 1, because priority then holds bus-wide by construction, not by convention per subsystem.
The 29 bits are divided as follows, from the most significant bit down:
| Bits | Width | Field | Meaning |
|---|---|---|---|
| 28–26 | 3 | Priority class | Arbitration priority. A lower value wins. |
| 25–21 | 5 | Subsystem | The subsystem that owns the message and defines its layout. |
| 20–16 | 5 | Instance | The unit of that subsystem the message concerns. 0 means all. |
| 15–0 | 16 | Message number | Distinguishes messages of the same class, subsystem, and instance. |
The Subsystem names the owner of the message layout, not its sender. A drive command therefore carries the drive subsystem even though the superordinate controller sends it. The Instance names the unit of the subsystem the message concerns, as the subsystem defines it. How many boards implement a subsystem does not show in its identifiers.
Priority classes:
| Value | Class | Use |
|---|---|---|
| 0 | Emergency | Emergency stop and other safety-critical messages |
| 1 | Warning and fault | Warnings, errors, and event-driven state changes |
| 2 | Control | Commands and requests that drive the robot |
| 3 | Telemetry | Cyclic measurements and status |
| 4 | Diagnostics | Diagnostics and configuration |
| 5–7 | Reserved | Not used. Sending them requires a new ADR. |
Rules:
- A message is defined by exactly one subsystem. Its identifier is allocated in the CAN identifier allocation before it is used.
- Among messages of the same class, the lower subsystem number wins arbitration. Subsystem number 0 (system-wide messages) is therefore the most important one within each class.
- The subsystem numbers and the identifiers of system-wide messages are recorded in the allocation document, not in this ADR. This ADR fixes the scheme only.
- A subsystem defines the layout of its messages in a machine-readable specification (DBC by default,
see the
can_bus.mdguideline of its artifact) and uses only identifiers that follow this scheme.
Example: the drive command concerns the whole drive. It has priority class 2, subsystem 1, instance 0,
and message number 1. The identifier is 0x08200001.
Positive Consequences¶
- An emergency message wins against every other message, whichever subsystem sends it.
- New subsystems and messages need no change to existing identifiers.
- The fields are visible in the hexadecimal identifier, so bus traces are easy to read.
Negative Consequences / Risks¶
- Within a class, subsystems with a low number always win against those with a high number. Mitigation: the allocation document assigns low numbers to the subsystems that need low latency.
- 16 bits for the message number are more than any subsystem is likely to need. Mitigation: none needed. Unused bits cost nothing, and a narrower field could not be widened later.
- Every new subsystem needs a change to the allocation document. Mitigation: the change is a small merge request.
Pros and Cons of the Options¶
Option 1 — Priority class first¶
- Pro: Priority holds bus-wide. No subsystem can accidentally outrank an emergency message.
- Pro: Subsystem and instance stay readable in the identifier.
- Con: The subsystem number influences priority within a class.
Option 2 — Subsystem ranges¶
- Pro: Simple to explain and to allocate.
- Con: Priority between subsystems is fixed by their range. A low-priority message of a high-ranking subsystem outranks an important message of another one.
- Con: An emergency message would need a range of its own that duplicates every subsystem.
Option 3 — Sender address¶
- Pro: Well known from vehicle networks.
- Con: Identifies the sender, not the meaning, and the priority still needs a place in the identifier.
- Con: A command to several instances of one subsystem would need one identifier per receiver.
Links¶
- Mission statement, principle One bus
- CAN identifier allocation