`esw.can`
CAN bus access and DBC handling. Every tool that talks to a board goes through this module.
esw.can.canbus
Section titled “esw.can.canbus”CANBus
Section titled “CANBus”A context manager wrapping python-can and a cantools database. Opens a SocketCAN interface in
FD mode with bit rate switching enabled.
CANBus(dbc: Database, channel: str, on_recv: Callable = lambda arg: None)| Method | Signature |
|---|---|
send | send(message_name: str, signals: dict[str, Any], src_id: int = 0, dest_id: int = 0) -> None |
recv | recv(block: bool = True, timeout: float | None = None) -> Tuple[str, dict, int, int] | None |
get_dbc | get_dbc() -> Database |
recv returns (message_name, signals, src_id, dest_id).
from esw.can.canbus import CANBusfrom esw.can.dbc import get_dbc
with CANBus(get_dbc(dbc_name="MRoverCAN"), "can0") as bus: bus.send("BMCResetCmd", {"reset": 1, "clear_faults": 1}, dest_id=49) print(bus.recv(timeout=1.0))Arbitration ID Layout
Section titled “Arbitration ID Layout”Messages carry a source and destination node ID packed into the arbitration ID alongside the DBC frame ID:
arbitration_id = frame_id + (src_id << 8) + dest_id| Constant | Value |
|---|---|
_CAN_DEST_ID_MASK | 0x00FF, offset 0 |
_CAN_SRC_ID_MASK | 0xFF00, offset 8 |
_MJBOTS_CAN_PREFIX | 0x0000 |
The same constants are emitted into the generated C++ header, so both sides agree. Frames with a
base ID of 0x0000 are moteus traffic and are logged but not decoded.
float2bits
Section titled “float2bits”float2bits(value: float)Reinterprets a float as its IEEE-754 bit pattern. Needed when sending a float through an integer
DBC signal, which is what the configuration interface does. Raises TypeError on non-floats.
esw.can.dbc
Section titled “esw.can.dbc”get_dbc
Section titled “get_dbc”get_dbc(filepath: Path | None = None, dbc_name: str | None = None) -> DatabaseLoads a DBC into a cantools database. With dbc_name, resolves <repo root>/dbc/<name>.dbc;
with filepath, loads that path directly. One of the two is required. Cached, so repeated calls
with the same argument are free.
Header Generation
Section titled “Header Generation”generate_can_header(ctx: Path, dest: Path, files: list[str]) -> NoneRenders <ctx>/templates/dbc_header.hpp.j2 once per DBC file into <dest>/<stem>.hpp. Invoked
from lib/dbc/CMakeLists.txt during a firmware build, not usually by hand. See
Generated Libraries.
Two helpers back the template context:
get_c_type(signal) -> strmaps a DBC signal to a C type, choosing width by bit length andfloat/doubleat the 32-bit boundary.prepare_context(dbc_db, dbc_name)builds the full Jinja context: messages keyed by frame ID, per-signal types and lengths, and the sorted message name list.
DBC File
Section titled “DBC File”dbc/MRoverCAN.dbc is the single source of truth for the CAN protocol. It is consumed twice: by this module at runtime, and by the firmware build to generate
C++ message classes.
Per-board message tables are on the board pages.
A tagged release packages the generated headers as mrover_can.tar.gz for the ros2 build, which needs the protocol without the Python toolchain.
See Continuous Integration.