CAN setup

AK drivers run at 1 Mbit/s. The manual says changing it is not recommended.

Linux (production)

socketcan is the production path. The kernel owns the bitrate — python-can cannot set it — so bring the interface up first:

sudo ip link set can0 up type can bitrate 1000000
sudo ip link set can0 txqueuelen 1000

Then, with no privileges:

from cubemarspycan import CanTransport, MotorBus

with CanTransport.open("socketcan:can0") as tp, MotorBus(tp) as bus:
    ...

CanTransport reads the configured bitrate back from /sys/class/net/can0/can_bittiming/bitrate and warns if it is not 1 Mbit/s, because a mismatch looks exactly like a wiring fault.

cubemars doctor lists your interfaces, their state and their bitrates, and prints the bring-up lines. It never runs them. This library does not shell out to sudo, ever.

Persisting it

With systemd-networkd, /etc/systemd/network/80-can.network:

[Match]
Name=can0

[CAN]
BitRate=1M

No hardware? Use vcan

A virtual CAN interface behaves like a real one. It is how this project covers the socketcan-only code — interface opening, bitrate and controller-state read-back, kernel receive filters — that python-can’s in-process virtual backend never touches:

sudo modprobe vcan
sudo ip link add dev vcan0 type vcan
sudo ip link set up vcan0
cubemars scan --url socketcan:vcan0
pytest -m socketcan                      # the interface-backed tests

Note that a vcan interface has no bit timing and no error state, so read_socketcan_bitrate() and read_socketcan_state() return None for it. That is the useful case to test: it is the same shape as a gs_usb adapter whose kernel exposes no can_bittiming directory, which once made doctor report “no bitrate set” for a perfectly healthy 1 Mbit/s link.

macOS and Windows (development)

socketcan is a Linux kernel facility and does not exist elsewhere. Asking for it raises UnsupportedPlatform with that explanation rather than a confusing OSError.

Use a USB-CAN adapter:

pip install "cubemarspycan[slcan]"
cubemars scan --url "slcan:/dev/tty.usbmodem1101@1M"

pip install "cubemarspycan[gs-usb]"      # CANable / candleLight native
cubemars scan --url "gs_usb:0@1M"

Loop rates on slcan

An slcan send is an ASCII line over a USB CDC endpoint: typically 0.5–2 ms, with macOS scheduling spikes into the tens of milliseconds. Plan for 200–500 Hz; 1 kHz needs gs_usb or Linux socketcan.

MotorBus.transport.stats.tx_percentiles() reports p50/p95/max send time in milliseconds, so this is measurable rather than mysterious.

Bringing your own bus

The URL forms are sugar. Any can.BusABC works:

import can
from cubemarspycan import CanTransport, MotorBus

bus = can.interface.Bus(channel="can0", interface="socketcan", fd=False)
with MotorBus(CanTransport(bus)) as motor_bus:
    ...

CanTransport only shuts down buses it opened itself.