Skip to content

The CAN backplane

Almost the only thing that crosses a subsystem boundary is an 8-byte CAN frame. This page covers addressing, function codes, the two multi-frame transports that ride the bus, and how provenance survives the trip to the ground.

The exception is bulk science, which crosses a second bus: the payload pushes captured files straight into the DHS’s mass memory over a point-to-point SpaceWire mission data link. Nothing else — no command, housekeeping report or telecommand — travels on that bus, so this one remains the spacecraft’s command-and-control backplane.

Source: include/shared_can.h (frame, addressing, UDP transport — shared by the hub and the nodes, no FreeRTOS), common/can_drv.c (the node-side driver, where the Posix port workarounds live), and hub/can_hub.c (the backplane process).


1. Addressing

An 11-bit identifier:

[10:8] function | [7:4] destination | [3:0] source

Addresses are 4-bit and live in both the destination and the source field: DHS 0x1, TTC 0x2, ADCS 0x3, EPS 0x4, PAYLOAD 0x5, broadcast 0xF (CAN_ADDR_*). The CAN_SYS_* constants are the same values pre-shifted into the destination positionCAN_SYS_DHS is 0x10, not what CAN_ID_DST(id) returns, which is 0x1.

All eight function codes:

FunctionCodeCarries
CMD0x0Ground/DHS commands, highest priority
ACK0x1Command acknowledgements
HK0x2Housekeeping
SCI0x3Science telemetry
XFER_FF0x4Payload file readout, first frame
XFER_CF0x5Payload file readout, consecutive frame
MGMT0x6Bus management — HELLO, heartbeat
PKT0x7A whole PUS space packet, segmented (DHS↔TTC)

Function codes sit in the high bits, so a lower code yields a lower ID — CMD outranks HK exactly as CAN arbitration would decide it.


2. The hub

can_hub is a plain POSIX process — deliberately no FreeRTOS — that receives every frame over UDP :20000 and forwards it to the destination’s registered socket, or to all of them for a broadcast. The routing table is learned at runtime: a node’s first frame registers its address, and a logical address migrates to whichever node last announced it. That migration is what would make a future warm-redundancy upgrade (two real OBC binaries) a node-side change with no protocol change. The hub logs every frame it routes, collapsing bulk readouts to a count — the first place to look when a subsystem goes quiet.


3. Two multi-frame transports

Nothing but 8-byte frames crosses a node boundary on this bus, so anything larger is segmented. Two independent transports do this, distinguished by function code — do not confuse them:

  • Payload readout (XFER_FF/XFER_CF, PAYLOAD→DHS) is a simplified ISO-TP with no flow control: the sender streams at the requested rate. Frame layouts, pacing and what the DHS does with the reassembled file are in The file transport.
  • Whole-packet transport (PKT, function 0x7, DHS↔TTC) carries a complete PUS space packet: a first segment (start marker + 2-byte length + 5 bytes) then continuations (7-bit rolling sequence + 7 bytes), reassembled by can_drv’s canPktSend/canPktFeed. This is how the DHS’s packets reach TTC for downlink and how uplinked telecommands reach the DHS.

A third transport exists and is not on this bus: RMAP over the mission data link, which needs no segmentation at all because it writes directly into the receiver’s memory. It is also why the payload readout above is now the fallback route for a science file rather than the only one — but it stays the default, and the eight function codes here are unchanged.


4. Provenance is carried by APID

The ground must be able to attribute every packet to the subsystem that produced it. The DHS does this at packetisation: it reads each incoming CAN frame’s source address and uses it as the PUS APID when it builds the downlink packet (pusApidFromAddr(CAN_ID_SRC(...))).

So a TTC housekeeping frame becomes an APID-2 packet, a payload science frame an APID-5 packet — provenance rides the APID, not the DHS’s own address. Get this wrong and telemetry is misattributed on the ground.

The ADCS and the EPS are attributed the same way but not frame by frame: their five and eight housekeeping frames are ingested into the DHS’s parameter pool and leave as one APID-3 and one APID-4 report built on the DHS’s own cadence (dhs.md §6). The APID still names the producer; the packet boundary is the subsystem’s, not the bus’s.

canSendFrom(), which preserves an originator’s CAN address on a raw relay, still exists but is no longer used by flight code now that the DHS builds packets instead of relaying frames.


5. The driver

common/can_drv.c is the node-side virtual driver: a UDP socket to the hub, a receive task, and a frame queue. It is also where the Posix-port workarounds live — SO_RCVTIMEO, the EINTR retry and CAN_RX_BATCH, all three required and none of them optional. See porting.md §1.

TODO — document the driver API surface (canInit, canSend, canSendFrom, canPktSend, canPktFeed, the receive queue depth) and the frame struct itself.

Last updated on