Using Python3 With Socketcan
10 Jul 2016Linux’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.