Every CANopen device carries an object dictionary, and the SDO is the only way to reach into it from outside. Read a serial number, change a gear ratio, remap a PDO, save the configuration to flash — all of it is SDO traffic.
Unlike a PDO, an SDO frame carries its own address. The first byte says what kind of operation this is, the next three say which object, and whatever is left holds data. That structure means you can read an SDO exchange straight off a bus trace without a decoder, once you know what the command byte is telling you.
This guide takes the frame apart byte by byte, walks through the three transfer types with real hex, and covers what the abort codes actually mean when a write comes back rejected. Verified against CiA 301.
Table of Contents
Client, server, and who owns what
An SDO is a peer-to-peer channel between exactly two devices, and the roles are not symmetric.
The server owns the object dictionary being accessed. The client initiates every transfer, in both directions. A slave device never starts an SDO exchange on its own — it answers.
That last point catches people who expect “upload” and “download” to describe the direction of travel from the master’s point of view. The names are written from the server’s perspective:
- Download — client writes into the server’s object dictionary.
- Upload — client reads out of it.
So a PLC reading a drive’s status is doing an SDO upload, even though from the PLC’s side the data is coming in.
Every device supports at least one SDO server, and the default channel uses two identifiers from the predefined connection set:
| Direction | COB-ID | Carries |
|---|---|---|
| Client → server | 600h + node-ID | Requests |
| Server → client | 580h + node-ID | Responses |
For node 12 that is 60Ch and 58Ch. A trace full of traffic on 60Ch and 58Ch is a configuration tool talking to node 12, and you can read the exchange directly.
SDOs work in NMT pre-operational and operational states. They stop in stopped state — worth remembering when a device that was answering suddenly does not.
The eight bytes
Every SDO frame is eight bytes, always. Short transfers are padded.

Bytes 1 to 3 are the multiplexer: index low byte, index high byte, sub-index. Object 6041h sub-index 00h appears on the wire as 41 60 00. The byte swap trips everyone up once — CANopen is little-endian throughout, including inside PDO payloads.
Byte 0 is the command byte, and it does the real work. The top three bits are the command specifier, and their meaning depends on which side is talking:
| Bits 7–5 | Client sends (ccs) | Server sends (scs) |
|---|---|---|
| 0 | Download segment | Upload segment |
| 1 | Initiate download | Download segment ack |
| 2 | Initiate upload | Initiate upload response |
| 3 | Upload segment | Initiate download ack |
| 4 | Abort | Abort |
| 5 | Initiate block upload | Block download response |
| 6 | Initiate block download | Block upload response |
The remaining five bits carry flags whose meaning changes with the command. For an initiate, they are:
| Bit | Name | Meaning |
|---|---|---|
| 3–2 | n | Number of bytes in the data field that are not data |
| 1 | e | 1 = expedited, 0 = normal (segmented) |
| 0 | s | 1 = size is indicated |
The n field is defined backwards from what you would guess: it counts the unused bytes, not the used ones. Two bytes of payload in an expedited frame means n = 2, because bytes 6 and 7 are padding.
Expedited transfer: four bytes or fewer
If the data fits in four bytes, it rides along in the initiate frame and the whole transaction is two CAN frames. This covers the overwhelming majority of real SDO traffic — control words, mode selections, timeouts, mapping entries, node IDs.
Expedited transfer is mandatory. Every CANopen device supports it.
Write: 16-bit value to the control word
Writing 000Fh to object 6040h sub-index 00h on node 3.
Request COB-ID 603h 2B 40 60 00 0F 00 00 00
Response COB-ID 583h 60 40 60 00 00 00 00 00
The request command byte, 2Bh:
2Bh = 0010 1011
001 ccs = 1, initiate download
0 reserved
10 n = 2, so bytes 6-7 are padding
1 e = 1, expedited
1 s = 1, size indicated
Two bytes of payload, 0F 00, little-endian, value 000Fh. The response, 60h, is scs = 3 with no flags: the write succeeded. Nothing else in the response matters — the multiplexer is echoed back and the data field is empty.
Command bytes for expedited writes are worth memorizing, because you will read them constantly:
| Payload | Command byte |
|---|---|
| 1 byte | 2Fh |
| 2 bytes | 2Bh |
| 3 bytes | 27h |
| 4 bytes | 23h |
Some clients send 22h — expedited, size not indicated — and let the server work out the length from the object dictionary. It is legal and common. It also means a mismatched data type fails at the server instead of being caught in the frame, so 2Bh-style writes with the size declared are the better habit.
Read: status word
Request COB-ID 603h 40 41 60 00 00 00 00 00
Response COB-ID 583h 4B 41 60 00 37 06 00 00
The request is 40h — ccs = 2, no flags, since a read carries no data. The response 4Bh is scs = 2 with n = 2, e = 1, s = 1: two bytes of data follow. Value 0637h.
Segmented transfer: more than four bytes
Anything longer than four bytes goes out in pieces. A device name, a domain object, a firmware block. Segmented transfer is mandatory only if the device has objects larger than four bytes — a simple I/O module may legitimately not support it.
The initiate frame declares the total size, then each segment carries up to seven data bytes.
Reading a 12-byte string from 1008h (manufacturer device name) on node 3:
Request 603h 40 08 10 00 00 00 00 00 initiate upload
Response 583h 41 08 10 00 0C 00 00 00 size = 12 bytes
Request 603h 60 00 00 00 00 00 00 00 segment, toggle 0
Response 583h 00 44 72 69 76 65 20 58 "Drive X"
Request 603h 70 00 00 00 00 00 00 00 segment, toggle 1
Response 583h 15 4C 33 30 30 00 00 00 "L300", last
Three things are happening here.
The initiate response declares the size. 41h is scs = 2 with e = 0 and s = 1: not expedited, size indicated. Bytes 4 to 7 hold the length as a 32-bit little-endian value — 0Ch, twelve bytes.
The toggle bit alternates. Bit 4 of the command byte starts at 0, flips on every subsequent segment, and must match between request and response. 60h has it clear, 70h has it set. If two consecutive segments arrive with the same toggle value, the receiver discards the second one. This is the entire retransmission defense of segmented transfer: no sequence numbers, one bit.
The last segment is flagged. In a segment frame the bit layout changes — bits 3 to 1 are n, and bit 0 is c, the completion flag:
15h = 0001 0101
000 scs = 0, upload segment
1 toggle = 1
010 n = 2, so 2 of the 7 data bytes are unused
1 c = 1, no more segments
Seven bytes minus two unused gives five bytes of data: 4C 33 30 30 00, which is “L300” plus the terminating null. Seven from the first segment plus five here is twelve, matching the size the server declared at initiate. That reconciliation is the check worth doing whenever a string comes back truncated.
It is also exactly where hand-decoding goes wrong, which is why the CANopen message decoder exists — paste the trace and the segments come back reassembled.
Segmented transfer costs two CAN frames per seven bytes. A 200-byte object is around 58 frames. It works, and it is slow.
Block transfer: the fast path nobody uses
Block transfer exists to fix that overhead. Instead of acknowledging every segment, the client and server agree on a block size, then the sender pushes up to 127 segments back to back with only one acknowledgment at the end.
The mechanism is a go-back-N scheme. Each segment carries a sequence number in its command byte. After the block, the receiver reports the sequence number of the last segment it got cleanly, which implicitly acknowledges everything before it, and states how many segments it wants in the next block. Anything after that point is retransmitted.
The initiate exchange also negotiates a 16-bit CRC over the complete data set, checked at the end of the transfer. Both sides have to advertise CRC support for it to be used.
Two details matter in practice. Block size is 1 to 127 segments, and the receiver can shrink it mid-transfer if it is falling behind. And there is a threshold mechanism on upload: the client tells the server the smallest data size worth using a block transfer for, and the server may fall back to normal segmented transfer if the object is smaller than that.
Block transfer is optional, and support in the field is patchy. Configuration tools generally probe for it and quietly fall back. If you are moving firmware images over CANopen it is worth checking whether both ends support it — the difference on a 64 kB file is substantial. For everything else, it rarely comes up.
Choosing a transfer type
The client picks, based on size, but it is not entirely free:
| Data size | Available |
|---|---|
| ≤ 4 bytes | Expedited, segmented, or block |
| > 4 bytes | Segmented or block |
In practice: expedited for anything that fits, segmented for the rest, block only if both ends support it and the object is large enough to pay back the setup cost.
Reading an abort
When a transfer fails, either side sends an abort. It is unconfirmed — the frame goes out and the transfer is over. Nobody responds.
The command byte is always 80h, the multiplexer is echoed, and bytes 4 to 7 carry a 32-bit abort code, little-endian.
583h 80 40 60 00 02 00 01 06
Read bytes 4 to 7 backwards: 06 01 00 02 → abort code 06010002h, “attempt to write a read-only object”. The byte reversal is the reason abort codes so often look unfamiliar in a raw trace.
The codes group by their first byte, which tells you where to look:
| Abort code | Meaning |
|---|---|
| 0503 0000h | Toggle bit not alternated |
| 0504 0000h | SDO protocol timed out |
| 0504 0001h | Command specifier not valid or unknown |
| 0504 0002h | Invalid block size (block mode only) |
| 0504 0003h | Invalid sequence number (block mode only) |
| 0504 0004h | CRC error (block mode only) |
| 0504 0005h | Out of memory |
| 0601 0000h | Unsupported access to an object |
| 0601 0001h | Attempt to read a write-only object |
| 0601 0002h | Attempt to write a read-only object |
| 0602 0000h | Object does not exist in the object dictionary |
| 0604 0041h | Object cannot be mapped to the PDO |
| 0604 0042h | Number and length of mapped objects would exceed PDO length |
| 0604 0043h | General parameter incompatibility |
| 0604 0047h | General internal incompatibility in the device |
| 0606 0000h | Access failed due to a hardware error |
| 0607 0010h | Data type does not match, length of service parameter does not match |
| 0607 0012h | Data type does not match, length too high |
| 0607 0013h | Data type does not match, length too low |
| 0609 0011h | Sub-index does not exist |
| 0609 0030h | Invalid value for parameter (download only) |
| 0609 0031h | Value written too high |
| 0609 0032h | Value written too low |
| 0609 0036h | Maximum value is less than minimum value |
| 060A 0023h | Resource not available: SDO connection |
| 0800 0000h | General error |
| 0800 0020h | Data cannot be transferred or stored to the application |
| 0800 0021h | Data cannot be transferred or stored — local control |
| 0800 0022h | Data cannot be transferred or stored — current device state |
| 0800 0023h | Dynamic object dictionary generation failed, or no object dictionary present |
| 0800 0024h | No data available |
Three of these dominate real troubleshooting.
0602 0000h — the object does not exist. Nine times out of ten this is the wrong index, or a manufacturer-specific object that this particular firmware revision does not implement. Compare against the EDS for the exact firmware version, not the family datasheet.
0800 0022h — wrong device state. The write is valid but not right now. Communication parameters usually cannot be changed while the associated PDO is active, and many devices refuse configuration writes in operational state. Drop to pre-operational and repeat.
0607 0010h — length mismatch. The client sent a different number of bytes than the object expects. Writing a 16-bit object with a 4-byte expedited frame produces this, and so does the reverse. Check the data type in the EDS.
More than one SDO channel
A device can support several SDO servers. Each one gets its own record:
| Range | Purpose |
|---|---|
| 1200h–127Fh | SDO server parameters |
| 1280h–12FFh | SDO client parameters |
1200h describes the default server, and its identifiers are usually read-only — they are fixed by the node-ID. Additional servers at 1201h upward can be assigned freely, which is how you give two masters independent access to the same device without their transactions colliding.
The client records at 1280h upward are what make a device capable of initiating SDO transfers itself. A PLC configuring drives on the same bus needs these; a sensor does not. Each record holds the two COB-IDs plus the node-ID of the server it talks to.
One SDO channel supports exactly one transfer at a time. A second request on the same channel while a segmented transfer is in progress aborts the first. Tools that appear to configure several nodes in parallel are either using separate channels or interleaving at a level above the protocol.
SDO or PDO
Both move data between the same objects. The difference is what they are for.
| SDO | PDO | |
|---|---|---|
| Model | Client/server, confirmed | Producer/consumer, unconfirmed |
| Addressing | Index and sub-index in the frame | Implicit, from the mapping |
| Payload | Unlimited, segmented | ≤ 8 bytes, single frame |
| Overhead | 4 bytes per frame | None |
| Priority | Low (580h/600h + node-ID) | High (180h–500h + node-ID) |
| Errors | Abort code returned | Silent |
| Use | Configuration, diagnostics, occasional reads | Cyclic process data |
The practical rule: if a value is read once at startup or written once during commissioning, SDO. If it moves every cycle, map it into a PDO.
Polling a value over SDO in a fast loop is the classic mistake. It costs two frames per read where a PDO costs one, it runs at low priority so it loses arbitration to everything else on a busy bus, and it produces jitter that looks like a device problem. If you find yourself SDO-polling at 50 ms, the answer is a TPDO with an event timer.
The reverse mistake is rarer but worse: mapping configuration parameters into PDOs so they can be changed quickly. Now a corrupted or mistimed frame silently rewrites a machine parameter with no acknowledgment and no error path.
Timeouts and field failures
The standard does not fix an SDO timeout value — it is a client-side setting, and typical values run from 500 ms to a few seconds. Too short and slow devices produce spurious aborts, particularly ones that write to flash during the transaction. Too long and a dead node stalls a configuration sequence for seconds per object.
Object 101Bh, where implemented, holds the device’s own SDO protocol timeout.
The failure patterns that come up repeatedly:
No response at all. Not an SDO problem. Check the node-ID first — a request to 605h reaches nobody if the device is set to node 6 by DIP switch but node 5 in the project. Then check NMT state, since a stopped node ignores SDOs entirely.
First segment works, transfer stalls. Toggle bit handling on one side. This shows up with hand-written stacks and with some gateway devices that forward SDO traffic without tracking the toggle state.
Write returns success, value unchanged. The write landed in RAM. Store it with the save signature to 1010h, or the device is applying the value only after a reset.
Works with one tool, fails with another. Usually the size-indicated flag. A client that sends 22h and lets the server infer the length will behave differently from one that sends 2Bh and declares two bytes, on a device that is strict about data types.
Intermittent aborts on a loaded bus. SDO identifiers sit low in priority, so they lose arbitration to every PDO on the segment. On a bus running near capacity, configuration traffic is the first thing to suffer. Take the machine out of operational state before configuring it.
FAQ
What is the difference between SDO upload and download?
The names are written from the server’s point of view. Download means the client writes into the server’s object dictionary; upload means the client reads out of it. A master reading a slave parameter is performing an upload.
What is the maximum data size an SDO can transfer?
There is no fixed limit. Expedited transfer carries up to four bytes in a single frame, and segmented or block transfer handles anything larger by splitting it across frames. Firmware images of tens of kilobytes are moved this way.
Why does my SDO write return abort code 06010002h?
The object is read-only. Either it genuinely is — an identity or diagnostic object — or the device treats it as read-only in the current state. Communication parameters are commonly writable only when the associated PDO is disabled and the device is in pre-operational state.
What is the toggle bit for?
It detects lost or duplicated segments during segmented transfer. It starts at 0, alternates on each subsequent segment, and must match between request and response. Two consecutive segments with the same toggle value cause the second to be discarded.
Which COB-IDs do SDOs use?
By default, 600h + node-ID for client-to-server requests and 580h + node-ID for server-to-client responses. Additional SDO channels can be assigned other identifiers through the objects at 1200h and above.
Can I use SDOs instead of PDOs for process data?
You can, and it is usually a mistake. SDOs cost twice the frames, sit at low priority so they lose arbitration on a busy bus, and add variable latency. Anything transferred cyclically belongs in a PDO.
Is block transfer worth enabling?
Only for large objects, and only if both ends support it. For a few hundred bytes the saving is marginal. For firmware download it is significant. Support is optional and inconsistent, so most tools probe for it and fall back to segmented transfer.
Why does an SDO work in pre-operational but not in stopped state?
Stopped state halts all communication except NMT and heartbeat. That is by design — it is a way of isolating a device without powering it down. Return the node to pre-operational to configure it.
