python-can API

The python-can module is imported with:

import can

Note

In examples below, <INTERFACE> should be replaced with csscan_mux.

Note

In examples below, <CHANNEL> should be replaced with the channel for the relevant USB port.

The two main python-can objects are:

  • Bus: Defines a CAN-bus interface[1]

  • Message: Defines a CAN-bus message (containing timestamp, ID, data, and more)[2]


Detect interfaces

The python-can package supports auto-detection of available interfaces.

Detect interface on Windows
>>> can.detect_available_configs("<INTERFACE>")
[{'interface': '<INTERFACE>', 'channel': 'COM19'}]
Detect interface on Linux
>>> can.detect_available_configs("<INTERFACE>")
[{'interface': '<INTERFACE>', 'channel': '/dev/ttyACM0'}]

Auto-detection can be used to automatically find and open a bus.

import can

# Detect busses
configs = can.detect_available_configs("<INTERFACE>")
assert len(configs) == 1

# Open bus
with can.Bus(interface=configs[0]["interface"], channel=configs[0]["channel"]) as bus:
    ...

Receive messages

The most basic way of receiving messages is demonstrated below.

import can

# Open bus
with can.Bus(interface="<INTERFACE>", channel="<CHANNEL>") as bus:

    # Receive messages
    for msg in bus:
        print(f"{msg.timestamp:.3f} {msg.arbitration_id:X} {msg.data.hex()}")

The python-can notifier can be used for more advanced use-cases. Below demonstrates how to use the existing python-can Printer and Logger together with a custom Listener.

import can
from time import sleep

class CustomListener(can.Listener):
    def on_message_received(self, msg: can.Message) -> None:
        # Some custom handling of received messages
        pass

# Open bus
with can.Bus(interface="<INTERFACE>", channel="<CHANNEL>") as bus:

    # Printer (prints formatted messages to screen)
    print_listener = can.Printer()

    # Logger (logs formatted messages to file)
    log_listener = can.Logger("out.log")

    # Custom listener (invokes listener call-back when message received)
    custom_listener = CustomListener()

    # Create notifier with printer, logger, and custom listeners
    notifier = can.Notifier(bus, listeners=[print_listener, log_listener, custom_listener])
    sleep(10)
    notifier.stop()

For more information, see https://python-can.readthedocs.io/en/stable/listeners.html#notifier.


Transmit messages

The most basic way of transmitting messages is demonstrated below.

import can

# Open bus
with can.Bus(interface="<INTERFACE>", channel="<CHANNEL>") as bus:

    # Transmit message
    bus.send(can.Message(arbitration_id=0x123, is_extended_id=False, data=[0x01, 0x23, 0x45, 0x67], channel="1"))

Note

To receive notifications of successfully sent messages using the CANmod.router, the TX acknowledgement setting must be enabled in the device configuration.

Periodic transmit messages can be configured using the python-can broadcast-manager, see https://python-can.readthedocs.io/en/stable/bcm.html.


Muxing/demuxing configuration (advanced)

Note

The below is primarily relevant if you have configured your device to use custom CAN IDs for the primary CAN

Configuration of the muxing/demuxing parameters can be done using additional arguments to the can.Bus class:

  • channel - Can be either an instance of a can.Bus or an argument to the underlying driver. If the argument is an instance of can.Bus, the demuxing is applied to all messages from this channel, and all messages sent through the muxer is routed through the channel. Else, the argument is forwarded to the instance created by the driver argument.

  • driver - Which python-can driver to use for the underlying bus. If not specified, it will default to csscan_serial [3]. All other supplied keywords are forwarded to this driver.

  • mux_input - String or dictionary of demuxing configurations. The format is “CAN ID (hex):Channel 1;Channel 2;Channel 3;Channel 4” for the string. If the CAN ID is written using 8 characters, it will be treated as an extended ID. Multiple configurations can be chained using commas. I.e. a configuration for the CAN IDs 0x010 and 0x012 could be “010:1;2;3;4,012:5;6;7;8”. When using a dictionary, the dictionary key is the CAN ID. For extended IDs, the CAN ID must be bitwise OR’ed with 0x80000000. The value for the dictionary is a list of channel strings, i.e. 0x10: ['1', '2', '3', '4']. Defaults to “010:1;2;3;4”.

  • mux_output - String or dictionary of muxing configurations. Same format as the mux_input argument. Defaults to “011:1;2;3;4”.

  • mux_fd - Flag controlling if the muxed output messages should be emitted as classical CAN or CAN FD frames. If enabled, the enhanced DLC capabilities of CAN FD will be utilized. Defaults to true.

  • mux_brs - Flag controlling if the muxed output messages should be emitted using CAN FD bit rate switching. Defaults to the same value as mux_fd.

For direct usage over USB with i.e. the CANmod.router using the default config, the default options for these configuration arguments are sufficient, and only the channel argument needs to be supplied with the correct USB interface.

If a message does not match any of the configured CAN IDs, it is passed on. For transmitting/muxing, sending a frame with an unknown channel will pass the CAN frame to the underlying python-can driver. For receiving/demuxing, any incoming messages with IDs which are not present in the configuration will get passed on unaltered.