A Python implementation of the CANopen standard. The aim of the project is to support the most common parts of the CiA 301 standard in a simple Pythonic interface. It is mainly targeted for testing and automation tasks rather than a standard compliant master implementation.
The library supports Python 3.9 or newer.
The library is mainly meant to be used as a master.
- NMT master
- SDO client
- PDO producer/consumer
- SYNC producer
- EMCY consumer
- TIME producer
- LSS master
- Object Dictionary from EDS
- 402 profile support
Incomplete support for creating slave nodes also exists.
- SDO server
- PDO producer/consumer
- NMT slave
- EMCY producer
- Object Dictionary from EDS
Install from PyPI using pip:
$ pip install canopen
Install from latest master on GitHub:
$ pip install https://github.com/canopen-python/canopen/archive/master.zip
If you want to be able to change the code while using it, clone it then install it in develop mode:
$ git clone https://github.com/canopen-python/canopen.git $ cd canopen $ pip install -e .
Unit tests can be run using the pytest framework:
$ pip install -r requirements-dev.txt $ pytest -v
You can also use unittest standard library module:
$ python3 -m unittest discover test -v
Documentation can be found on Read the Docs:
http://canopen.readthedocs.io/en/latest/
It can also be generated from a local clone using Sphinx:
$ pip install -r doc/requirements.txt $ make -C doc html
This library supports multiple hardware and drivers through the python-can package. See the list of supported devices.
It is also possible to integrate this library with a custom backend.
Here are some quick examples of what you can do:
The PDOs can be access by three forms:
1st:node.tpdo[n] or node.rpdo[n]
2nd:node.pdo.tx[n] or node.pdo.rx[n]
3rd:node.pdo[0x1A00] or node.pdo[0x1600]
The n is the PDO index (normally 1 to 4). The second form of access is for backward compatibility.
importcanopen# Start with creating a network representing one CAN busnetwork=canopen.Network() # Add some nodes with corresponding Object Dictionariesnode=canopen.RemoteNode(6, '/path/to/object_dictionary.eds') network.add_node(node) # Connect to the CAN bus# Arguments are passed to python-can's can.Bus() constructor# (see https://python-can.readthedocs.io/en/latest/bus.html).network.connect() # network.connect(interface='socketcan', channel='can0')# network.connect(interface='kvaser', channel=0, bitrate=250000)# network.connect(interface='pcan', channel='PCAN_USBBUS1', bitrate=250000)# network.connect(interface='ixxat', channel=0, bitrate=250000)# network.connect(interface='vector', app_name='CANalyzer', channel=0, bitrate=250000)# network.connect(interface='nican', channel='CAN0', bitrate=250000)# Read a variable using SDOdevice_name=node.sdo['Manufacturer device name'].rawvendor_id=node.sdo[0x1018][1].raw# Write a variable using SDOnode.sdo['Producer heartbeat time'].raw=1000# Read PDO configuration from nodenode.tpdo.read() node.rpdo.read() # Re-map TPDO[1]node.tpdo[1].clear() node.tpdo[1].add_variable('Statusword') node.tpdo[1].add_variable('Velocity actual value') node.tpdo[1].add_variable('Some group', 'Some subindex') node.tpdo[1].trans_type=254node.tpdo[1].event_timer=10node.tpdo[1].enabled=True# Save new PDO configuration to nodenode.tpdo[1].save() # Transmit SYNC every 100 msnetwork.sync.start(0.1) # Change state to operational (NMT start)node.nmt.state='OPERATIONAL'# Read a value from TPDO[1]node.tpdo[1].wait_for_reception() speed=node.tpdo[1]['Velocity actual value'].physval=node.tpdo['Some group.Some subindex'].raw# Disconnect from CAN busnetwork.sync.stop() network.disconnect()If you need to see what's going on in better detail, you can increase the logging level:
importlogginglogging.basicConfig(level=logging.DEBUG)