### Install PySOEM Source: https://context7.com/bnjmnp/pysoem/llms.txt Install the PySOEM library using pip. ```bash pip install pysoem ``` -------------------------------- ### Configure EtherCAT Slaves with Callback Functions Source: https://context7.com/bnjmnp/pysoem/llms.txt Use custom Python functions to configure slaves via SDO during the PreOP to SafeOP transition. This example demonstrates configuring PDO mapping for a Beckhoff EL3002 analog input terminal. ```python import pysoem import struct BECKHOFF_VENDOR_ID = 0x0002 EL3002_PRODUCT_CODE = 0x0bba3052 def el3002_setup(slave_pos): """Configuration function called during PreOP to SafeOP transition.""" slave = master.slaves[slave_pos] # Disable default PDO mapping slave.sdo_write(0x1c12, 0, struct.pack('B', 0)) # Configure custom PDO mapping with complete access map_bytes = struct.pack('BxHH', 2, 0x1A01, 0x1A03) slave.sdo_write(0x1c13, 0, map_bytes, ca=True) master = pysoem.Master() master.open('eth0') if master.config_init() > 0: for i, slave in enumerate(master.slaves): # Verify slave identity if slave.man == BECKHOFF_VENDOR_ID and slave.id == EL3002_PRODUCT_CODE: # Assign configuration function slave.config_func = el3002_setup # config_map() calls each slave's config_func master.config_map() # Continue with state machine... master.close() ``` -------------------------------- ### Makefile for PySoem Project Management Source: https://github.com/bnjmnp/pysoem/blob/master/docs/developer_notes.rst This Makefile provides targets for common development tasks such as building, cleaning, installing locally or from various sources (GitHub, TestPyPI, PyPI), running tests, and executing tox environments. Ensure tabs are used for indentation. Targets requiring hardware need the IFACE variable specified. ```makefile include .env build: python -m pip install build python -m build clean: -rm src/pysoem/pysoem.c -rm src/pysoem/*.pyd -rm -rf src/pysoem.egg-info -rm -rf src/pysoem/__pycache__ -rm -rf src/__pycache__ -rm -rf tests/__pycache__ -rm -rf build -rm -rf dist -rm -rf .pytest_cache uninstall: python -m pip uninstall -y pysoem install_local: python -m pip install . install_github: python -m pip install git+https://github.com/bnjmnp/pysoem.git install_testpypi: python -m pip install -i https://test.pypi.org/simple/ pysoem install_pypi: python -m pip install pysoem test: python -m pip install pytest pytest tests --ifname=$(IFACE) tox_local: python -m pip install tox python -m tox run -r -c tests/tox_local.ini -- --ifname=$(IFACE) tox_test_pypi: python -m pip install tox python -m tox run -r -c tests/tox_test_pypi.ini -- --ifname=$(IFACE) tox_pypi: python -m pip install tox python -m tox run -r -c tests/tox_pypi.ini -- --ifname=$(IFACE) run_basic_example: python examples/basic_example.py $(IFACE) ``` -------------------------------- ### Read CoE Integer Object with `struct` Source: https://github.com/bnjmnp/pysoem/blob/master/docs/source/coe_objects.md Read a 32-bit unsigned integer CoE object and unpack it using `struct.unpack()` with the format character 'I'. Index the resulting tuple to get the integer value. ```python import struct ... vendor_id = struct.unpack('I', device.sdo_read(0x1018, 1))[0] ``` -------------------------------- ### Initialize EtherCAT network with PySOEM Source: https://github.com/bnjmnp/pysoem/blob/master/docs/source/index.md Initializes the master, opens the network adapter, and iterates through detected slave devices. ```python import pysoem master = pysoem.Master() master.open('Your network adapters ID') if master.config_init() > 0: for device in master.slaves: print(f'Found Device {device.name}') else: print('no device found') master.close() ``` -------------------------------- ### Create Master and Discover Slaves with PySOEM Source: https://context7.com/bnjmnp/pysoem/llms.txt Initializes an EtherCAT master, opens a network interface, and discovers connected slave devices. Ensure the correct adapter name is used. ```python import pysoem # Create master instance master = pysoem.Master() # Open network interface (use adapter name from find_adapters()) master.open('eth0') # Initialize and enumerate all slaves on the network # Returns the number of slaves found slave_count = master.config_init() if slave_count > 0: print(f'{slave_count} slaves found') # Access individual slaves through the slaves list for i, slave in enumerate(master.slaves): print(f'Slave {i}: {slave.name}') print(f' Vendor ID: {hex(slave.man)}') print(f' Product Code: {hex(slave.id)}') print(f' Revision: {hex(slave.rev)}') else: print('No slaves found') # Always close the master when done master.close() ``` -------------------------------- ### Initialize Master and Access Slaves Source: https://github.com/bnjmnp/pysoem/blob/master/docs/source/basics.md Connects to a network adapter and retrieves slave device instances from the master's slave list after configuration initialization. ```python import pysoem master = pysoem.Master() master.open('Your network adapters ID') if master.config_init() > 0: device_foo = master.slaves[0] device_bar = master.slaves[1] else: print('no device found') master.close() ``` -------------------------------- ### Find Network Adapters with PySOEM Source: https://context7.com/bnjmnp/pysoem/llms.txt Lists all available network adapters on the system that can be used for EtherCAT communication. Requires administrator privileges on Linux. ```python import pysoem # Find all available network adapters adapters = pysoem.find_adapters() for i, adapter in enumerate(adapters): print(f'Adapter {i}') print(f' Name: {adapter.name}') print(f' Description: {adapter.desc}') ``` -------------------------------- ### Run Tox Tests from PyPI Source: https://github.com/bnjmnp/pysoem/blob/master/docs/developer_notes.rst Use tox to test Pysoem downloaded from PyPI across different Python versions, using the specified tox configuration file. The IFACE variable must be provided. ```bash tox run -c tox_pypi.ini -- --ifname="" ``` -------------------------------- ### Configure Distributed Clocks (DC) Synchronization Source: https://context7.com/bnjmnp/pysoem/llms.txt Sets up DC synchronization for time-critical applications. Cycle times are specified in nanoseconds. ```python import pysoem master = pysoem.Master() master.open('eth0') if master.config_init() > 0: master.config_map() # Configure distributed clocks master.config_dc() if master.state_check(pysoem.SAFEOP_STATE, 50000) == pysoem.SAFEOP_STATE: # Enable DC SYNC0 for a slave # sync0_cycle_time is in nanoseconds (10,000,000 ns = 10 ms) slave = master.slaves[0] slave.dc_sync( act=True, # Activate SYNC sync0_cycle_time=10000000, # 10ms cycle time sync0_shift_time=0 # Optional shift time ) # For SYNC0 and SYNC1: # slave.dc_sync( # act=True, # sync0_cycle_time=10000000, # sync0_shift_time=0, # sync1_cycle_time=5000000 # SYNC1 fires 5ms after SYNC0 # ) master.state = pysoem.OP_STATE master.write_state() # In the cyclic loop, use dc_time for synchronization # dc_time = master.dc_time # Returns DC time in ns master.close() ``` -------------------------------- ### Configure Watchdog Timers Source: https://context7.com/bnjmnp/pysoem/llms.txt Sets and retrieves PDI and process data watchdog timers for EtherCAT slaves. ```python import pysoem master = pysoem.Master() master.open('eth0') if master.config_init() > 0: slave = master.slaves[0] # Get maximum watchdog time for this slave max_wd_time = slave.get_max_watchdog_time() print(f'Maximum watchdog time: {max_wd_time} ms') # Set process data watchdog (triggers if no process data received) slave.set_watchdog('processdata', 100.0) # 100 ms # Set PDI watchdog slave.set_watchdog('pdi', 100.0) # 100 ms # Read current watchdog values pd_wd = slave.get_watchdog('processdata') pdi_wd = slave.get_watchdog('pdi') print(f'Process data watchdog: {pd_wd} ms') print(f'PDI watchdog: {pdi_wd} ms') master.close() ``` -------------------------------- ### Run Tox Tests from TestPyPI Source: https://github.com/bnjmnp/pysoem/blob/master/docs/developer_notes.rst Use tox to test Pysoem downloaded from TestPyPI across different Python versions, using the specified tox configuration file. The IFACE variable must be provided. ```bash tox run -c tox_test_pypi.ini -- --ifname="" ``` -------------------------------- ### Environment Variable for Hardware Interface Source: https://github.com/bnjmnp/pysoem/blob/master/docs/developer_notes.rst This snippet shows how to define the IFACE environment variable in an .env file, which is imported by the Makefile. This variable is crucial for targets that require hardware interaction. ```makefile IFACE= ``` -------------------------------- ### Run Tox Tests Locally Source: https://github.com/bnjmnp/pysoem/blob/master/docs/developer_notes.rst Use tox to run Pysoem tests locally across different Python versions, using the specified local tox configuration file. The IFACE variable must be provided. ```bash tox run -c tox_local.ini -- --ifname="" ``` -------------------------------- ### Update Slave Firmware via FoE Source: https://context7.com/bnjmnp/pysoem/llms.txt Updates slave firmware using File over EtherCAT (FoE) in BOOT state. Requires manual mailbox configuration based on EEPROM settings. ```python import pysoem import struct def update_firmware(ifname, slave_position, firmware_path): master = pysoem.Master() master.open(ifname) num_slaves = master.config_init() if num_slaves == 0: raise Exception('No slaves found') if slave_position > num_slaves: raise Exception(f'Slave position {slave_position} not available') device = master.slaves[slave_position - 1] # Request INIT state device.state = pysoem.INIT_STATE device.write_state() device.state_check(pysoem.INIT_STATE, 3000000) if device.state != pysoem.INIT_STATE: raise Exception('Device did not enter INIT state') # Read BOOT mailbox configuration from EEPROM boot_rx_mbx = device.eeprom_read(pysoem.SiiOffset.BOOT_RX_MBX) rx_addr, rx_len = struct.unpack('HH', boot_rx_mbx) boot_tx_mbx = device.eeprom_read(pysoem.SiiOffset.BOOT_TX_MBX) tx_addr, tx_len = struct.unpack('HH', boot_tx_mbx) # Configure mailbox for BOOT state device.amend_mbx(mailbox='out', start_address=rx_addr, size=rx_len) device.amend_mbx(mailbox='in', start_address=tx_addr, size=tx_len) # Request BOOT state device.state = pysoem.BOOT_STATE device.write_state() device.state_check(pysoem.BOOT_STATE, 3000000) if device.state != pysoem.BOOT_STATE: raise Exception('Device did not enter BOOT state') # Upload firmware via FoE with open(firmware_path, 'rb') as f: firmware_data = f.read() device.foe_write( filename=firmware_path.split('/')[-1], password=0, data=firmware_data, timeout=60000000 # 60 seconds for large files ) print('Firmware update complete') # Return to INIT state device.state = pysoem.INIT_STATE device.write_state() master.close() # Usage: # update_firmware('eth0', 1, '/path/to/firmware.bin') ``` -------------------------------- ### Manage EtherCAT State Machine Transitions Source: https://context7.com/bnjmnp/pysoem/llms.txt Control EtherCAT slave states from INIT to OP and back. Ensure all slaves reach the desired state before proceeding. Handles potential state transition failures. ```python import pysoem master = pysoem.Master() master.open('eth0') if master.config_init() > 0: # Map PDOs (Process Data Objects) master.config_map() # Check if all slaves reached SAFEOP state (timeout in microseconds) if master.state_check(pysoem.SAFEOP_STATE, 50000) != pysoem.SAFEOP_STATE: master.read_state() for slave in master.slaves: if slave.state != pysoem.SAFEOP_STATE: print(f'{slave.name} did not reach SAFEOP state') print(f'AL Status: {hex(slave.al_status)} - {pysoem.al_status_code_to_string(slave.al_status)}') raise Exception('Not all slaves reached SAFEOP state') # Request OP state for all slaves master.state = pysoem.OP_STATE master.write_state() # Wait for OP state master.state_check(pysoem.OP_STATE, 50000) if master.state == pysoem.OP_STATE: print('All slaves in OP state') # ... perform operations ... # Return to INIT state when done master.state = pysoem.INIT_STATE master.write_state() master.close() ``` -------------------------------- ### Handle EtherCAT Exceptions Source: https://context7.com/bnjmnp/pysoem/llms.txt Demonstrates catching specific pysoem exceptions such as SdoError, WkcError, and MailboxError during master operations. ```python import pysoem master = pysoem.Master() try: master.open('eth0') except ConnectionError as e: print(f'Failed to open interface: {e}') exit(1) if master.config_init() > 0: slave = master.slaves[0] # SDO Error handling try: data = slave.sdo_read(0xFFFF, 0) # Invalid index except pysoem.SdoError as e: print(f'SDO Error on slave {e.slave_pos}:') print(f' Index: {hex(e.index)}, Subindex: {e.subindex}') print(f' Abort Code: {hex(e.abort_code)}') print(f' Description: {e.desc}') # Working counter error try: slave.sdo_write(0x1234, 0, b'\x00') except pysoem.WkcError as e: print(f'Working counter error: {e.wkc}') # Mailbox error try: data = slave.sdo_read(0x1008, 0) except pysoem.MailboxError as e: print(f'Mailbox error on slave {e.slave_pos}:') print(f' Error Code: {e.error_code}') print(f' Description: {e.desc}') # FoE error try: slave.foe_read('nonexistent.bin', 0, 1024) except pysoem.FoeError as e: print(f'FoE error on slave {e.slave_pos}:') print(f' Error Code: {hex(e.error_code)}') print(f' Description: {e.desc}') # EEPROM error try: slave.eeprom_write(0x0000, b'\x00\x00') except pysoem.EepromError as e: print(f'EEPROM error: {e.message}') # SDO Info error try: od = slave.od except pysoem.SdoInfoError as e: print(f'SDO Info error: {e.message}') # Config map error (collects multiple errors) try: master.config_map() except pysoem.ConfigMapError as e: print('Config map errors:') for err in e.error_list: print(f' {type(err).__name__}: {err}') master.close() ``` -------------------------------- ### Read and Write Slave EEPROM Data Source: https://context7.com/bnjmnp/pysoem/llms.txt Demonstrates reading and writing data to a slave's EEPROM. This is typically used for initial configuration or retrieving identification information. Use caution when writing to EEPROM as incorrect data can affect slave operation. Reads are performed in 4-byte chunks (word-addressed). ```python import pysoem master = pysoem.Master() master.open('eth0') if master.config_init() > 0: slave = master.slaves[0] # Read EEPROM (4 bytes at a time, word-addressed) # Read first 128 bytes (0x00 to 0x7F in word addresses) print('EEPROM contents:') for addr in range(0, 0x40, 2): # Word addresses data = slave.eeprom_read(addr) hex_str = '|'.join(f'{b:02x}' for b in data) print(f'{addr:04x}: {hex_str}') # Read specific SII (Slave Information Interface) fields vendor_id = slave.eeprom_read(pysoem.SiiOffset.MAN) product_code = slave.eeprom_read(pysoem.SiiOffset.ID) revision = slave.eeprom_read(pysoem.SiiOffset.REV) print(f'Vendor ID: {vendor_id.hex()}') print(f'Product Code: {product_code.hex()}') print(f'Revision: {revision.hex()}') # Write EEPROM (2 bytes/1 word at a time, use with caution!) # slave.eeprom_write(word_address, bytes([0x12, 0x34])) master.close() ``` -------------------------------- ### Configure GIL Release and Process Data Threading Source: https://context7.com/bnjmnp/pysoem/llms.txt Sets global or per-master settings to release the GIL during EtherCAT operations, allowing concurrent processing in separate threads. ```python # Global setting: release GIL for all operations pysoem.settings.always_release_gil = True master = pysoem.Master() master.open('eth0') # Per-master setting master.always_release_gil = True if master.config_init() > 0: master.config_map() if master.state_check(pysoem.SAFEOP_STATE, 50000) == pysoem.SAFEOP_STATE: master.state = pysoem.OP_STATE master.write_state() master.state_check(pysoem.OP_STATE, 50000) def process_data_thread(): while running: # GIL is released during these calls master.send_processdata(release_gil=True) master.receive_processdata(2000, release_gil=True) time.sleep(0.001) running = True thread = threading.Thread(target=process_data_thread) thread.start() # Main thread can do other work while process data runs time.sleep(5) running = False thread.join() master.close() ``` -------------------------------- ### Configure SOEM Timeouts Source: https://context7.com/bnjmnp/pysoem/llms.txt Defines global and per-master timeout values in microseconds for various EtherCAT operations. ```python import pysoem # Configure global timeouts (in microseconds) pysoem.settings.timeouts.ret = 2000 # Return timeout pysoem.settings.timeouts.safe = 20000 # Safe timeout pysoem.settings.timeouts.eeprom = 20000 # EEPROM timeout pysoem.settings.timeouts.tx_mailbox = 20000 # TX mailbox timeout pysoem.settings.timeouts.rx_mailbox = 700000 # RX mailbox timeout pysoem.settings.timeouts.state = 2000000 # State change timeout master = pysoem.Master() # Configure per-master SDO timeouts (in microseconds) master.sdo_read_timeout = 1000000 # 1 second master.sdo_write_timeout = 1000000 # 1 second master.open('eth0') # ... operations with custom timeouts ... master.close() ``` -------------------------------- ### File over EtherCAT (FoE) Upload and Download Source: https://context7.com/bnjmnp/pysoem/llms.txt Handles file transfers to and from EtherCAT slaves using the File over EtherCAT (FoE) protocol. This is commonly used for firmware updates or transferring configuration files. Ensure the slave supports FoE and provide the correct filename and password if required. A timeout is recommended for large file transfers. ```python import pysoem master = pysoem.Master() master.open('eth0') if master.config_init() > 0: slave = master.slaves[0] # FoE Write - upload file to slave with open('firmware.bin', 'rb') as f: file_data = f.read() try: slave.foe_write( filename='firmware.bin', password=0, # FoE password (device specific) data=file_data, timeout=6000000 # 6 second timeout for large files ) print('File uploaded successfully') except pysoem.FoeError as e: print(f'FoE write error: {e}') # FoE Read - download file from slave try: data = slave.foe_read( filename='config.bin', password=0, size=1024, # Maximum file size to read timeout=200000 ) print(f'Read {len(data)} bytes') except pysoem.FoeError as e: print(f'FoE read error: {e}') master.close() ``` -------------------------------- ### Run Pytest with Hardware Interface Source: https://github.com/bnjmnp/pysoem/blob/master/docs/developer_notes.rst Execute Pytest tests for the Pysoem project, specifying the network interface ID. This command is used for local testing with the currently active Python distribution. ```bash python -m pytest --ifname="" ``` -------------------------------- ### Implement Slave Recovery and Reconfiguration Source: https://context7.com/bnjmnp/pysoem/llms.txt Monitors slave states during process data exchange and attempts to recover lost or error-state slaves. Requires an active master instance and a cyclic loop for state checking. ```python import pysoem import time master = pysoem.Master() master.open('eth0') if master.config_init() > 0: # Mark slaves as not lost for slave in master.slaves: slave.is_lost = False master.config_map() master.state = pysoem.OP_STATE master.write_state() master.state_check(pysoem.OP_STATE, 50000) def check_slaves(): master.read_state() for i, slave in enumerate(master.slaves): if slave.state == (pysoem.SAFEOP_STATE + pysoem.STATE_ERROR): print(f'Slave {i} in SAFEOP + ERROR, attempting ACK') slave.state = pysoem.SAFEOP_STATE + pysoem.STATE_ACK slave.write_state() elif slave.state == pysoem.SAFEOP_STATE: print(f'Slave {i} in SAFEOP, requesting OP') slave.state = pysoem.OP_STATE slave.write_state() elif slave.state > pysoem.NONE_STATE: if slave.reconfig(timeout=500): slave.is_lost = False print(f'Slave {i} reconfigured') elif not slave.is_lost: slave.state_check(pysoem.OP_STATE) if slave.state == pysoem.NONE_STATE: slave.is_lost = True print(f'Slave {i} lost') if slave.is_lost: if slave.state == pysoem.NONE_STATE: if slave.recover(timeout=500): slave.is_lost = False print(f'Slave {i} recovered') else: slave.is_lost = False print(f'Slave {i} found') try: while True: master.send_processdata() wkc = master.receive_processdata(2000) if wkc < master.expected_wkc: check_slaves() time.sleep(0.01) except KeyboardInterrupt: pass master.state = pysoem.INIT_STATE master.write_state() master.close() ``` -------------------------------- ### Read CoE Integer Object with `int.from_bytes` Source: https://github.com/bnjmnp/pysoem/blob/master/docs/source/coe_objects.md Read a 32-bit unsigned integer CoE object using `sdo_read()` and convert the resulting bytes to a Python `int` with `int.from_bytes()`. Specify `byteorder` and `signed` parameters. ```python vendor_id = int.from_bytes(device.sdo_read(0x1018, 1), byteorder='little', signed=False) ``` -------------------------------- ### Access Slave Device Names Source: https://github.com/bnjmnp/pysoem/blob/master/docs/source/basics.md Retrieves the name property from a previously initialized slave device instance. ```python print(device_foo.name) print(device_bar.name) ``` -------------------------------- ### Use Context Manager for PySOEM Master Source: https://context7.com/bnjmnp/pysoem/llms.txt Utilizes a context manager for automatic resource cleanup when working with the EtherCAT master. This ensures master.close() is called implicitly. ```python import pysoem # Using context manager for automatic cleanup with pysoem.open('eth0') as master: if master.config_init() > 0: for slave in master.slaves: print(f'Found slave: {slave.name}') # master.close() is called automatically ``` -------------------------------- ### Write CoE Integer Object with `int.to_bytes` Source: https://github.com/bnjmnp/pysoem/blob/master/docs/source/coe_objects.md Convert a Python `int` to bytes using `int.to_bytes()` for writing to a 32-bit unsigned integer CoE object with `sdo_write()`. The number of bytes must be specified. ```python device.sdo_write(0x3456, 0, (1).to_bytes(4, byteorder='little', signed=False)) ``` -------------------------------- ### Read SDO Object Dictionary from Slave Source: https://context7.com/bnjmnp/pysoem/llms.txt Reads the complete object dictionary from an EtherCAT slave that supports SDO info. This is useful for understanding the slave's capabilities and available parameters. Ensure the master is initialized and connected to the network. ```python import pysoem master = pysoem.Master() master.open('eth0') if master.config_init() > 0: for slave in master.slaves: try: # Get object dictionary od = slave.od print(f'\n{slave.name} Object Dictionary:') for obj in od: print(f' Index: {hex(obj.index)}') print(f' Object Code: {obj.object_code}') print(f' Data Type: {obj.data_type}') print(f' Bit Length: {obj.bit_length}') print(f' Access: {hex(obj.obj_access)}') print(f' Name: {obj.name}') # Read object entries (subindices) for i, entry in enumerate(obj.entries): if entry.data_type > 0 and entry.bit_length > 0: print(f' [{i}] Type: {entry.data_type}, ' f'Bits: {entry.bit_length}, ' f'Access: {hex(entry.obj_access)}, ' f'Name: {entry.name}') except pysoem.SdoInfoError: print(f'{slave.name}: SDO Info not supported') master.close() ``` -------------------------------- ### Encode and Write CoE String Object Source: https://github.com/bnjmnp/pysoem/blob/master/docs/source/coe_objects.md Encode a Python string to bytes using `.encode()` before writing it to a CoE string object with `sdo_write()`. Specify the desired encoding. ```python device.sdo_write(0x2345, 0, 'hello world'.encode('ascii')) ``` -------------------------------- ### SDO Write Operations with PySOEM Source: https://context7.com/bnjmnp/pysoem/llms.txt Writes data to CoE objects on slave devices. Data must be provided as bytes and can be formatted using standard Python modules like struct and ctypes. Supports writing various data types and complete access for complex objects. ```python import pysoem import struct import ctypes master = pysoem.Master() master.open('eth0') if master.config_init() > 0: slave = master.slaves[0] # Write string data slave.sdo_write(0x2345, 0, 'hello world'.encode('ascii')) # Write 32-bit unsigned integer using int.to_bytes slave.sdo_write(0x3456, 0, (1).to_bytes(4, byteorder='little', signed=False)) # Write using struct.pack slave.sdo_write(0x3456, 0, struct.pack('I', 42)) # Write using ctypes slave.sdo_write(0x3456, 0, bytes(ctypes.c_uint32(100))) # Write 8-bit value slave.sdo_write(0x8001, 2, struct.pack('B', 1)) # Write with complete access (ca=True) for array/record objects rx_map_obj = [0x1603, 0x1607, 0x160B, 0x160F] rx_map_bytes = struct.pack('Bx' + 'H' * len(rx_map_obj), len(rx_map_obj), *rx_map_obj) slave.sdo_write(0x1C12, 0, rx_map_bytes, ca=True) master.close() ``` -------------------------------- ### SDO Read Operations with PySOEM Source: https://context7.com/bnjmnp/pysoem/llms.txt Reads CoE objects from slave devices using various methods for data conversion. Data is initially returned as raw bytes. Ensure the master is initialized and slaves are discovered before performing SDO operations. ```python import pysoem import struct import ctypes master = pysoem.Master() master.open('eth0') if master.config_init() > 0: slave = master.slaves[0] # Read string object (Device Name at 0x1008:00) device_name = slave.sdo_read(0x1008, 0).decode('utf-8') print(f'Device Name: {device_name}') # Read 32-bit unsigned integer (Vendor ID at 0x1018:01) # Method 1: Using int.from_bytes vendor_id_bytes = slave.sdo_read(0x1018, 1) vendor_id = int.from_bytes(vendor_id_bytes, byteorder='little', signed=False) print(f'Vendor ID: {hex(vendor_id)}') # Method 2: Using struct module product_code = struct.unpack('I', slave.sdo_read(0x1018, 2))[0] print(f'Product Code: {hex(product_code)}') # Method 3: Using ctypes revision = ctypes.c_uint32.from_buffer_copy(slave.sdo_read(0x1018, 3)).value print(f'Revision: {hex(revision)}') # Read with custom timeout (default is 700000 us) master.sdo_read_timeout = 1000000 # 1 second data = slave.sdo_read(0x1018, 1) master.close() ``` -------------------------------- ### Write CoE Integer Object with `struct` Source: https://github.com/bnjmnp/pysoem/blob/master/docs/source/coe_objects.md Pack a Python integer into bytes using `struct.pack()` with the format character 'I' for writing to a 32-bit unsigned integer CoE object. ```python device.sdo_write(0x3456, 0, struct.pack('I', 1)) ``` -------------------------------- ### Read CoE Integer Object with `ctypes` Source: https://github.com/bnjmnp/pysoem/blob/master/docs/source/coe_objects.md Read a 32-bit unsigned integer CoE object and convert it to a `ctypes.c_uint32` using `from_buffer_copy()`. Access the value using `.value`. ```python import ctypes ... vendor_id = ctypes.c_uint32.from_buffer_copy(device.sdo_read(0x1018, 1)).value ``` -------------------------------- ### Write CoE Integer Object with `ctypes` Source: https://github.com/bnjmnp/pysoem/blob/master/docs/source/coe_objects.md Write a `ctypes.c_uint32` value to a CoE object by converting it to bytes using `bytes()`. This method is often more concise than `int.to_bytes()`. ```python device.sdo_write(0x3456, 0, bytes(ctypes.c_uint32(1))) ``` -------------------------------- ### Release GIL for Multi-threading Source: https://context7.com/bnjmnp/pysoem/llms.txt Imports necessary modules to release the Python Global Interpreter Lock (GIL) during blocking operations. ```python import pysoem import threading import time ``` -------------------------------- ### Exchange Process Data with EtherCAT Slaves Source: https://context7.com/bnjmnp/pysoem/llms.txt Send and receive cyclic process data to/from slaves while in OP state. Includes error checking for working counter and reading/writing slave input/output data. Requires slaves to be in OP state. ```python import pysoem import struct import time master = pysoem.Master() master.open('eth0') if master.config_init() > 0: master.config_map() if master.state_check(pysoem.SAFEOP_STATE, 50000) == pysoem.SAFEOP_STATE: master.state = pysoem.OP_STATE master.write_state() master.state_check(pysoem.OP_STATE, 50000) if master.state == pysoem.OP_STATE: try: while True: # Send process data to slaves master.send_processdata() # Receive process data from slaves (timeout in us) wkc = master.receive_processdata(2000) # Check working counter if wkc != master.expected_wkc: print(f'Working counter mismatch: {wkc} != {master.expected_wkc}') # Read input data from slave (e.g., analog input) input_bytes = master.slaves[0].input if len(input_bytes) >= 2: value = struct.unpack('h', input_bytes[:2])[0] print(f'Input value: {value}') # Write output data to slave output_len = len(master.slaves[0].output) output_data = bytearray(output_len) output_data[0] = 0x01 # Set first byte master.slaves[0].output = bytes(output_data) time.sleep(0.01) # 10ms cycle except KeyboardInterrupt: print('Stopped') master.state = pysoem.INIT_STATE master.write_state() master.close() ``` -------------------------------- ### Register Emergency Message Callbacks Source: https://context7.com/bnjmnp/pysoem/llms.txt Registers a callback function to handle emergency (EMCY) messages received from slaves during operation. ```python import pysoem def emergency_callback(emcy): """Called when an emergency message is received.""" print(f'Emergency from slave {emcy.slave_pos}:') print(f' Error Code: {emcy.error_code:04x}') print(f' Error Register: {emcy.error_reg:02x}') print(f' Data: b1={emcy.b1}, w1={emcy.w1}, w2={emcy.w2}') master = pysoem.Master() master.open('eth0') if master.config_init() > 0: # Register emergency callback for each slave for slave in master.slaves: slave.add_emergency_callback(emergency_callback) master.config_map() # ... normal operation ... # Emergency messages will trigger the callback during # sdo_read, sdo_write, or mbx_receive operations master.close() ``` -------------------------------- ### Decode CoE String Object Source: https://github.com/bnjmnp/pysoem/blob/master/docs/source/coe_objects.md Use `bytes.decode()` to convert a CoE string object read via `sdo_read()` to a Python 3 string. Ensure the correct encoding is used. ```python device_name = device.sdo_read(0x1008, 0).decode('utf-8') ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.