CAN (Controller Area Network) is the bus that Bosch designed for cars in the 1980s and that now runs far beyond them: in trucks, tractors and construction machines, in robots, lifts and medical devices, inside battery packs, inverters, EV chargers and heat pumps. All nodes share one twisted pair. Any node can send, and a frame carries no address: its identifier names the message, and because the lower identifier wins arbitration bit by bit, it also sets the priority. Classical CAN carries 8 bytes per frame, CAN FD up to 64 bytes with a faster data phase, and CAN XL, in the 2024 edition of ISO 11898-1, up to 2048 bytes. ISO-TP segments longer messages, and a DBC file says which bits of which frame hold which signal.
CAN stops there. It defines how frames get onto the wire and to every node, not what a device is or how a network is managed, so it is a transport: the protocols on top give the frames their meaning. In machines and automation that is CANopen, with its object dictionary, SDO, PDOs and network management; in trucks and mobile machines SAE J1939; in diagnostics UDS over ISO-TP. OT Fabric builds them in layers too: go-canbus is the CAN transport, and go-canopen runs on it.
Where you meet it
- Battery management systems, inverters, DC/DC converters and EV chargers, where CAN connects the cells, the power electronics and the controller
- Vehicles and mobile machines: cars, trucks, buses, tractors, construction and mining equipment, forklifts and AGVs, often with SAE J1939 or CANopen on top
- Industrial machines, robots, drives and sensors, lifts and medical equipment, most of them with CANopen on top
- Test benches, data loggers and diagnostics, where engineers record traces, decode them with a DBC file and replay them
Quickstart
From the command line
Download canbusctl from GitHub Releases, or go install github.com/otfabric/canbusctl@latest. Without hardware, a Linux virtual CAN interface is enough to try it:
# A CAN FD capable virtual interface (Linux)sudo ip link add vcan0 type vcan && sudo ip link set vcan0 mtu 72 up
# Watch an interface and decode with a DBC file, passivelycanbusctl monitor -i can0 --dbc vehicle.dbc
# Send a frame on vcan (a real bus needs --yes)canbusctl send -i vcan0 123#DEADBEEF
# Record a trip, then convert or replay itcanbusctl record -i can0 -o trip.blf --duration 10mcanbusctl trace convert trip.blf trip.asccanbusctl replay trip.blf -i vcan0 --speed 2
# A CANable or other serial-line adapter, on any OS, listen-onlycanbusctl monitor -i slcan:/dev/ttyACM0 --slcan-bitrate 250000
# Encode a message from a database, or generate typed Go from itcanbusctl dbc encode vehicle.dbc Engine Speed=1500 Gear=Secondcanbusctl dbc generate vehicle.dbc -o vehicle/vehicle.gocanbusctl is passive by default: monitor, record, stats and every dbc command never transmit, and every command that does needs --yes on an interface that is not virtual, with limits on rate, count and duration.
From Go
go get github.com/otfabric/go-canbusReceive filtered frames from a SocketCAN interface and decode them with a DBC file (abridged from the go-canbus README):
conn, err := socketcan.Open("can0", socketcan.WithFD())bus, _ := canbus.NewBus(conn, canbus.WithStartOnSubscribe())defer bus.Close()
sub, _ := bus.Subscribe(canbus.WithFilters(canbus.FilterSet{ Filters: []canbus.Filter{canbus.ExactStandard(0x123)},}))db, _, err := dbc.ParseFile("vehicle.dbc")for { f, err := sub.Receive(ctx) if err != nil { return } _, signals, _ := db.DecodeFrame(&f) for _, s := range signals { fmt.Println(s.Signal.Name, s.Physical, s.Signal.Unit) }}The same Bus runs on a serial-line adapter (slcan.Open) on Linux, macOS and Windows, or on the in-memory virtual bus in tests. ISO-TP runs on top of it with isotp.Dial, and so does go-canopen: every CANopen service takes a canbus.Bus.
What go-canbus and canbusctl support
| Frames | Classical CAN 2.0A and 2.0B, CAN FD with BRS and ESI, remote and error frames, CAN XL with priority, VCID, SDU type and acceptance field |
| Transports | Linux SocketCAN with kernel filters, timestamps and drop accounting; serial-line (slcan, LAWICEL) adapters such as CANable, CANtact and USBtin on every OS; an in-memory virtual bus with seeded faults |
| Interface administration | List and inspect interfaces, bit rates, CAN FD, restart, vcan and vxcan through rtnetlink, kept apart from opening a socket |
| ISO-TP | Userspace and Linux kernel; normal, extended and mixed addressing, flow control, CAN FD, messages above 4095 bytes |
| Traces | candump, JSON Lines, pcap and pcapng, Vector ASC and BLF, PEAK TRC; replay with original or scaled timing |
| Databases | DBC and KCD read and write, AUTOSAR 4 ARXML read; Intel and Motorola, signed, float, scaling, value tables, multiplexing, exact 64-bit raw values; typed Go code generation |
| Fault testing | A seeded bridge that drops, duplicates, delays, reorders and corrupts frames between two interfaces |
| Platforms | Linux (amd64, arm64, armv7) for SocketCAN; frames, slcan, the virtual bus, ISO-TP, traces and databases also on macOS and Windows. No cgo |