How to Use CAN on TorizonCore
In this article, we will show how to setup one or more CAN networks to be accessed in a containerized application in TorizonCore.
This article complies to the Typographic Conventions for Torizon Documentation
- Your module must be compatible with Torizon.
- TorizonCore installed on a Toradex module.
- Have completed the Torizon Quickstart Guide lessons.
- Have installed and configured the Visual Studio Code Extension for Torizon.
- Check the CoM Compatibility and pin assignment for exposing the CAN signals, for the module you plan o use.
- Knowledge of Device Tree Overlay on Torizon.
CAN Support on TorizonCore 5
Starting at TorizonCore 5, the following Kernel modules are ready to use:
- can: CAN protocol module.
- can-dev: Standard library for CAN drivers.
- can-raw: Offers access to the CAN bus via the BSD socket API.
- can-bcm: Broadcast Manager module, offers content filtering, timeout monitoring, sending of RTR frames, and cyclic CAN messages without permanent user interaction.
- can-j1939: Available since our BSP 5, giving support for the CAN J1939 standard.
- can-gw: CAN gateway module, used to route CAN frames.
- vcan: Virtual Local CAN interface.
Enabling the CAN Interfaces
The first step to prepare our environment is to check if our module has the desired CAN interface enabled, otherwise, we must enable it.
As TorizonCore 5 follows the same structure as the BSP 5, the best way to check if your module has its CAN interfaces enabled is by checking the Kernel Support on CAN (Linux) article. Please also take into consideration that your module must be compatible with Torizon.
If your module does not have CAN enabled, you'll have to enable it by using Device Tree Overlays on TorizonCore.
Considering that you have a CAN interface enabled, you can check if the interface is present by executing the command:
## ip link show can0
4: can0: <NOARP,ECHO> mtu 16 qdisc noop state DOWN mode DEFAULT group default qlen 10
Considering that we have a TorizonCore compatible module with a CAN interface enabled, we can now start the procedures for a Container to make use of it.
Creating a Container for CAN Communication
The first thing we need to do is to create our container for interfacing with CAN. To be able to build your own containers, please follow the article Configure Build Environment for Torizon Containers.
Always build your docker images for TorizonCore in a host development computer.
For our tutorial, we'll take the Dockerfile showed below as a template for a Container to interact with CAN.
# Use the parameter below for Arm 32 bits (like iMX6 and iMX7)
# ARG IMAGE_ARCH=arm32v7
RUN apt-get -y update && apt-get install -y \
&& apt-get clean && apt-get autoremove && rm -rf /var/lib/apt/lists/*
As you have observed, the definition of our custom container image has the following packages:
- iproute2: Which is necessary for configuring, enabling, and disabling the CAN network interfaces.
- can-utils: A set of Linux command-line utilities for debugging CAN network.
- Python3-CAN: A wrapper of SocketCAN for Python3.
- git: If you want to clone example projects/libraries to interact with CAN
- Plus, other useful tools as nano, to edit files within the container.
From those packages, the most important ones are iproute2, because without it we are unable to configure the CAN interface, and can-utils, which allows us to send and receive CAN messages using the terminal.
With the Dockerfile for our sample Container image for CAN, you can build it in your host computer with the following command:
$ docker build -t can-torizon-sample .
When the build is complete, you can compress the container in a tar file so you can send it to your target development board:
$ docker save -o can-torizon-sample.tar can-torizon-sample
As said, you can send the compressed container to your target board:
$ scp can-torizon-sample.tar torizon@X.X.X.X:/home/torizon/
In your target board with TorizonCore 5, you can load your container directly from the tar file:
# docker load -i can-torizon-sample.tar
After the load, you can check that the image is in the system:
# docker image ls
verdin-imx8mm-06612136:~$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
can-torizon-sample latest 5ea7a31bf664 About an hour ago 203MB
Now that we have our customized container image for CAN in our target system, we can execute it.
$ docker run -it --rm --name=can-test --net=host --cap-add="NET_ADMIN" \
-v /dev:/dev -v /tmp:/tmp -v /run/udev/:/run/udev/ \
The main secret behind the setup and usage of CAN on containers with TorizonCore is the usage of the following flags when running your container:
- --net=host : This will make Docker to uses the host's network stack for the container.
- --cap-add=NET_ADMIN : For interacting with the network stack, this allow to modify the network interfaces.
Once within the container console, you'll have to configure the CAN Network. This process is much similar to the setup of CAN on Linux.
We'll set only one CAN interface. But depending on your CoM, you may have more CAN interfaces available.
First, let us configure the CAN0 interface with a bitrate of 500000 bps:
## ip link set can0 type can bitrate 500000
If everything went fine (no complaints!), we can bring that interface up:
## ip link set can0 up
To be sure that the interface is now set and ready, check it by using the following command:
## ip link show
This will output something similar as this:
root@verdin-imx8mm-06612136:/home/torizon# ip link show can0
3: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP mode DEFAULT group default qlen 10
Now you can start using applications that may connect to the CAN Network, or simply use the can-utils for testing.
For programming a Linux application in C/C++, you can use the SocketCAN API.
There is also a wrapper for it in Python.
Simple testing with can-utils
One way to test if everything is OK with your CAN communication, you can do the following:
- Use the
cansend <interface> <message>to send CAN messages on a given interface. Like for example:
## cansend can0 123#deadbeef
If everything is OK, you'll see a CAN message "DEADBEEF" with ID 123 on CAN.
- Use the
candump <interface>command to start listening to incoming CAN messages on a given interface. Like for example:
root@verdin-imx8mm-06612136:/home/torizon# candump can0
can0 123  DE AD BE EF
If everything is OK, you'll see the messages appearing as other devices start sending messages in the same CAN network that your device is connected to.
You can read more about can-utils on its project page.
Developing a CAN application with Visual Studio Code and Python
As you observed in the process of having your container for CAN, you had to manually create your Dockerfile, build your container, upload and load it on the target device. Not to mention the process in the sequence of loading your application as well.
In this particular example, let us see how to setup a Python application to interact with CAN, with our personalized container, having everything configured from within Visual Studio Code and the Torizon Extension on it.
Please, make sure to have followed all the steps for configuring your environment for Visual Studio Code and Torizon Extension.
With your environment all set, please create a new Python3 project, according to your target architecture.
Then, go to the Torizon Extension menu and configure your project with the following parameters:
- username: root
- volumes: /dev and /dev
- extraparms: cap_add with value ["CAP_NET_ADMIN"] and network_mode with value host
- extrapackages: iproute2 can-utils python3-can
The NetworkManager does not support the configuration of a CAN interface. That said, for using CAN it's required to execute the application as root, in order to manipulate iproute2 utility and configure it accordingly. Please avoid executing this container application in a privileged mode to prevent security breaches.
See the figure below as an example of how your Torizon Extension setup must look like:
Basically, what we did was the addition of our required packages, as we did in the Dockerfile, plus the required environment flags we used in the docker run....
Now, go to the Explorer menu in the Visual Studio Code, open the main.py code and paste the following code:
Python code for sample application for CAN communication
# Using the extension we must setup the can interface within Python
os.system("ip link set can0 type can bitrate 500000")
os.system("ip link set can0 up")
# set up a CAN bus
bus = can.interface.Bus(channel="can0", bustype='socketcan')
# Our Transmission ID
tx_arb_id = 0x123
# Our CAN Frame
obd_req_data = [0xDE, 0xAD, 0xBE, 0xEF]
# Send our message in CAN 11-bit format
msg = can.Message(arbitration_id=tx_arb_id, data=obd_req_data, is_extended_id=False)
message = bus.recv(10.0) # Quite a good timeout for a demonstration!
if message is None:
print('Timeout occurred, no message.')
# Then we print the received message if any
To load this application container in your target, which the Torizon Extension asked for its credentials before (hostname/IP, user, and password, for example), you can just press F5 on your keyboard. The utility will then begin to build the container image, will load it into the device, and will start the execution in Debug mode, a process that you can observe in the Output section of the Visual Studio Code.
You can also add breakpoints to track parts of interest in your program.
How to change CAN clock source - Verdin i.MX8MM
Due to product changes on Verdin i.MX8M Mini 0055 and 0059, the CAN clock source changed from 20MHz to 40MHz. So, the projects using CAN protocol since
TorizonCore 5.7.0_devel_202205 may stop working properly. A workaround to avoid this problem is to change the CAN clock from 40MHz to 20MHz using TorizonCore Builder Tool - Customizing TorizonCore Images and Device Tree Overlays on Torizon.
The following device tree overlay should work to change the
clk40m node, which is the node that describes the CAN clock source. Copy and the following
.dts, and then use TorizonCore Builder Tool to apply and deploy to the board.
compatible = "toradex,verdin-imx8mm";
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <20000000>;
- Nov/2020 - Reading Vehicle OBD-II data through CAN within a containerized application in Embedded Linux: article at CNX-Software about how to create a containerized application with TorizonCore 5 in order to read OBD-II through CAN interface using Python