The bench
Both interfaces sharing one twisted pair at 250 kbit/s. Same bus, same flood, same fixture. Every data point in this article was captured by the two devices in the photo below, running off the wires between them.
The $50 side, up close — two MCP2515 controllers, an isolated SPI digital isolator (NS787 / ISO1042 in the middle), a B0505LS-1WR3 isolated DC-DC for the bus side, and two Phoenix-style terminal blocks labelled CAN0 / CAN1. The yellow jumpers at the corners are the on-board 120 Ω terminator-enable shunts.
And the other end of why this matters — the Kvaser's DB9, the marine-side M12 connector family it has to play nicely with, and a discrete 120 Ω terminator across H/L on the Kvaser DB9 since the Leaf Light v2 doesn't have a switchable on-dongle terminator the way the HAT does.
Test rig
Three nodes share one twisted pair at 250 kbit/s. The Pi HAT's can1 channel acts as the synthetic injector, the HAT's can0 channel is one capturer (Linux SocketCAN, python-can), and the Kvaser Leaf Light v2 is the other capturer (Windows, python-can over Kvaser CANLIB). 120 Ω terminators at each physical end. Common ground jumpered between hosts.
| Item | Detail |
|---|---|
| Bus rate | 250 kbit/s · standard 11-bit ID · 8-byte data frames |
| Injector | Pi 3A+ · python-can over SocketCAN · single ID 0x123 · 32-bit seq in data[0:4] |
| Capturer A | Pi 3A+ · SocketCAN · candump-equivalent loop |
| Capturer B | Windows · python-can kvaser backend · CANLIB driver |
| Run length | 30 s per rate point · 7 s burst window · 50 s saturation flood |
| Frame count | 3,000 to 142,000 transmitted per point |
Frame capture fidelity vs bus load
Above 1000 fps the bus itself saturates, so a "drop" measured here is really an injector-side queue limit, not a dongle limitation (see the throughput chart below).
Throughput plateau — where the bus runs out
At 250 kbit/s a standard 8-byte CAN frame takes ~440 µs on the wire (SOF + 11-bit ID + control + 64 data bits + 15-bit CRC + ACK + EOF). The ~1500 fps plateau is exactly what the bus math predicts under heavy back-to-back arbitration with normal inter-frame spacing.
Burst absorption — both at 100%
Sustained bus saturation — 50-second cangen flood
cangen -g 0 -i on can1. The amber band marks the injector window. Bus is fully saturated at ~1681 fps for the entire duration.
This is the clean answer to "can either dongle keep up with a busy bus": yes, both can. At 250 kbit/s, neither hardware path is the bottleneck.
Timestamp jitter — first place Kvaser pulls ahead
For raw bus sniffing this doesn't matter. For timing-sensitive reconstruction — latency analysis, message-order forensics, gateway benchmarking — it does.
Host CPU — the headline differentiator
For a Pi running just a CAN sniffer this isn't a problem; for a Pi running anything else alongside, the headroom difference is real.
Verdict — where the differences actually appear
| Factor | Pi HAT $50 | Kvaser Leaf Light v2 ~$500 |
|---|---|---|
| Frame capture at realistic loads (≤ 1 kfps) | 100% | 100% |
| Frame capture at saturated bus | Bus-limited (84,059 / 84,059) | Bus-limited (84,059 / 84,059) |
| Timestamp jitter (σ at 1 ms periodic) | 0.530 ms | 0.374 ms |
| Host CPU at sustained 2 kfps | ~51% (one Pi 3A+ core) | ~5% (Windows i-series) |
| Channels | 2 isolated | 1 |
| Galvanic isolation | Yes (opto + DC-DC) | No |
| Driver stack | Linux SocketCAN (in-tree) | Vendor CANLIB (Win/Linux) |
| Listen-only mode | Yes | Yes |
For an evening of bus sniffing on a properly-terminated 250 kbit/s backbone, the $50 HAT is genuinely competitive with a Kvaser Leaf Light v2: same captured frames, same percentage at every realistic load, and the HAT throws in galvanic isolation and a second channel that the Leaf Light doesn't have. The Kvaser pulls ahead on two things the bench can measure: tighter timestamps and ~10× less host CPU.
If either of those matters to your workflow — high-precision timing, or running other software alongside the sniffer on a constrained host — the price gap maps to real value. If neither does, the HAT is the more interesting hardware on the bench.
Caveats — what this bench did not test
- Bus rate fixed at 250 kbit/s. Both interfaces support 1 Mbit/s; the test rate matches the most common automotive / marine / industrial deployment but high-speed buses may differentiate the dongles more.
- Hosts differ. Kvaser ran on a Windows i-series laptop, HAT on a Pi 3A+. CPU comparison is apples-to-oranges in absolute terms; the qualitative gap (one driver consumes orders of magnitude more host time per frame than the other) holds regardless.
- Entry-level Kvaser only. USBcan Pro 2xHS v2 and equivalent pro-grade hardware were not tested. Leaf Light v2 specifically does not have galvanic isolation; higher-end Kvasers do.
- Synthetic single-ID load. Real CAN traffic involves many IDs, varying priorities, and arbitration losses. Behaviour under multi-priority traffic was not benchmarked.
- Python user-space injector limits. The rate-sweep "drops" above 1 kfps were Python user-space queue limits, not dongle limits. The saturation flood (
cangen) is the authoritative high-load result.
Coming in Part 2: 12-hour clock-drift soak, multi-ID mixed traffic, 1 Mbit/s flood, listen-only ACK verification on a scope.
Try it yourself — Pi setup + starter scripts
Whole bench is two Python files and three ip link commands. The HAT itself is plug-and-go — drop it on the GPIO header, add the MCP2515 device-tree overlays, bring the interfaces up, run the scripts. No Pi-side driver install, no kernel rebuild. github.com/joelundgren/circuitessentials-can-bench has both scripts plus a README.
1. Enable SPI + MCP2515 overlays in /boot/firmware/config.txt (older Pi OS images use /boot/config.txt):
dtparam=spi=on dtoverlay=mcp2515-can0,oscillator=16000000,interrupt=25 dtoverlay=mcp2515-can1,oscillator=16000000,interrupt=23
2. Reboot, then bring both interfaces up at 250 kbit/s:
sudo ip link set can0 up type can bitrate 250000 sudo ip link set can1 up type can bitrate 250000 ip -br link show can0 can1
The two yellow shunts on the HAT itself enable the on-board 120 Ω terminators (one per channel). Set both to ON when there are only two nodes on the bus — which is the case for this bench. Pinout for the GPIO header sits on the Amazon product page if you need to confirm which pin goes where; the overlays above match the stock Waveshare layout.
3. Capture every frame on can0 for 30 seconds:
python3 capture.py --backend socketcan --interface can0 --duration 30 --out cap.csv
4. From a second node (or the HAT's other channel), inject at a controlled rate:
python3 inject.py --interface can1 --rate 1000 --duration 30 --out tx.csv
Every frame carries a 32-bit sequence number in data[0:4] — diffing the seq counters in cap.csv against tx.csv is how the article above measured "did anything get dropped." For a real bus-saturation flood, use cangen from can-utils (sudo apt install can-utils) — its kernel-rate TX path beats anything the python-can user-space loop can drive:
cangen can1 -g 0 -I 123 -L 8 -D i -i
The same capture.py works against the Kvaser with --backend kvaser --interface 0 — channel index instead of a SocketCAN name, otherwise identical. CANLIB has to be installed on the host first.
python-can + psutil, run.
A bench session answers what one rig does. The references below answer why, and they pay dividends when something on the bus does not behave the way you expected. Affiliate links route through Bookshop.org.
A Comprehensible Guide to Controller Area Network
Wilfried Voss
CAN from arbitration through fault confinement, from a longtime industry consultant. Clearest practitioner-oriented intro I've seen for engineers coming from a SPI/I2C background.
BookshopHigh-Speed Digital Design: A Handbook of Black Magic
Howard Johnson & Martin Graham
CAN runs on a differential pair, and pairs misbehave the moment you treat them like logic. Johnson on return paths and terminations explains the bus-error counter spikes you see in the lab.
BookshopMaking Embedded Systems
Elecia White
If you are wiring the CAN HAT into a real product rather than a bench rig, White's chapters on drivers, queues, and bring-up methodology save weeks of pain.
BookshopFull reading list across embedded, PCB, signal integrity, firmware, and protocols on the /library page.
The Amazon link earns a small commission at no extra cost to you. The Kvaser link goes to a distributor (no commission). Book links above route through Bookshop.org, which supports independent bookstores and pays the site a slightly higher commission than Amazon. Both CAN interfaces are on this bench right now — the comparison data came from a single bench session with both running side-by-side. Link choices follow the evidence, not the kickback.
Want the full bench? See /bench →