Hi all,
We do have an experimental project where we need control a DMX control with an S7-1200 industrial PLC. We have chosen ETC GADGET II (CPU 1) as it has library that can be used in python. We have developed the code that can be seen found in attachments. However, the sending and receiving data are inconsistent. I am not receiving the breakDMX signal for each received data on the refresh rate which I set 0.04s (25 FPS). So, is there any application examples that has used ETClab/gadget library either in python or C+ that we can refer to solve the issue ? Thank you so much for your helps in advance.
Note: I looped back ports to test the application. So, port 1 connected to port 2 to recieve data that I sent on port 1.
import ctypes import os import sys import time import random from opcua import Client, ua import threading # Adjustable variables Sending_Port = 1 # Port to send DMX values (fixed, adjustable) Receiving_Port = 2 # Port to receive DMX data (fixed, adjustable) RX_DMX_START_CH = 1 # Starting DMX channel for receive (adjustable) RX_COUNT = 5 # Number of channels to receive (adjustable, e.g., 1 to 5) RX_PLC_START_IDX = 100 # Starting PLC index for receive data (adjustable) TX_PLC_START_IDX = 100 # Starting PLC index for transmit data (adjustable) TX_COUNT = 5 # Number of channels to transmit (adjustable, e.g., 1 to 5) TX_DMX_START_CH = 1 # Starting DMX channel for transmit (adjustable) TX_RATE_S = 0.04 # DMX update rate (~25 FPS) ALIVE_WINDOW_S = 1.0 # Alive tag toggle window (seconds) # Specify the directory and DLL path G2_DIR = r"C:\libGadget\x64\bin" G2_DLL = os.path.join(G2_DIR, "GadgetDLL.dll") # OPC UA configuration PLC_OPC_URL = "opc.tcp://10.0.11.2:4840" DMX_IN_TAG = 'ns=4;i=12' # BYTE[512] from DMX -> PLC (mirror 1..5 for testing) DMX_OUT_TAG = 'ns=4;i=535' # BYTE[512] from PLC -> DMX (send 1..5 for testing) RX_ALIVE_TAG = 'ns=4;i=1058' # BOOL TX_ALIVE_TAG = 'ns=4;i=1069' # BOOL # Add DLL directory to the search path (Python 3.8+) if hasattr(os, "add_dll_directory"): os.add_dll_directory(G2_DIR) # Load the DLL g = ctypes.CDLL(G2_DLL) # Define C types c_uint = ctypes.c_uint c_char_p = ctypes.c_char_p c_bool = ctypes.c_bool c_ubyte = ctypes.c_ubyte c_short = ctypes.c_short # For raw data (unsigned short *) c_int = ctypes.c_int # For SetRawReceiveMode return type # Define function signatures based on GadgetDLL.h GetDllVersion = g.Gadget2_GetDllVersion GetDllVersion.restype = c_char_p Connect = g.Gadget2_Connect Connect.restype = c_bool Disconnect = g.Gadget2_Disconnect GetNumDevs = g.Gadget2_GetNumGadgetDevices GetNumDevs.restype = c_uint GetPorts = g.Gadget2_GetPortCount GetPorts.argtypes = [c_uint] GetPorts.restype = c_ubyte GetType = g.Gadget2_GetGadgetType GetType.argtypes = [c_uint] GetType.restype = c_char_p GetVersion = g.Gadget2_GetGadgetVersion GetVersion.argtypes = [c_uint] GetVersion.restype = c_char_p GetSerial = g.Gadget2_GetGadgetSerialNumber GetSerial.argtypes = [c_uint] GetSerial.restype = ctypes.c_uint32 SendDMX = g.Gadget2_SendDMX SendDMX.argtypes = [c_uint, c_uint, ctypes.POINTER(c_ubyte), c_uint] # DeviceNum, PortNum, Buffer, Size SetRawReceiveMode = g.Gadget2_SetRawReceiveMode SetRawReceiveMode.argtypes = [c_uint, c_uint] SetRawReceiveMode.restype = c_int # Returns 0 on success, -1 on failure GetNumberOfRXRawBytes = g.Gadget2_GetNumberOfRXRawBytes GetNumberOfRXRawBytes.argtypes = [c_uint, c_uint] GetNumberOfRXRawBytes.restype = c_uint GetRXRawBytes = g.Gadget2_GetRXRawBytes GetRXRawBytes.argtypes = [c_uint, c_uint, ctypes.POINTER(c_short), c_uint] # DeviceNum, PortNum, Data, Length # Global variables for OPC UA opc_client = None dmx_in_data = (ctypes.c_ubyte * 512)() # 512-byte array for received DMX dmx_out_data = (ctypes.c_ubyte * 512)() # 512-byte array for sent DMX rx_alive = False tx_alive = False def opcua_update(): global opc_client, dmx_in_data, dmx_out_data, rx_alive, tx_alive while True: try: if opc_client is None: opc_client = Client(PLC_OPC_URL) opc_client.connect() print("Connected to OPC UA server at", PLC_OPC_URL) # Read DMX_OUT data from PLC dmx_out_node = opc_client.get_node(DMX_OUT_TAG) plc_data = dmx_out_node.get_value() if isinstance(plc_data, list) and len(plc_data) >= TX_PLC_START_IDX + TX_COUNT: for i in range(TX_COUNT): dmx_out_data[TX_DMX_START_CH - 1 + i] = plc_data[TX_PLC_START_IDX - 1 + i] tx_alive = not tx_alive # Toggle TX alive opc_client.get_node(TX_ALIVE_TAG).set_value(ua.Variant(tx_alive, ua.VariantType.Boolean)) # Write DMX_IN data to PLC (simplified write) dmx_in_node = opc_client.get_node(DMX_IN_TAG) rx_data = list(dmx_in_data) if len(rx_data) >= RX_PLC_START_IDX + RX_COUNT: plc_in_data = [rx_data[i] for i in range(RX_PLC_START_IDX - 1, RX_PLC_START_IDX - 1 + RX_COUNT)] dmx_in_node.set_value(plc_in_data) # Simplified write without status/timestamps rx_alive = not rx_alive # Toggle RX alive opc_client.get_node(RX_ALIVE_TAG).set_value(ua.Variant(rx_alive, ua.VariantType.Boolean)) except Exception as e: print(f"OPC UA error: {e}") if opc_client is not None: opc_client.disconnect() opc_client = None time.sleep(ALIVE_WINDOW_S) # Print DLL information print("DLL:", G2_DLL) print("DLL ver:", (GetDllVersion() or b'').decode()) # Initialize the Gadget2 interface if not Connect(): sys.exit("Connect False") try: # Wait up to 5 seconds for devices to be detected t = time.time() while time.time() - t < 5 and GetNumDevs() == 0: time.sleep(0.1) print("Waiting for device detection...") # Get the number of Gadget devices n = int(GetNumDevs()) print("devices:", n) if n: ports = int(GetPorts(0)) print("ports:", ports) print("type:", (GetType(0) or b'').decode()) print("fw :", (GetVersion(0) or b'').decode()) print("sn :", int(GetSerial(0))) # Set receiving port to raw receive mode if SetRawReceiveMode(0, Receiving_Port) == 0: print(f"Port {Receiving_Port} set to raw receive mode successfully.") else: print(f"Failed to set port {Receiving_Port} to raw receive mode.") print(f"Note: Ensure {Receiving_Port} is valid and a DMX loopback cable is used on {Sending_Port} for receive testing.") # Start OPC UA update thread opc_thread = threading.Thread(target=opcua_update, daemon=True) opc_thread.start() # Start sending DMX values from PLC and reading to PLC print(f"Starting DMX bridge: Sending to port {Sending_Port}, receiving from port {Receiving_Port}. Press Ctrl+C to stop...") while True: # Send DMX data from PLC (TX) SendDMX(0, Sending_Port, dmx_out_data, 512) # Print sent values for the specified range tx_range = [dmx_out_data[TX_DMX_START_CH - 1 + i] for i in range(TX_COUNT)] print(f"Sent DMX values to device 0, port {Sending_Port} (channels {TX_DMX_START_CH} to {TX_DMX_START_CH + TX_COUNT - 1}): {tx_range}") # Attempt to read raw data from the receiving port num_bytes = GetNumberOfRXRawBytes(0, Receiving_Port) print(f"Number of raw bytes available on port {Receiving_Port}: {num_bytes}") if num_bytes > 0: raw_data = (c_short * num_bytes)() GetRXRawBytes(0, Receiving_Port, raw_data, num_bytes) # Print raw data sample for debugging print(f"Raw data sample from port {Receiving_Port}: {list(raw_data[:10])}...") # Accumulate data until 512 bytes, resetting on break temp_buffer = [] for i in range(num_bytes): value = raw_data[i] if value == 0x8000: # Reset on break temp_buffer = [] elif value != 0x9000: # Skip framing errors temp_buffer.append(value & 0xFF) # Add data byte if len(temp_buffer) >= 512: break # Pad or truncate to 512 while len(temp_buffer) < 512: temp_buffer.append(0) temp_buffer = temp_buffer[:512] # Update dmx_in_data with the full 512-byte frame for i in range(512): dmx_in_data[i] = temp_buffer[i] # Map received data to specific receive range rx_range = [dmx_in_data[i] for i in range(RX_DMX_START_CH - 1, RX_DMX_START_CH - 1 + RX_COUNT)] print(f"Received DMX data from port {Receiving_Port} (channels {RX_DMX_START_CH} to {RX_DMX_START_CH + RX_COUNT - 1}): {rx_range}") else: print(f"No raw data received on port {Receiving_Port}. Use a DMX loopback cable on port {Sending_Port} if needed.") # Sleep for the specified rate time.sleep(TX_RATE_S) else: print("No devices detected. Check connections and drivers.") input("Press Enter to exit…") except KeyboardInterrupt: print("\nStopping DMX transmission.") except Exception as e: print(f"Error during transmission: {e}") finally: try: Disconnect() except: pass if opc_client is not None: opc_client.disconnect() print("Gadget2 interface disconnected.")
Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 448 Raw data sample from port 2: [-32768, 0, 0, 0, 21, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 21] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 298 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 306 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 384 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 402 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 306 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 384 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 306 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 298 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 466 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 320 Raw data sample from port 2: [-32768, 0, 0, 0, 21, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 21] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 306 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 384 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 402 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 306 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 338 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 344 Raw data sample from port 2: [-32768, 0, 0, 0, 21, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 21] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 402 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 306 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 384 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 306 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 314 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 376 Raw data sample from port 2: [-32768, 0, 0, 0, 21, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 21] Requested session timeout to be 3600000ms, got 60000ms instead Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 394 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 306 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Connected to OPC UA server at opc.tcp://10.0.11.2:4840 Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 384 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 306 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 402 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] OPC UA error: "The server does not support writing the combination of value, status and timestamps provided."(BadWriteNotSupported) Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 384 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 306 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 298 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 384 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 402 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 306 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 378 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 312 Raw data sample from port 2: [-32768, 0, 0, 0, 21, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 21] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 306 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 410 Raw data sample from port 2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 0] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 368 Raw data sample from port 2: [-32768, 0, 0, 0, 21, 0, 0, 0, 0, 0]... Received DMX data from port 2 (channels 1 to 5): [0, 0, 0, 0, 21] Sent DMX values to device 0, port 1 (channels 1 to 5): [0, 0, 21, 0, 0] Number of raw bytes available on port 2: 306