Every fault: method, raw capture, bus state, and what distinguishes it from the others.
Baseline for all comparisons is a healthy three-node bus at 125 kbit/s.
can0 036 [1] AA
can0 037 [1] AA
can0 038 [1] AA
can0 036 [1] AA
can0 037 [1] AA
can0 038 [1] AA
can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP mode DEFAULT group default qlen 10
link/can
can state ERROR-ACTIVE (berr-counter tx 0 rx 0)
bitrate 125000 sample-point 0.875
tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
gs_usb: tseg1 6..16 tseg2 2..8 sjw 1
clock 48000000
re-started bus-errors arbit-lost error-warn error-pass bus-off
0 0 0 0 0 0
All three IDs present. ERROR-ACTIVE, all counters zero.
Discovered: accidentally, while flipping the termination switch.
Symptom:
$ candump can0
SIOCGIFINDEX: No such device
The interface disappeared entirely. Nothing had been unplugged.
Diagnosis: the device was still enumerated — but as something else.
BUSID VID:PID DEVICE STATE
7-2 0483:df11 DFU in FS Mode Not shared
VID:PID had changed from 1d50:606f (canable2, gs_usb) to 0483:df11 — an
STM32 in Device Firmware Update mode. The boot-mode switch sits directly beside
the R120 termination switch and had been bumped.
Root cause: boot-mode switch in the wrong position.
Fix: flip it back, replug to force re-enumeration, re-attach to WSL2, bring the interface up.
Why it's worth cataloguing: the device was physically connected,
successfully enumerated, visible to the OS, and completely unable to do its job.
That present-but-not-functional shape is the same signature as a wedged
USB-serial adapter — the port exists, lsusb lists it, a device node sits in
/dev, nothing works.
A diagnostic habit that stops at "is it connected?" returns a false negative here. The question has to be: connected as what, and answering?
Discovered: accidentally, after repeatedly loosening and re-tightening screw terminals while adding a third conductor.
Symptom:
can0 037 [1] AA
can0 037 [1] AA
can0 037 [1] AA
Node 2 present and perfect. Node 1 absent entirely — not intermittent, not corrupted, gone.
Diagnosis, cheapest test first:
| Test | Cost | Result | Eliminated |
|---|---|---|---|
| Check Node 1's serial output | Free — window already open | Still printing Node 1: sent on schedule |
Power, firmware crash, upload failure |
| Move the monitor's tap from Node 1's terminal to Node 2's | ~1 min | No change, still only 037 |
Listener-side connection |
| Replace both bus wires | ~2 min | Both IDs return | — |
The second test returned no change, which felt like a dead end and wasn't: it eliminated the entire listener-side hypothesis and proved Node 1's frames weren't reaching the bus at all, making the monitor's position irrelevant.
Root cause: a wire that carried signal when first installed and stopped after being unscrewed, re-seated, and clamped alongside a second conductor several times. Likely a fatigued strand inside the insulation, or one that backed out enough to lose contact under the clamp.
Fix: replaced both bus wires.
can0 036 [1] AA
can0 037 [1] AA
can0 036 [1] AA
can0 037 [1] AA
Why it's worth cataloguing: the wire looked perfect. This is the signature of strain-relief and connector-wear failure on real robots — intermittent, correlated with handling or arm pose, invisible on inspection. It's also the class of fault a power cycle appears to "fix," because jostling the hardware momentarily reseats a marginal contact. Those recur weeks later and nobody connects them to the earlier incident.
Method: with all three nodes running, physically disconnect one node's CAN-H
wire while candump runs.
Capture:
can0 036 [1] AA
can0 037 [1] AA
can0 036 [1] AA
can0 037 [1] AA
can0 036 [1] AA
can0 037 [1] AA
Bus state during the fault:
can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP mode DEFAULT group default qlen 10
link/can
can state ERROR-ACTIVE (berr-counter tx 0 rx 0)
bitrate 125000 sample-point 0.875
tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
gs_usb: tseg1 6..16 tseg2 2..8 sjw 1
clock 48000000
re-started bus-errors arbit-lost error-warn error-pass bus-off
0 0 0 0 0 0
Result: the disconnected node vanished from candump. The bus stayed
ERROR-ACTIVE with zero tx/rx errors and no bus-errors. Completely clean.
Signature: one ID missing, bus metrics unchanged. This is a local fault — that device, its connector, or its controller. Nothing is corrupting the shared medium.
In a real fleet this is the cheapest and most useful distinction available: it tells you whether to go look at one joint or at the whole harness.
Method: flash two nodes with the same CAN ID (0x036) but different data
bytes, so both transmit identical arbitration fields.
Why this differs from a collision: CAN arbitration is resolved electrically — a dominant bit overrides a recessive one, the loser backs off and retries, no data lost. That works when IDs differ. With identical IDs and different data, neither node loses arbitration; the conflict surfaces later, in the data field.
Capture:
can0 036 [1] AA
can0 036 [1] AA
can0 036 [1] 55
can0 036 [1] AA
can0 036 [1] 55
can0 036 [1] AA
Bus state during the fault:
can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP mode DEFAULT group default qlen 10
link/can
can state ERROR-ACTIVE (berr-counter tx 18 rx 0)
bitrate 125000 sample-point 0.875
tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
gs_usb: tseg1 6..16 tseg2 2..8 sjw 1
clock 48000000
re-started bus-errors arbit-lost error-warn error-pass bus-off
0 7 0 0 0 0
Result: transmit error counter climbed to 18, bus-errors to 7. The state
remained ERROR-ACTIVE — the errors accumulated but never crossed the
error-passive threshold.
The counter that didn't move matters: arbit-lost stayed at 0. Both nodes
transmit the same identifier, so neither loses arbitration to the other. The
intuitive guess — "duplicate IDs cause arbitration loss" — is wrong, and the
counters say so directly.
Signature vs. fault 3: both produce "expected traffic is missing." Fault 3 leaves the bus completely clean. Fault 4 shows the controller actively detecting transmission errors while nodes are still trying to send. Same surface symptom, opposite counter behavior.
Method: two variants —
- Termination removed from one physical end entirely
- Termination replaced with an effective 60Ω (two 120Ω resistors in parallel)
Capture — termination removed:
can0 036 [1] AA
can0 037 [1] AA
can0 038 [1] AA
can0 036 [1] AA
can0 037 [1] AA
can0 038 [1] AA
Capture — 60Ω substituted:
can0 036 [1] AA
can0 037 [1] AA
can0 038 [1] AA
can0 036 [1] AA
can0 037 [1] AA
can0 038 [1] AA
Bus state (checked after both variants; neither produced a change):
can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP mode DEFAULT group default qlen 10
link/can
can state ERROR-ACTIVE (berr-counter tx 0 rx 0)
bitrate 125000 sample-point 0.875
tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
gs_usb: tseg1 6..16 tseg2 2..8 sjw 1
clock 48000000
re-started bus-errors arbit-lost error-warn error-pass bus-off
0 0 0 0 0 0
Neither variant caused frame loss or moved any counter. The interface stayed
ERROR-ACTIVE throughout, with all three nodes transmitting normally.
This is documented as-is rather than escalated until something broke.
The bus here is a few feet of bench wiring at 125 kbit/s. Signal reflections that would destroy a long deployed harness are simply below the failure threshold at this scale and bitrate. The experiment exposed a limitation of benchtop reproduction, not a property of the protocol.
That's a useful finding in its own right: some physical-layer faults require the physical scale of the real system before their consequences appear. It's precisely why termination problems surface in deployed robots and not in testing.
| Fault | candump symptom |
Bus state | Counters | Distinguishing signal |
|---|---|---|---|---|
| Node disconnect | One ID missing | ERROR-ACTIVE |
All zero | Missing traffic, bus completely clean |
| Duplicate ID | One ID, alternating data bytes | ERROR-ACTIVE |
berr tx 18, bus-errors 7, arbit-lost 0 |
Errors accumulating while nodes still transmit |
| Bad termination | No change | ERROR-ACTIVE |
All zero | No software-visible signal at this bus length |
| Bad wire / connector | One ID missing | ERROR-ACTIVE |
All zero | Indistinguishable from node disconnect by counters — separated by a physical swap test |
| Device in DFU mode | Interface gone entirely | n/a | n/a | Enumerates under a different VID:PID |
Two pairs are not distinguishable by counters alone:
- Node disconnect vs. bad wire produce identical signatures. Both are one ID missing on a clean bus. Separating them required a physical test — checking the node's serial output to confirm it was still transmitting, then moving the monitor's tap point.
- Bad termination vs. healthy are identical at this scale.
So the counters aren't a fault classifier. They narrow the search space, and they're decisive for fault 4, but the resolution comes from combining them with cheap physical tests:
candump— is expected traffic present?- Bus state and counters — is the shared medium unhappy, or just quiet?
- Node serial output — is the sender still running?
- A controlled physical change — does the fault follow the hardware?
That's not a lookup table. It's a progressively narrower search — which is closer to how real robot debugging actually works.