Bencz code or something like it

Using Python3 With Socketcan

Linux’s socketcan driver is a good way to interact with a CAN network in Linux. As of Python 3.3, socketcan support is built into Python’s socket module, allowing you to use socketcan from Python directly. This makes it very easy to write scripts to send, receive, and analyze CAN data.

Starting With Socketcan

One of the best things about socketcan in Linux is that you can experiment with it without spending money on a CAN adapter. Assuming you are using at least Ubuntu 14.04, socketcan is built right into the kernel and the tools to work with it are available as an apt package:

apt-get install can-utils
cansend --help

You can also create a virtual CAN interface with the “vcan” driver:

modprobe vcan
ip link add vcan0 type vcan
ip link set vcan0 up

With this you can already send and receive CAN packets on your virtual CAN network. Try this by running candump in one terminal and sending CAN data with cansend in another:

candump -L vcan0
cansend vcan0 012#deadbeef

This capability will come in handy later to test your Python code.

Creating a CAN Socket

The first step to CAN communication with Python is creating a CAN compatible socket and binding it to your virtual CAN interface:

import socket

sock = socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
sock.bind(("vcan0",))

However, the bind call may raise an exception if the interface you’re trying to bind to doesn’t exist - it’s a good idea to handle this:

import socket, sys

sock = socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
interface = "vcan0"
try:
    sock.bind((interface,))
except OSError:
    sys.stderr.write("Could not bind to interface '%s'\n" % interface)
    # do something about the error...

Communication

In python, every1 CAN packet is represented by a 16 byte bytestring. The first 4 bytes are the CAN-ID which can be either 11 or 29 bits long. The next byte is the length of the data in the packet (0-8 bytes) followed by 3 bytes of padding then the final 8 bytes are the data.

A good way to pack/unpack the data is Python’s struct module. Using struct’s syntax, the format can be represented by:

fmt = "<IB3x8s"

Sending CAN Packets

Sending a packet using the previously opened CAN socket requires packing the packet’s CAN-ID and data using the above format, for example to send “hello” with the CAN-ID 0x741:

import struct

fmt = "<IB3x8s"
can_pkt = struct.pack(fmt, 0x741, len(b"hello"), b"hello")
sock.send(can_pkt)

If sending a CAN packet with an extended (29 bit) CAN-ID, the “Identifier extension bit” needs to be set. This bit is defined in the socket module as socket.CAN_EFF_FLAG:

can_id = 0x1af0 | socket.CAN_EFF_FLAG
can_pkt = struct.pack(fmt, can_id, len(b"hello"), b"hello")
sock.send(can_pkt)

Receiving CAN Packets

Receiving CAN packets uses the sock.recv method:

can_pkt = sock.recv(16)

The contents of the packet may be unpacked using the same format. Because the first three bits of the resulting CAN-ID may be used for flags, the returned CAN-ID should be masked with socket.CAN_EFF_MASK. For a normal CAN packet the body will always be 8 bytes long after unpacking but may be trimmed to the actual length received based on the length field from the packet:

can_id, length, data = struct.unpack(fmt, can_pkt)
can_id &= socket.CAN_EFF_MASK
data = data[:length]

Putting it All Together

With these simple pieces it’s now possible to create a Python application that participates in a CAN network. I’ve created a simple command line utility that uses Python to send and listen for CAN packets:

./python_socketcan_example.py send vcan0 1ff deadbeef
./python_socketcan_example.py listen vcan0

The source code can be found on my Github.

  1. CAN FD, which is also supported by socketcan, allows up to 64 bytes of data per packet. I’ll cover this case in a future post.