CAN bus for robots: wiring, messages and debugging

How CAN bus works on a robot: termination rules, frame format, a sane message map, SocketCAN commands and Python, plus the faults that stop a bus dead.

Reference
RE-1404
Published
Read
8 min

About the author · Sources, testing and AI policy

Twisted CAN bus pair with termination resistors and a USB-CAN adapter on a robot workbench

CAN bus is how most robot parts talk to each other once UART stops being enough. One twisted pair carries motor commands, battery data and sensor status between a controller and a dozen devices, with arbitration built in.

This guide covers the wiring rules that decide whether CAN works at all, the frame format you need to understand before designing message IDs, SocketCAN on Linux, and the failure modes that show up on a real robot.

Why robots use CAN

CAN is a differential bus: two wires, CANH and CANL, carry a voltage difference rather than a voltage relative to ground. Motor current switching, PWM and long cables inject noise into ground references, and a differential pair rejects most of it. That is the main reason CAN survives on a robot where I2C and bare UART do not.

The second reason is arbitration. Any node can start transmitting when the bus is idle, and if two start at once the one with the lower numeric ID wins without losing data. That gives you a priority scheme for free: safety stop messages get lower IDs than cosmetic telemetry.

  • One pair of wires for many devices, with no master polling loop.
  • Hardware error detection: CRC, bit stuffing, acknowledgement and error counters.
  • Broadcast by design, so a BMS, a drive and a logger can all use the same frame.
  • Deterministic priority through arbitration rather than through software scheduling.

The wiring rules that decide success

CAN is a bus, not a star. All nodes tap the same pair, and the pair must be terminated at both physical ends with a 120 ohm resistor. Two terminations, not one per node. A robot with five devices and five resistors will have a bus impedance low enough to distort edges and cause intermittent errors.

Bit ratePractical bus lengthTypical robot use
1 Mbit/sUp to about 40 mJoint drives in a compact arm
500 kbit/sUp to about 100 mMobile robot with several nodes
250 kbit/sUp to about 250 mLarge cell, long cable runs
125 kbit/sUp to about 500 mSlow telemetry, battery racks

These are limits for a clean installation, and they assume short stubs. A stub longer than about 30 cm from the main pair creates reflections. On a robot, that usually means bringing the pair to each board and keeping the transceiver close to the connector.

  • Use twisted pair with a characteristic impedance near 120 ohm, not random hookup wire.
  • Keep CANH and CANL as a pair the whole way; do not route one wire around a motor.
  • Share ground between nodes, but keep motor return current out of that ground path.
  • Terminate only the two end nodes, and make termination switchable on your own boards.
  • Check with a multimeter: about 60 ohm between CANH and CANL across the powered-down bus.

Frames, IDs and what a message actually looks like

A classic CAN data frame carries an 11 bit identifier, a data length code, and up to 8 data bytes. The identifier is not an address; it describes the content, and every node decides from the ID whether the payload matters to it.

FieldSizeNotes
Identifier11 bit standard, 29 bit extendedLower value wins arbitration
DLC4 bit0 to 8 bytes in classic CAN
Data0 to 8 bytesUp to 64 bytes with CAN FD
CRC15 bit plus delimiterHardware checked
ACK slot1 bitAny receiving node pulls it low

Eight bytes forces discipline. A joint command usually packs a target position as a 32 bit integer plus a mode byte and a checksum, and the scaling factor is documented once for the whole product. Write that document before the first board ships, because a scaling mismatch between firmware versions produces motion that looks like a control bug.

CAN FD extends the payload to 64 bytes and allows a faster data phase. It is useful for firmware updates and for streaming data, and it is not a drop-in replacement: both ends, the transceiver and every node on the segment must support it.

Designing a message map

Most robot builders start by assigning IDs in the order devices were added, then regret it. A small amount of planning avoids a rewrite. Group IDs by function and leave gaps.

  • 0x000 to 0x07F for safety and stop messages, so they always win arbitration.
  • 0x080 to 0x0FF for motion commands, one block per joint or axis.
  • 0x100 to 0x17F for feedback: position, current, temperature, faults.
  • 0x180 to 0x1FF for battery and power data.
  • 0x700 and above for heartbeat and node status, following the CANopen convention if you ever want to adopt it.

Two habits make a message map maintainable. First, send a heartbeat from every node at a fixed period, with a counter and a state byte, so a controller can detect a dead device instead of assuming silence means everything is fine. Second, define one fault frame per node rather than scattering error bits across several messages.

SocketCAN on Linux in five commands

On Linux, a USB-CAN adapter appears as a network interface. SocketCAN turns CAN frames into socket traffic, which means tools you already know work on it.

sudo ip link set can0 up type can bitrate 500000
candump can0
cansend can0 123#DEADBEEF
cangen can0 -g 100 -I 321
candump -l can0

candump prints traffic in real time, cansend sends one frame, and candump with the log flag writes a file you can replay later. The last command is worth adopting early: a captured log of a fault is far more useful than a description of it.

import can

bus = can.interface.Bus(channel='can0', bustype='socketcan')

msg = can.Message(arbitration_id=0x123,
                  data=[0x01, 0x00, 0x7D, 0x00],
                  is_extended_id=False)
bus.send(msg)

for frame in bus:
    if frame.arbitration_id == 0x181:
        print(frame.data.hex(), frame.timestamp)

Bring the interface up before starting your node, and check that the bitrate matches the robot. A device configured for 500 kbit/s on a 1 Mbit/s bus produces error frames and nothing else, and the symptom is indistinguishable from bad wiring until you check the configuration.

The microcontroller side

An STM32 with bxCAN or FDCAN gives you hardware filters, three mailboxes and automatic retransmission. Set the filter to accept only the IDs you care about, otherwise every frame wakes the CPU and a busy bus starves your control loop.

Arduino boards without CAN need an MCP2515 controller over SPI, plus a transceiver such as the TJA1050 or SN65HVD230. That combination works, with two caveats: the SPI link limits sustained throughput, and the module’s onboard termination resistor must be removed unless the module sits at a physical end of the bus.

  • Enable automatic bus-off recovery, otherwise one wiring glitch leaves the node silent until a reboot.
  • Read the error counters during development; rising receive errors usually mean termination or bitrate.
  • Never call blocking transmit from inside a control interrupt.
  • Keep a ring buffer of received frames and parse them in the main loop.

Debugging a bus that will not work

SymptomMost likely causeCheck
No frames at all in candumpInterface down or wrong channel nameip link show can0
Frames sent but nothing acknowledgesOnly one node on the bus, or no terminationMeasure 60 ohm across the pair
Bus-off after a few secondsBitrate mismatch or a shorted pairCompare configuration on every node
Works on the bench, fails when motors runGround noise and long stubsShorten stubs, separate motor return
One device never answersID filter or wrong node IDcandump -a to see all frames
Random dropped frames under loadReceive buffer too smallIncrease socket buffer, reduce traffic

A single node cannot acknowledge its own frame. If you are testing one device on a bench, use an adapter that generates acknowledge bits or add a second node. Otherwise every transmission retries until the error counter drives the controller into bus-off, and the device looks broken when it is not.

Buying CAN hardware in India

  • A USB-CAN adapter with Linux SocketCAN support is the single most useful purchase; check the driver before buying.
  • Buy two adapters if you plan to develop firmware, so one can act as a monitor and reference node.
  • Transceiver modules are cheap; buy spares, because a swapped CANH and CANL is a five-second mistake with a ten-minute diagnosis.
  • For production boards, prefer a part with integrated protection and specify the common-mode choke.
  • Keep a short twisted test cable with termination built into the connectors for bench work.

Related: STM32 for robotics, industrial Ethernet for robots, robot controller PCB design, BLDC motors and ESCs, robot battery guide.

Does a robot need CAN if it only has two motors?

Not necessarily. UART or even PWM is enough for two independent drivers. CAN starts paying off when several devices must share one cable, when cable runs are long, or when you need arbitration and error detection in hardware.

How many 120 ohm resistors does a CAN bus need?

Exactly two, one at each physical end of the bus. More than two loads the bus and distorts edges. Many breakout modules ship with a resistor fitted, so count them before connecting.

What is the difference between CAN 2.0 and CAN FD?

CAN 2.0 carries up to 8 data bytes at a single bitrate. CAN FD carries up to 64 bytes and switches to a faster data phase, but every node and transceiver on the segment must support it.

Why does my USB-CAN adapter see nothing?

Usually the interface is down, the bitrate is wrong, or the adapter is on a different channel name. Bring it up with ip link, confirm the bitrate matches the robot, and check that the pair is terminated.

Can I run CAN and motor power in the same cable bundle?

You can, but keep the CAN pair twisted and separated from the motor conductors, and avoid long parallel runs. Noise coupling into an unterminated stub is the usual reason a bus works on the bench and fails under load.

Cable entry is a classic leak path, so plan glands and routing with IP-rated enclosure practice in mind.

Sources and further reading

Pass it onWhatsAppLinkedIn

Keep reading

Continue learning

The Build Note

One short email when something is worth your bench time.

A new build sheet, a part price that moved, or a mistake worth avoiding. No daily drip, no recycled news.

One short email when a build or a price is worth your time. Unsubscribe in one click.