Introduction
This article is an installation and use case guide for communicating with can devices using python. For this article I am focussing on using the PCAN-USB usb-to-can hardware, but follow the link to see if your hardware is supported - python-can interfaces.
Install free commercial tools and drivers
We will first install the peak can drivers and a free software to view the can messages PCAN-View. Next install candb++ which lets you open, view and create dbc files.
Python Libraries Installation
The first library is python-can. The python-can library provides Controller Area Network support for Python, providing common abstractions to different hardware devices, and a suite of utilities for sending and receiving messages on a CAN bus. It can use pywin32 extension to run faster using windowsAPI.
To install perform the following from inside your development venv:
1
2
3
pip install python-can
pip install python-can[pcan]
python -m pip install --upgrade pywin32
Then you have to run the post-install script for pywin32 from OUTSIDE of the venv AND with admin rights. Open a command window as administrator and execute the following
1
2
3
python [venv root folder path]\Scripts\pywin32_postinstall.py -install
The other important library to install specially using DBC files (CAN database file) is to install cantools library. Run this from within your venv.
1
pip install cantools
You are all set !
How to communicate via can
Below is a simple script from the documentation on how to send a simple packet using pcan and configure bus for a specific bitrate 12.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import can
with can.interface.Bus(interface='pcan', bitrate=250000) as bus:
msg = can.Message(
arbitration_id=0xC0FFEE,
data=[0, 25, 0, 1, 3, 1, 4, 1],
is_extended_id=True
)
try:
bus.send(msg)
print(f"Message sent on {bus.channel_info}")
except can.CanError:
print("Message NOT sent")
However, this is quite difficult as you have to set all the individual bytes in a packet manually. A better option is to use the cantools package. Here is an example from the cantools docs where ExampleMessage has an address of 0x1f0 and within the 8 bytes Temperature and AverageRadius are data as shown in the screenshot of the candb++ tool.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import can
import cantools
from pprint import pprint
db = cantools.database.load_file('tests/files/dbc/motohawk.dbc')
pprint(db.messages)
example_message = db.get_message_by_name('ExampleMessage')
pprint(example_message.signals)
can_bus = can.interface.Bus(interface='pcan', bitrate=250000)
data = example_message.encode({'Temperature': 250.1, 'AverageRadius': 3.2, 'Enable': 1})
message = can.Message(arbitration_id=example_message.frame_id, data=data)
can_bus.send(message)
The pprint output is shown below
1
2
3
4
5
6
7
[message('ExampleMessage', 0x1f0, False, 8, 'Example message used as template in MotoHawk models.')]
[signal('Enable', 7, 1, 'big_endian', False, 1.0, 0, 0.0, 0.0, '-', False, None, {0: 'Disabled', 1: 'Enabled'}, None),
signal('AverageRadius', 6, 6, 'big_endian', False, 0.1, 0, 0.0, 5.0, 'm', False, None, None, ''),
signal('Temperature', 0, 12, 'big_endian', True, 0.01, 250, 229.53, 270.47, 'degK', False, None, None, None)]
Important Gotcha for Displaying encoded Data
The data encoded by cantools when printed is shown as a series of hex bytes except if the data can be displayed as a valid ascii character3. This can sometimes create confusion that perhaps the data didn’t get encoded correctly.
1
2
3
4
5
6
>>data
b' \x00\x00\x00\x00\x00\x00\x00'
>>data.hex()
'2000000000000000'
In the above example, 0x20 has an equivalent ascii character of space 4 and therefore displayed with a space. The best way to prevent confusion is to use .hex() method to display the hex values.