CANopen Stack for Python

2.4.1 · active · verified Sun Apr 12

The `canopen` library provides a comprehensive Python implementation of the CANopen communication protocol. It allows developers to interact with CANopen devices, manage CANopen networks, and implement custom CANopen nodes. The current stable version is 2.4.1. Releases occur on an as-needed basis, driven by bug fixes, new features, and compatibility updates, typically every few months to once a year.

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to connect to a CAN bus, add a CANopen node using an EDS file, and perform basic SDO (Service Data Object) read/write operations. It includes error handling for common issues like CAN connection problems and missing EDS files. Remember to replace placeholder values for `CAN_BUSTYPE`, `CAN_CHANNEL`, `NODE_ID`, and `EDS_FILE` with your actual setup. A dummy `example.eds` is created if not found, but a real EDS file for your device is crucial for proper communication.

import canopen
import time
import os

# Configuration
CAN_BUSTYPE = os.environ.get('CAN_BUSTYPE', 'socketcan') # e.g., 'socketcan', 'pcan', 'ixxat'
CAN_CHANNEL = os.environ.get('CAN_CHANNEL', 'can0')     # e.g., 'can0', 'PCAN_USBBUS1'
NODE_ID = int(os.environ.get('CANOPEN_NODE_ID', '1'))
EDS_FILE = os.environ.get('CANOPEN_EDS_FILE', 'example.eds') # Replace with your device's EDS file

# Create a dummy EDS file for demonstration if it doesn't exist
# In a real application, you would use a manufacturer-provided .eds
if not os.path.exists(EDS_FILE):
    print(f"Warning: {EDS_FILE} not found. Using a generic one (may not work with your device).")
    with open(EDS_FILE, 'w') as f:
        f.write("""
[FileInfo]
; Generated EDS for a generic CANopen device
FileName = ExampleDevice.eds
FileVersion = 1.0
VendorName = Example Vendor
ProductName = Example Product
ProductCode = 0
RevisionNo = 1.0
CreationTime = 2023-01-01 12:00:00

[Device]
DeviceType = 0x00020002

[0x1000] ; Device Type
ParameterName = Device Type
ObjectType = 7
DataType = 0x0007
AccessType = ro
DefaultValue = 0x00020002

[0x1001] ; Error Register
ParameterName = Error Register
ObjectType = 7
DataType = 0x0005
AccessType = ro
DefaultValue = 0x00

[0x2000] ; Example Manufacturer Specific Integer (RW)
ParameterName = My Integer
ObjectType = 7
DataType = 0x0007
AccessType = rw
DefaultValue = 123
""")

try:
    # Start with creating a network representing one CAN bus
    network = canopen.Network()

    # Connect to the CAN bus
    # The bustype and channel must match your installed python-can backend
    print(f"Connecting to CAN bus: bustype='{CAN_BUSTYPE}', channel='{CAN_CHANNEL}'")
    network.connect(bustype=CAN_BUSTYPE, channel=CAN_CHANNEL)
    print("Connected to CAN bus.")

    # Add a CANopen node with documentation from the EDS file
    # For a real device, replace NODE_ID and EDS_FILE with your device's info
    node = canopen.Node(NODE_ID, EDS_FILE)
    network.add_node(node)
    print(f"Added Node {NODE_ID} using {EDS_FILE}.")

    # Read a value from the object dictionary (e.g., Device Type 0x1000)
    device_type = node.sdo['Device Type'].raw
    print(f"Node {NODE_ID} Device Type (0x1000): {hex(device_type)}")

    # Write and read a manufacturer-specific object (e.g., 0x2000 subindex 0)
    # This assumes your EDS or device supports object 0x2000
    try:
        original_val = node.sdo[0x2000].raw
        print(f"Original value of 0x2000: {original_val}")

        new_val = 456
        node.sdo[0x2000].raw = new_val
        print(f"Wrote {new_val} to 0x2000.")

        read_val = node.sdo[0x2000].raw
        print(f"Read back value of 0x2000: {read_val}")
        assert read_val == new_val

    except canopen.sdo.SdoError as e:
        print(f"Could not access object 0x2000 on Node {NODE_ID}. This might be an EDS mismatch or device limitation: {e}")

    # For real-time data (PDOs), you'd typically start the network's processing loop
    # in a separate thread or call network.process() periodically.
    # For this quickstart, we'll just demonstrate it briefly.
    print("Starting network processing for 1 second...")
    network.nmt.state = 'OPERATIONAL' # Set node to operational for PDOs
    start_time = time.time()
    while time.time() - start_time < 1:
        network.process(0.01) # Process messages for 10ms
        time.sleep(0.01)
    print("Network processing finished.")

except canopen.CanError as e:
    print(f"CANopen Error: {e}")
    print("Please ensure your CAN hardware is connected and the python-can backend is correctly installed.")
    print(f"Tried bustype='{CAN_BUSTYPE}', channel='{CAN_CHANNEL}'")
except FileNotFoundError:
    print(f"Error: EDS file '{EDS_FILE}' not found. Please provide a valid EDS file for your device.")
except Exception as e:
    print(f"An unexpected error occurred: {e}")
finally:
    # Disconnect from CAN bus
    if 'network' in locals() and network.is_connected:
        print("Disconnecting from CAN bus.")
        network.disconnect()
    if os.path.exists(EDS_FILE) and 'Warning: ' in locals() and 'generic one' in locals(): # Clean up dummy EDS
        os.remove(EDS_FILE)
        print(f"Removed dummy EDS file: {EDS_FILE}")

view raw JSON →