Yes, managed to work out integration with the PQI data today. Revised code that integrates this functionality providerd here:
FUNCTION_BLOCK PUBLIC FB_IOL_Comms
(* Authors : MWP, JV
Last Edit : 30 May 2025 (JV)
This FB manages communications with IO-Link devices via an IO-Link master
*)
VAR_INPUT
port_number : DWORD := 1; // IO-Link master physical port number (e.g. 1-8)
io_index : WORD := 19; // Index (default to product id)
io_subindex : BYTE := 0;
data_write : ARRAY[0..60] OF BYTE; // Data to write to IO-Link slave
io_write_length : WORD; // Byte length of data to write to slave
t_timeout : TIME := T#100S;
END_VAR
VAR_IN_OUT
x_read_cmd : BOOL;
x_write_cmd : BOOL;
x_port_query : BOOL;
END_VAR
VAR_OUTPUT
x_done : BOOL;
x_busy : BOOL;
x_error : BOOL;
s_error : STRING;
data_read : ARRAY[0..63] OF BYTE;
io_read_length : UDINT;
END_VAR
VAR
EIP : ENIP.Generic_Service;
EIP_trigger : BOOL;
EIP_done : BOOL;
EIP_busy : BOOL;
EIP_err : BOOL;
EIP_class : ENIP.CIPClass;
EIP_instance : DWORD;
EIP_attribute : WORD;
EIP_eService : ENIP.CIPCommonService;
EIP_eError : ENIP.ERROR;
EIP_write_data : ARRAY[0..63] OF BYTE; // Write buffer
EIP_write_data_size : UDINT;
EIP_read_data : ARRAY[0..63] OF BYTE; // Read buffer
EIP_read_data_size : UDINT;
EIP_rcvd_size : UDINT;
x_read : BOOL;
x_write : BOOL;
x_query : BOOL;
i_step : INT := 0;
tonWatchDog : TON;
(*========================================================
LIST OF RELATED VARIABLES DESCRIBED BY DEFINITION OF IO-LINK MASTER
========================================================
EIP_DI_status: DIGITAL INPUT STATUS
Reference: WAGO I/O System Field documentation, Table 19
BYTES: 1
Each bit representing one port 0 = port is not a digital input; 1 = port is a digital input
========================================================
EIP_DI_data: DIGITAL INPUT DATA
Reference: WAGO I/O System Field documentation, Tables 20 and 21
BYTES: 2
Not sure about use of these data
========================================================
EIP_x1_pqi: PORT QUALIFIER INFORMATION
Reference: WAGO I/O System Field documentation, Table 37 of documentation
BYTES: 1
Bit 0: reserved
1: reserved
2: reserved
3: event: 0 = port has no IO-Link event; 1 = port has IO-Link event
4: reserved
5: DevCom: 0 = no IO-Link device present; 1 = IO-Link device present
6: DevErr: 0 = no error/warning; 1 = error/warning
7: PQ: 0 = port qualfier data invalid; 1 = port qualifier data valid
========================================================
EIP_DO_status: DIGITAL OUTPUT STATUS
Reference: WAGO I/O System Field documentation, Table 28 of documentation
BYTES: 1
Each bit representing one port 0 = port is not a digital output; 1 = port is a digital output
========================================================
EIP_DO_data: DIGITAL OUTPUT DATA
Reference: WAGO I/O System Field documentation, Table 28 of documentation
BYTES: 2
Not sure about use of these data
========================================================
EIP_x1_enable_out: DIGITAL OUTPUT DATA
Reference: WAGO I/O System Field documentation, Table 28 of documentation
BYTES: 1
Bit 0: 0 = disable; 1 = enable
Bit 1-7: reserved
========================================================*)
END_VAR
(* =======================================*)
(* ===== [ Set Function Block Busy ] =====*)
(* =======================================*)
IF i_step > 0 THEN
x_busy := TRUE;
ELSE
x_busy := FALSE;
END_IF
(* =============================*)
(* ===== [ State Machine ] =====*)
(* =============================*)
CASE i_step OF
0 : // Idle
IF x_read_cmd OR x_write_cmd OR x_port_query THEN
i_step := 5;
s_error := 'None';
END_IF
5 : // PLC will begin executing before the EIP connection is established.
// Check for an EIP connection before trying to access to the IO-Link device.
IF IO_Link_Master.eState <> IoDrvEthernetIp.AdapterState.RUNNING THEN
i_step := 999;
x_error := TRUE;
s_error := 'EIP Master Not Running';
ELSIF NOT FN_Port_Active(port_number) THEN
i_step := 999;
x_error := TRUE;
s_error := 'EIP Port not active';
ELSIF port_number > GVL.max_iol_ports THEN
i_step := 999;
x_error := TRUE;
s_error := 'Requested IOL port number exceeds project maximum';
ELSE
i_step := 10;
END_IF
10 : // determine if read or write call
IF x_write_cmd THEN
i_step := 100; // Steps 100 to 199 write to device
ELSIF x_read_cmd THEN
i_step := 200; // Steps 200 to 299 read from device
ELSIF x_port_query THEN
i_step := 300; // Steps 300 to 399 read from device
END_IF
(* ===== [ Write to device ] =====*)
100 : // initialise write
x_write := TRUE;
i_step := 110;
110 : // check if complete
IF NOT x_write AND EIP_done THEN
x_done := TRUE;
i_step := 999;
END_IF
(* ===== [ Read from device ] =====*)
200 : // initialise read
x_read := TRUE;
i_step := 210;
210 : // check if complete
IF NOT x_read AND EIP_done THEN
x_done := TRUE;
data_read := EIP_read_data;
io_read_length := EIP_read_data_size;
i_step := 999;
END_IF
(* ===== [ Port query ] =====*)
300 : // initialise query
//x_query := TRUE;
//i_step := 310;
i_step := 999;
x_error := TRUE;
s_error := 'Port query not currently working';
310 : // check if complete
IF NOT x_query AND EIP_done THEN
x_done := TRUE;
data_read := EIP_read_data;
io_read_length := EIP_read_data_size;
i_step := 999;
END_IF
(* ===== [ Step 999 resets the fuction block] =====*)
999 : // Reset
x_done := FALSE;
x_error := FALSE;
s_error := '';
x_busy := FALSE;
x_read_cmd := FALSE;
x_read := FALSE;
x_write_cmd := FALSE;
x_write := FALSE;
EIP_trigger := FALSE;
i_step := 0;
END_CASE
(* ============================================*)
(* ===== [ Process EIP service requests ] =====*)
(* ============================================*)
// if x_query TRUE, then initialise query of the service
IF x_query THEN
EIP_read_data_size := 64; // Size of receive buffer. Can't leave this at 0, or no data will be read
EIP_eService := 16#0E; // Get Attribute Single
EIP_class := 16#41; // Object 65 (0x41) - IO-Link Event Log
EIP_instance := port_number; // This is the physical port number (1-8) of the 765-450X IO-Link Master
EIP_attribute := 2; // 2 returns current state of instance (1=stopped, 2=empty, 3=present, 4=full/overwrite, 5=full/stop)
EIP_trigger := TRUE; // Setting this variable TRUE will execute the EIP function block
x_query := FALSE;
END_IF
// if x_read TRUE, then initialise read parameters and execute the read service
IF x_read THEN
EIP_write_data[0] := TO_BYTE(io_index); // Index (LSB)
EIP_write_data[1] := TO_BYTE(SHR(io_index,8)); // Index (MSB)
EIP_write_data[2] := io_subindex; // Subindex
EIP_write_data_size := 3; // 3 bytes for index/subindex
EIP_read_data_size := 64; // Size of receive buffer. Can't leave this at 0, or no data will be read
EIP_eService := 16#4B; // Read is 0x4B, Write is 0x4C
EIP_class := 16#83; // Object 131 (0x83) - IO-Link Device Parameters
EIP_instance := port_number; // This is the physical port number (1-8) of the 765-450X IO-Link Master
EIP_attribute := 0; // Always 0
EIP_trigger := TRUE; // Setting this variable TRUE will execute the EIP function block
x_read := FALSE;
END_IF
// if x_write TRUE, then initialise write parameters and execute the write service
IF x_write THEN
EIP_write_data[0] := TO_BYTE(io_index); // Index (LSB)
EIP_write_data[1] := TO_BYTE(SHR(io_index,8)); // Index (MSB)
EIP_write_data[2] := io_subindex; // Subindex
WagoSysPlainMem.MemCopySecure( // copy write data to internal buffer
pDest := ADR(EIP_write_data)+3,
udiDestSize := SIZEOF(EIP_write_data)-3,
pSource := ADR(data_write),
udisourceSize := io_write_length,
bPadding :=0
);
EIP_write_data_size := 3 + io_write_length; // 3 bytes for index/subindex, plus byte length of data to write
EIP_read_data_size := 0; // Can be set to 0 for write service
EIP_eService := 16#4C; // Read is 0x4B, Write is 0x4C
EIP_class := 16#83; // Object 131 (0x83) - IO-Link Device Parameters
EIP_instance := port_number; // This is the physical port number (1-8) of the 765-450X IO-Link Master
EIP_attribute := 0; // Always 0
EIP_trigger := TRUE; // Setting this variable TRUE will execute the EIP function block
x_write := FALSE;
END_IF
// Instance of ENIP.Generic_Service function block
// This function block performs a generic service at an EtherNet/IP Adapter
// The message will be sent as an unconnected explicit message request
EIP(
xExecute := EIP_trigger,
xDone => EIP_done,
xBusy => EIP_busy,
xError => EIP_err,
itfEtherNetIPDevice := IO_Link_Master,
eClass := EIP_class,
dwInstance := EIP_instance,
wAttribute := EIP_attribute,
eError => EIP_eError,
eService := EIP_eService,
pWriteData := ADR(EIP_write_data),
udiWriteDataSize := EIP_write_data_size,
pReadData := ADR(EIP_read_data),
udiReadDataSize := EIP_read_data_size,
udiReceivedDataSize => EIP_rcvd_size
);
(* =============================================*)
(* ===== [ Process EIP service responses ] =====*)
(* =============================================*)
IF EIP_done THEN
EIP_trigger := FALSE;
END_IF
IF EIP_err THEN
x_error := TRUE;
s_error := WagoAppString.Concat3('EIP error = ','', TO_STRING(EIP_eError));
i_step := 999;
END_IF
(* ==============================*)
(* ===== [ Watchdog Timer ] =====*)
(* ==============================*)
tonWatchDog( IN := i_step <> 0, PT := t_timeout);
IF tonWatchdog.Q THEN
x_error := TRUE;
s_error := 'Timeout';
i_step := 999;
END_IF
(* ==============================*)
(* ===== [ FN_Port_active ] =====*)
(* ==============================*)
FUNCTION FN_Port_Active : BOOL
VAR_INPUT
port_number : DWORD := 1;
END_VAR
VAR
byte_value : BYTE;
byte_bit : BYTE_AS_BIT;
END_VAR
byte_value := FN_Port_PQI(port_number);
byte_bit(B:=byte_value, B5=>FN_Port_Active);
(* ===========================*)
(* ===== [ FN_Port_PQI ] =====*)
(* ===========================*)
FUNCTION FN_Port_PQI : Byte
VAR_INPUT
port_number : DWORD := 1;
END_VAR
VAR
END_VAR
IF port_number = 1 THEN
FN_Port_PQI := EIP_x1_pqi;
ELSIF port_number = 2 THEN
FN_Port_PQI := EIP_x2_pqi;
ELSIF port_number = 3 THEN
FN_Port_PQI := EIP_x3_pqi;
ELSIF port_number = 4 THEN
FN_Port_PQI := EIP_x4_pqi;
ELSIF port_number = 5 THEN
FN_Port_PQI := EIP_x5_pqi;
ELSIF port_number = 6 THEN
FN_Port_PQI := EIP_x6_pqi;
ELSIF port_number = 7 THEN
FN_Port_PQI := EIP_x7_pqi;
ELSIF port_number = 8 THEN
FN_Port_PQI := EIP_x8_pqi;
END_IF