This article is aimed at giving the reader a glimpse of the CAN protocol support in the Linux environment through the SocketCAN stack.
The CAN (Controller Area Network) is a standard serial communication protocol that is used to establish communication between various ECUs (electronic control units) inside a vehicle. The current intensive use of electronics within vehicles, the complex network topologies, the need for better wiring harnesses and for reliable transfer of critical data, as well as the ever increasing speed and bandwidth requirements are a few reasons why we need a robust serial communication protocol in the automobile industry.
The CAN protocol offers a multi-master and message-oriented communication architecture in which messages are identified and prioritised using message identifiers. This protocol also incorporates mechanisms such as bitwise arbitration and robust error handling techniques, which assure reliable communication. 11898-1:2015 is the latest standard specification for the CAN 2.0 protocol.
CAN communication occurs in terms of frames. There are four types of frames supported by the CAN protocol.
- Data frame: To transmit regular payloads across the nodes
- Remote frame: To request the data frame from other nodes (a kind of request-response model)
- Error frame: To transmit fault detection in frames
- Overload frame: To communicate to the network when a node is busy processing incoming frames
At the outset, a data frame will have the following fields:
- ID: 11 bits field for standard and 29 bits for extended frames
- Data: 0 to 8 bytes of payload
- Data Length Code (DLC): Number of bytes present in the data
CAN FD (flexible data rate), an extension of the original CAN protocol, supports increased bandwidth with payloads up to 64 bytes.
The CAN protocol is supported in the Linux environment through SocketCAN, which is a set of drivers and a networking stack. Volkswagen Research contributed the initial code by adding support to the Linux kernel v2.6.25 in 2008. Current kernel versions come with a rich set of drivers and networking stack support for a wide variety of CAN interfaces.
Setting up Virtual CAN
Are you thinking of playing around with SocketCAN? The Virtual CAN (vcan) interface provided by the Linux kernel is a good solution for this. It allows initial application development based on vcan and later migration to physical interfaces supported by real hardware. It works almost like a loopback interface in a TCP/IP stack, where any frames written to vcan can be captured on the same machine.
The following simple steps will help you to start with SocketCAN.
To load the kernel module for virtual can support, give the following command:
sudo modprobe vcan
Then add a virtual interface as follows:
ip link add dev vcan0 type vcan
Next, configure and bring up the interface. For a physical interface, we can set the bit rate (speed of the CANBus) and communication mode like loopback or listen only, using the following command:
ip link set up vcan0
To list enabled interfaces, type:
ifconfig -a ifconfig vcan0
Working with user space utilities
For user space communication, the can-utils package comes with various commands. Here are some of the steps for setting up can-utils, and a few examples of its basic usage.
In the Ubuntu Linux distribution, can-utils is available through the software repository:
sudo apt-get install can-utils
If can-utils is not available from the package manager, we can build it from the source code, as follows. This is helpful when cross-compiling is required for target platforms.
git clone can-utils make make install
The following are some examples of the usage of can-utils. We can replace vcan0 by can0, can1, etc, when physical interfaces are available.
a. To send a standard can frame with 0x101 as the ID and 0x41, 0x42, 0x43, 0x44 as a 4-byte payload, type:
b. To send an extended frame with 0xA1B2 as the ID, give the following command:
cansend vcan0 0000A1B2#3450
c. To capture all incoming can frames on vcan0 along with timestamps, type:
candump -t Absolute vcan0
d. Interface name can be any to capture all the interfaces, as follows:
candump -t A any
e. To store all captured frames in a log file, use the following command:
candump -l vcan0
These recorded frames can be replayed using the player or be displayed in a user friendly format using log2asc.
cangen is used to generate random frames continuously, so do explore the various options of cangen to control IDs, the payload and the timing of generated frames.
C/C++ APIs for application development
In C/C++, CAN APIs are supported by extending Berkeley Socket APIs and by adding a new protocol family called PF_CAN. Let’s look at some simple steps for developing a CANBus application using these APIs.
Step 1: Create an empty socket API with PF_CAN as the protocol family:
int s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
Step 2: Bind the socket to one of the interfaces (vcan0 or canx):
struct sockaddr_can addr; struct ifreq ifr; strcpy(ifr.ifr_name, ifname); ioctl(s, SIOCGIFINDEX, &ifr); addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex; bind(s, (struct sockaddr *)&addr, sizeof(addr));
Step 3a – Writing CAN frames: To send a frame with the ID 0x101 and a two byte payload of 0x41, 0x42, use the following commands:
struct can_frame frame; frame.canid=0x101; frame.can_dlc=2; frame.data=0x41; frame.data=0x42; int nbytes = write(s, &frame, sizeof(struct can_frame));
This can be called periodically on an even basis as per application needs.
Step 3b – Reading CAN frames: Use the following commands in this step:
nbytes = read(s, &frame, sizeof(struct can_frame)); //Extract frame.id, frame.can_dlc, frame.data[i]
This can be called repeatedly, preferably in an independent thread execution.
Step 4 – Closing the socket: Give the following command:
Wrappers are available in different languages to support CAN communication. A few bindings are listed below:
- Python comes with two types of implementations, through the package.
- Native implementation with direct interfacing to kernel is available from Python 3.3 onwards.
- The second implementation is based on ctypes, which is a wrapper around the standard C library; it is suitable for Python 2.7 and Python3 <3.4 (https://python-can.readthedocs.io/en/2.1.0/interfaces/socketcan.html).
- Java: This is a JNI wrapper by Entropia.de (https://github.com/entropia/libsocket-can-java).
- Node.js: This is through the npm package ‘socketcan’ (https://github.com/sebi2k1/node-can).
- Qt offers a good set of C++ APIs, signals and slot mechanisms for CAN communication via the serial bus module. These APIs are common for various back-ends (https://doc.qt.io/qt-5/qtcanbus-backends.html).
- Apart from the SocketCAN back-end, Qt 5.12 has also added the VirtualCAN back-end which works over TCP. This is helpful for initial application development even on systems like Windows, which do not support the SocketCAN stack and APIs.
- The Android Automotive project offers HAL APIs and examples for CANBus integration.
- Wireshark is a popular packet analyser. It has a good plugin set for the dissection of raw CAN frames as well as analysing upper layer protocols like CANOpen, UDS and J1939.
Here is a list of a few libraries, toolkits and bridges built on top of SocketCAN:
- CANOpen stacks in Linux — CANFestival, CANOpenSocket
- Libraries and simulators for Unified Diagnostic Services (UDS), a vehicle diagnostic protocol (https://github.com/openxc/uds-c and https://github.com/zombieCraig (UDSim, uds-server, ICSim))
- A python package for the Vehicle Interface Toolkit
- Python package
- Carloop CAN Analyzer works with SocketCAN (Carloop.io)
- Kayak, a CANBus analysis tool
- Socketcand, a bridging interface between CANBus and TCP/IP networks
- Cannelloni, a tunnelling implementation of SocketCAN over Ethernet (https://github.com/mguentner/cannelloni)
The author hopes that the hints and links given will help the reader to explore the subject further.
The author is a faculty member at C-DAC’s advanced computing training school, Pune and an evangelist in the embedded systems and IoT domains. He loves teaching and open source. You can reach him at email@example.com