Sending Raw Data over CANopen: Choosing COB-IDs and SDOs

Daniel Price8 min read
Industrial NetworkingOther ManufacturerTechnical Reference
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Problem Definition

You need to push an arbitrary byte stream across a CAN bus that is already running CANopen. CAN itself does not care what the payload means, but the moment the segment carries CANopen nodes, every 11-bit identifier you emit competes with the pre-defined connection set. Two decisions drive everything: which COB-ID is legal to use, and which service (PDO or SDO) can carry the payload length you need.

The short answer: for payloads of 8 bytes or fewer, transmit on a TPDO COB-ID belonging to your node ID. For anything longer, use SDO segmented or block transfer against a manufacturer-specific object of type DOMAIN. Only fall back to a custom, non-CANopen identifier when you are certain it collides with nothing on the bus.

Key constraint: PDOs are the only CANopen service whose frames may carry a data length code (DLC) other than 8. SDO frames are always 8 bytes on the wire; the payload length is carried inside the protocol, not in the DLC.

COB-ID Map: What Is Already Taken

An 11-bit CANopen COB-ID splits into a 4-bit function code (bits 10-7) and a 7-bit node ID (bits 6-0, valid range 1-127). The pre-defined connection set in CiA 301 v4.2 allocates the identifiers as follows.

Object Function code base Resulting COB-ID range Direction
NMT node control 0x000 0x000 (broadcast) Master to all
SYNC 0x080 0x080 (broadcast) Producer to all
EMCY 0x080 + NodeID 0x081-0x0FF Node to bus
TPDO1 0x180 + NodeID 0x181-0x1FF Node transmits
RPDO1 0x200 + NodeID 0x201-0x27F Node receives
TPDO2 0x280 + NodeID 0x281-0x2FF Node transmits
RPDO2 0x300 + NodeID 0x301-0x37F Node receives
TPDO3 0x380 + NodeID 0x381-0x3FF Node transmits
RPDO3 0x400 + NodeID 0x401-0x47F Node receives
TPDO4 0x480 + NodeID 0x481-0x4FF Node transmits
RPDO4 0x500 + NodeID 0x501-0x57F Node receives
SDO transmit (server to client) 0x580 + NodeID 0x581-0x5FF Server response
SDO receive (client to server) 0x600 + NodeID 0x601-0x67F Client request
Heartbeat / NMT error control 0x700 + NodeID 0x701-0x77F Node to bus

Because node IDs stop at 127 (0x7F) while each function-code block spans 128 identifiers, small gaps remain at the top of each RPDO/SDO-rx block: 0x101-0x180, 0x280, 0x380, 0x480, 0x580, 0x680-0x700, and 0x780-0x7FF. These are the only sizeable ranges that are not claimed by the pre-defined connection set. CiA 301 also permits 29-bit extended identifiers, but does not require devices to support them, so a mixed bus may ignore or mis-handle extended frames.

Do not squat on 0x000 or 0x080. A stray frame at ID 0x000 is interpreted as an NMT command by every node on the segment and can reset or stop the entire network. A stray 0x080 is interpreted as SYNC and will trigger synchronous PDO transmission.

Option 1: TPDO COB-IDs for Payloads up to 8 Bytes

PDOs are unconfirmed, broadcast-style producer/consumer objects and are the intended vehicle for process data. They accept any DLC from 0 to 8, which lets you size the frame to the payload instead of padding to 8.

  1. Assign your device a unique node ID in 1-127.
  2. Emit raw data on 0x180 + NodeID (TPDO1). Use TPDO2-4 at 0x280/0x380/0x480 + NodeID for additional independent channels; four TPDOs give you 32 bytes per node in the pre-defined set.
  3. Set the DLC to the actual number of valid bytes. Consumers should not assume 8.
  4. If a real CANopen master is present, describe the mapping in the device EDS so the master's PDO configuration (communication parameter 0x1800+n, mapping parameter 0x1A00+n) matches what you transmit.

If you want your ad-hoc device to look like a standard CANopen node to a third-party master, model it on the CiA 401 generic I/O module profile and mimic its PDO layout as closely as your data allows. That gets you interoperability with off-the-shelf configuration tools without writing a custom profile.

Custom (non-profile) identifiers

CANopen does not forbid using identifiers outside the pre-defined set - PDO COB-IDs are themselves reconfigurable through sub-index 1 of 0x1800+n / 0x1400+n. The rules you must respect:

  • The identifier must not collide with any COB-ID actually in use on the segment, including EMCY, heartbeat and SDO channels of nodes that are currently offline but will come online later.
  • Lower numeric IDs win arbitration. Placing bulk raw traffic on a low ID (for example in the 0x101-0x17F gap) starves higher-ID PDOs and heartbeats under load. Put bulk traffic on a high ID such as the 0x780-0x7FF range if latency of the CANopen traffic matters.
  • Document the reservation. An undocumented custom ID is the classic cause of a bus that works on the bench and fails when a fifth node is added.

Option 2: SDO for Payloads Larger Than 8 Bytes

SDO is a confirmed, peer-to-peer client/server service on 0x600 + NodeID (request) and 0x580 + NodeID (response). Its whole purpose is to move objects that do not fit in one frame. Three transfer modes exist, per the CiA SDO protocol description:

Mode Payload capacity Per-frame data Use when
Expedited 1-4 bytes 4 bytes in one request/response pair Small scalar parameters
Segmented Arbitrary length 7 bytes per segment, alternating toggle bit General bulk transfer, simple to implement
Block (download/upload) Arbitrary length 7 bytes per segment, up to 127 segments per block before acknowledgement Throughput-critical bulk transfer

Segmented transfer costs one response frame per data frame, so effective throughput is roughly 7 bytes per two frames. Block transfer amortises the handshake across up to 127 segments (889 bytes) and is the correct choice for firmware images, log dumps, or recorder buffers.

The Object Dictionary you cannot avoid

SDO addresses data by index/sub-index, so the server must implement at least a minimal object dictionary. Follow the layout in the CANopen internal device architecture:

  • 0x1000-0x1FFF - communication profile area (device type, error register, SDO server parameters, heartbeat). Implement 0x1000, 0x1001, and 0x1017 at minimum.
  • 0x2000-0x5FFF - manufacturer-specific area. Put your raw data object here.
  • 0x6000-0x9FFF - standardized device and application profile parameters. Do not use this range for vendor payloads.

Declare the raw buffer with data type DOMAIN (0x000F). A DOMAIN entry carries an unbounded, uninterpreted byte stream and is exactly what SDO segmented and block transfer are designed to move. Example minimal declaration:

Index    : 0x2000
Sub-index: 0x00
Name     : Raw data buffer
Data type: DOMAIN (0x000F)
Access   : rw
PDO map  : no    ; DOMAIN > 64 bits cannot be PDO-mapped

An object may be PDO-mapped only if it is 64 bits (8 bytes) or smaller. If part of your data must arrive cyclically and the rest in bulk, split it: a small mapped object for the cyclic slice, a DOMAIN object for the block.

Choosing Between PDO and SDO

Criterion PDO (TPDO COB-ID) SDO (0x600/0x580 + NodeID)
Max payload 8 bytes per frame, no fragmentation Arbitrary, protocol-level fragmentation
Variable DLC Yes (0-8) No, frames are always 8 bytes
Confirmation None (unconfirmed broadcast) Confirmed, every request acknowledged
Addressing model Producer/consumer Client/server, point-to-point
Object dictionary required Minimal (mapping parameters) Yes, plus SDO server state machine
Latency Low, single frame Higher, handshake per segment or block
Implementation effort Low Significantly higher

Rule of thumb: cyclic, latency-sensitive, small - PDO. Acyclic, large, must-not-be-lost - SDO. Fragmenting your own multi-frame protocol on top of PDOs is possible but you then re-implement toggle/sequence handling, timeout and abort semantics that SDO already specifies.

Diagnostics and Verification

  1. Scan before you allocate. Put a bus analyser on the segment with all nodes powered and running, capture for several minutes including a power-cycle, and list every observed identifier. Verify your chosen ID appears nowhere.
  2. Check offline nodes too. Compute the reserved COB-IDs for every node ID that could be commissioned later: EMCY 0x080+n, TPDO/RPDO 0x180-0x57F+n, SDO 0x580/0x600+n, heartbeat 0x700+n.
  3. Confirm DLC handling. Transmit a short PDO (for example DLC 3) and verify the consumer does not read stale bytes 4-8.
  4. Test SDO aborts. A failed SDO transfer terminates with an abort code in the four data bytes of the abort frame. Common codes to handle: 0x05040000 (SDO protocol timeout), 0x06070010 (data type does not match, length of service parameter does not match), 0x06020000 (object does not exist in the object dictionary). Log the raw 32-bit code - it identifies the fault precisely.
  5. Load-test arbitration. Run the bulk transfer at full rate while monitoring heartbeat jitter. If heartbeats start timing out, your raw traffic has too low an identifier or too high a duty cycle; move it to a higher COB-ID or throttle the block size.
  6. Verify error counters. Rising transmit/receive error counters or bus-off events after adding the raw traffic usually indicate a duplicate identifier being transmitted by two nodes simultaneously, not a wiring fault.
Duplicate-ID symptom: two nodes transmitting the same COB-ID with different data lose arbitration mid-frame, causing bit errors, error frames and eventually bus-off on one or both nodes. This looks like a physical layer problem but is purely a COB-ID allocation error.

FAQ

Which CANopen COB-ID should I use for custom raw data?

Use a TPDO COB-ID belonging to your node: 0x180 + NodeID for TPDO1, then 0x280/0x380/0x480 + NodeID. If you need an identifier outside the pre-defined connection set, the unclaimed gaps are roughly 0x101-0x180, 0x680-0x700 and 0x780-0x7FF, and you must verify no node on the segment uses them.

Can a CANopen frame carry fewer than 8 data bytes?

Only PDOs. PDOs may use any DLC from 0 to 8, which is why they are the right choice for short raw payloads. SDO, EMCY, NMT error control and SYNC use fixed frame layouts.

How do I send more than 8 bytes over CANopen?

Use SDO on 0x600 + NodeID / 0x580 + NodeID. Segmented transfer moves 7 data bytes per frame with an alternating toggle bit; block transfer streams up to 127 segments per block for higher throughput.

Where in the object dictionary should vendor-specific raw data live?

In the manufacturer-specific range 0x2000-0x5FFF, declared as data type DOMAIN (0x000F). The range 0x6000-0x9FFF is reserved for standardized device and application profile parameters and must not be used for vendor payloads.

Why does my SDO transfer abort with 0x06070010?

That abort code means the data type does not match or the length of the service parameter does not match - typically writing more bytes than the target object accepts, or using expedited transfer for an object larger than 4 bytes. Declare the target as DOMAIN and switch to segmented or block transfer.

Back to blog