Package: durand
CANopen library to implement responder nodes.Backends:CAN interfaces viapython-canSynopsisPure python implementationUnder MIT license (2021 Günther Jena)Source is hosted onGitHub.comTested on Python 3.7, 3.8, 3.9 and 3.10Unit tested withpytest, coding style done withBlack, static type checked withmypy, static code checked withPylint, documented withSphinxSupportingCiA301(EN 50325-4)Feature ListObject dictionaryprovides callbacks forvalidation,update,downloadandreadsupports records, arrays and variablesEDS supportdynamically generation of EDS fileautomatically provided via object 0x1021 (“Store EDS”)up to 128 SDO serversexpetited, segmented and block transfer for up- and downloadCOB-IDs dynamically configurablecustom up- and download handlers supportedup to 512 TPDOs and 512 RPDOsdynamically configurabletransmission types: synchronous (acyclic and every nth sync) and event driveninhibit time supportedEMCY producer serviceCOB-ID dynamically configurableinhibit time supportedHeartbeat producer servicedynamically configurableNMT slave serviceboot-up servicecallback for state change providedSYNC consumer serviceCOB-ID dynamically configurablecallback for received sync providedCiA305 Layer Setting Servicefast scan supportedbitrate and node id configuring supportedidentify remote responder supportedCAN interface abstractionpython-canfully supportedautomatic CAN id filtering by subscripted servicesScheduling supporting threaded and async operationTODObuild object dictionary via reading an EDS filesupporting MPDOsTIME consumer serviceUp- and download handler as io streamsExamplesCreating a nodeimportcanfromdurandimportCANBusNetwork,Node,Variable,Record,DatatypeEnumbus=can.Bus(bustype='socketcan',channel='vcan0')network=CANBusNetwork(bus)node=Node(network,node_id=0x01)Congratulations! You have a CiA-301 compliant node running. Layer Setting Service is also supported out of the box.Adding objectsod=node.object_dictionary# add variable at index 0x2000od[0x2000]=Variable(DatatypeEnum.UNSIGNED16,access='rw',value=10,name='Parameter 1')# add record at index 0x2001record=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]=recordAccess valuesThe 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)Add callbacksA more event driven approach is using of callbacks. Following callbacks are available:validate_callbacks- called before a value in the object dictionary is going to be updatedupdate_callbacks- called when the value has been changed (viaod.writeor via CAN bus)download_callbacks- called when the value has been changed via CAN busread_callback- called when a object is read (return value is used )od.validate_callbacks[(0x2000,0)].add(lambdav:v%2==0)od.update_callbacks[(0x2001,2)].add(lambdav:print(f'Update for Parameter 2b:{v}'))od.download_callbacks[(0x2000,0)].add(lambdav:print(f'Download for Parmeter 1:{v}'))od.set_read_callback(0x2001,1,lambda:17)PDO mappingPDOs can 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 SYNCnode.rpdo[0].mapping=[(0x2000,0)]node.tpdo[0].transmission_type=255# event driven (processed when received)InstallpipinstalldurandCreditsThis library would not be possible without:python-canopen: CANopen library (by Christian Sandberg)python-can: CAN interface library (by Brian Thorne)