Skip to content

Python CANopen library with focus on implementing responder nodes

Notifications You must be signed in to change notification settings

semiversus/python-durand

Repository files navigation

Python Durand - CANopen Responder Device Library

A CANopen library to implement responder nodes.

Backends:

Synopsis

  • Pure Python implementation
  • Licensed under MIT (2021 Günther Jena)
  • Source code hosted on GitHub.com
  • Tested on Python 3.7, 3.8, 3.9, and 3.10
  • Unit tested with pytest, coding style enforced with Black, static type checking with mypy, static code analysis with Pylint, documentation generated with Sphinx
  • Supports CiA301 (EN 50325-4)

Feature List

  • Object Dictionary:
    • Provides callbacks for validation, update, download, and read operations
    • Supports records, arrays, and variables
  • EDS Support:
    • Dynamic generation of EDS files
    • Automatically provided via object 0x1021 ("Store EDS")
  • SDO Servers:
    • Supports up to 128 SDO servers
    • Expedited, segmented, and block transfer for upload and download
    • Dynamically configurable COB-IDs
    • Custom upload and download handlers supported
  • PDO Support:
    • Up to 512 TPDOs and 512 RPDOs
    • Dynamically configurable
    • Transmission types: synchronous (acyclic and every nth sync) and event-driven
    • Supports inhibit time
  • EMCY Producer Service:
    • Dynamically configurable COB-ID
    • Supports inhibit time
  • Heartbeat Producer Service:
    • Dynamically configurable
  • NMT Slave Service:
    • Boot-up service
    • Callback for state change provided
  • SYNC Consumer Service:
    • Dynamically configurable COB-ID
    • Callback for received sync provided
  • CiA305 Layer Setting Service:
    • Supports fast scan
    • Configurable bitrate and node ID
    • Identify remote responder supported
  • CAN Interface Abstraction:
    • Full support for python-can
    • Automatic CAN ID filtering by subscribed services
  • Scheduling:
    • Supports threaded and async operation

TODO:

  • Build object dictionary via reading an EDS file
  • Support MPDOs
  • TIME consumer service
  • Up- and download handler as I/O streams

Examples

Creating a Node:

import can
from durand import CANBusNetwork, Node, Variable, Record, DatatypeEnum

bus = can.Bus(bustype='socketcan', channel='vcan0')
network = CANBusNetwork(bus)
node = Node(network, node_id=0x01)

Congratulations! You now have a CiA-301 compliant node running. The Layer Setting Service is also supported out of the box.

Adding Objects:

od = node.object_dictionary

# Add variable at index 0x2000
od[0x2000] = Variable(DatatypeEnum.UNSIGNED16, access='rw', value=10, name='Parameter 1')

# Add record at index 0x2001
record = Record(name='Parameter Record')
record[1] = Variable(DatatypeEnum.UNSIGNED8, access='ro', value=0, name='Parameter 2a')
record[2] = Variable(DatatypeEnum.REAL32, access='rw', value=0, name='Parameter 2b')
od[0x2001] = record

Accessing Values:

The objects can be read and written directly by accessing the object dictionary:

print(f'Value of Parameter 1: {od.read(0x2000, 0)}')
od.write(0x2001, 1, value=0xAA)

Adding Callbacks:

A more event-driven approach is to use callbacks. The following callbacks are available:

  • validate_callbacks: Called before a value in the object dictionary is updated
  • update_callbacks: Called when the value has been changed (via od.write or via CAN bus)
  • download_callbacks: Called when the value has been changed via CAN bus
  • read_callback: Called when an object is read (return value is used)
od.validate_callbacks[(0x2000, 0)].add(lambda v: v % 2 == 0)
od.update_callbacks[(0x2001, 2)].add(lambda v: print(f'Update for Parameter 2b: {v}'))
od.download_callbacks[(0x2000, 0)].add(lambda v: print(f'Download for Parameter 1: {v}'))
od.set_read_callback(0x2001, 1, lambda: 17)

PDO Mapping:

PDOs can be dynamically mapped via the SDO server or programmatically. The PDO indices start at 0.

node.tpdo[0].mapping = [(0x2001, 1), (0x2001, 2)]
node.tpdo[0].transmission_type = 1  # Transmit on every SYNC

node.rpdo[0].mapping = [(0x2000, 0)]
node.tpdo[0].transmission_type = 255  # Event-driven (processed when received)

Installation

pip install durand

Credits

This library would not be possible without:

About

Python CANopen library with focus on implementing responder nodes

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages