# CODESYS communicatioins with IO-Link master 765-45x

**URL:** https://www.wago.community/t/codesys-communicatioins-with-io-link-master-765-45x/1884
**Category:** CODESYS
**Tags:** codesys
**Created:** [May 28, 2025, 4:16am UTC](https://www.wago.community/t/codesys-communicatioins-with-io-link-master-765-45x/1884 "2025-05-28T04:16:45Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![Justin](https://avatars.discourse-cdn.com/v4/letter/j/3ab097/32.png) [@Justin](https://www.wago.community/u/Justin)
#### Post date: [May 28, 2025, 4:16am UTC](https://www.wago.community/t/codesys-communicatioins-with-io-link-master-765-45x/1884/1 "2025-05-28T04:16:45Z")

</div>

We are using the 765-4501 as an IO-Link master, connected by EtherNet/IP to a PFC200 (750-8217). Logic is programmed in CODESYS (V3.5 SP19 Patch 7). We know the set of IO-Link slave devices that will be connected to the IO-Link master, but not how many of each type of device will be connected or which ports each will use. We have been unable to find a CODESYS library to facilitate communications between the PFC and the 765-4501 (unlike e.g. the 750-657) and consequently have two questions:

1. How can we identify IO-Link device details for each connected port?
2. How can we write to selected settings of IO-Link devices for each connected port?

---

<div class="post-metadata">

### Author: ![MikePsaltis](https://sea2.discourse-cdn.com/flex016/user_avatar/www.wago.community/mikepsaltis/32/47_2.png) [@MikePsaltis](https://www.wago.community/u/MikePsaltis)
#### Post date: [May 28, 2025, 2:42pm UTC](https://www.wago.community/t/codesys-communicatioins-with-io-link-master-765-45x/1884/2 "2025-05-28T14:42:50Z")

</div>

Hi Justin,

You can do this with ISDU read/writes via an EthernetIP Explicit Message Function block.

I am working on a function block that can configure the ports of a WAGO 765-1703 DIO Hub (IOL Device). Here is what I have so far in this FB:

1. Check that the EIP Connection is OK

2. Check that the device that is connected to the specified port is a “765-1703”. In this case I perform an ISDU read for Index 19.  

3. I can then read and write to Index 64 (16#40) to set the each port as an input or output.  
 ![image](https://us1.discourse-cdn.com/flex016/uploads/wago/original/2X/8/80b05b65596aecf243c2508dbf054baa55d2356a.png)

You could all adapt this FB to read Index 19 for each port to get data about what device is connected. I believe that this is an IO-Link standard Index, but would need to check the manual of you IOL devices.

For this case, this is what the instance of my function block looks like. I pass it the EIP device name, a port number and can trigger reads/writes to the DIO Hub.

 ![image](https://us1.discourse-cdn.com/flex016/uploads/wago/original/2X/9/9ec445cccbd410d38c8edb8ea46807d54609933f.png)

I added the development logic to a GitHub link for now. Its not finalized but should get you started.

:[GitHub - mpsaltis/dev\_EIP\_IOLData\_FB: This is a development project to read IOL data from a WAGO 765-4501 master connected to a 765-1703 DIO Hub.](https://github.com/mpsaltis/dev_EIP_IOLData_FB/tree/main)

---

<div class="post-metadata">

### Author: ![MikePsaltis](https://sea2.discourse-cdn.com/flex016/user_avatar/www.wago.community/mikepsaltis/32/47_2.png) [@MikePsaltis](https://www.wago.community/u/MikePsaltis)
#### Post date: [May 28, 2025, 2:53pm UTC](https://www.wago.community/t/codesys-communicatioins-with-io-link-master-765-45x/1884/3 "2025-05-28T14:53:19Z")

</div>

I also forgot to mention that you can use the Web Based Management of the 765-4501 to do ISDU R/Ws. This was handy when testing.

---

<div class="post-metadata">

### Author: ![Justin](https://avatars.discourse-cdn.com/v4/letter/j/3ab097/32.png) [@Justin](https://www.wago.community/u/Justin)
#### Post date: [May 30, 2025, 6:29am UTC](https://www.wago.community/t/codesys-communicatioins-with-io-link-master-765-45x/1884/4 "2025-05-30T06:29:35Z")

</div>

> [@MikePsaltis](#):
>
> 765-1703

Hi Mike, This is really useful. I didn’t respond immediately, as I have been focussed on putting together a broadly similar routine. Like you, I started with the EtherNetIP Services library and provide my current working here in case it might be of some use:

```auto
FUNCTION_BLOCK FB_IOL_Index_Comms
VAR_INPUT
	x_trigger : BOOL;
	x_execute : BOOL;
	port_number : BYTE;
	io_index : WORD;
	io_subindex : BYTE;
	io_length : WORD;
	read_flag : BOOL;
	t_timeout : TIME := T#500S;
	t_timegap : TIME := T#0MS;
END_VAR
VAR_OUTPUT
	x_status : EVAL_STATUS;
	s_state : STRING;
	s_iol_call_data :	STRING(257);
END_VAR
VAR_IN_OUT
	data : ARRAY[0..255] OF BYTE;
END_VAR
VAR
	cip_class : ENIP.CIPClass := 16#83; //ISDU service of the IO-Field device
	cip_attribute : WORD := 0; //to avoid crash for unknown reason

	gs_done : BOOL;
	gs_busy : BOOL;
	gs_error : BOOL;

	state : INT;
	s_state_m1 : STRING;
	a_state : ARRAY[0..log_size] OF STRING;
	ii : INT;
	generic_service : ENIP.Generic_Service;
	usint_bytes : USINT_TO_BYTES;
	bytes_dint : BYTES_TO_DINT;
	cip_service : ENIP.CIPCommonService;
	cip_instance : DWORD;
	rec_data_size : UDINT;
	write_data : ARRAY[0..255] OF BYTE;
	
	o_timeout : TON;
	o_timegap : TON;
	flag_gap : BOOL;
END_VAR
VAR CONSTANT
	log_size : INT := 20;
END_VAR

IF x_trigger AND (o_timegap.Q OR x_status = EVAL_STATUS.rest) THEN

	//initialise
	flag_gap := FALSE;
	o_timegap(IN := flag_gap, PT := t_timegap);
	x_status := EVAL_STATUS.busy;
	generic_service(
		xDone => gs_done,
		xBusy => gs_busy,
		xError => gs_error,
		udiReceivedDataSize => rec_data_size,
		itfEtherNetIPDevice := IO_Link_Master,
		dwInstance := cip_instance,
		eClass := cip_class,
		wAttribute := cip_attribute
	);
	
	CASE state OF
		//STATE OF MACHINE
		
		0 :	//initialisation
			state := 1;
			s_state := 'IOL-CALL - initialised';
			
		1 : //wait for execute
			IF x_execute THEN
				(* ERROR CHECKING *)
				IF port_number < 0 OR port_number > 8 THEN
					state := 999;
					s_state := 'IOL-CALL - port error';
				ELSE
					cip_instance := port_number;
					state := 10;
					s_state := 'IOL-CALL - initialised';
				END_IF
			ELSE
				s_state := 'IOL-CALL - waiting for execution command';
			END_IF
			
		10 : //IOL-CALL to read or write
			IF read_flag THEN
				state := 20;
				s_state := 'IOL-CALL - read selected';
			ELSE
				state := 30;
				s_state := 'IOL-CALL - write selected';
			END_IF
			
		20 : //IOL-CALL to read
			cip_service := 75; //75 (0x4B) ISDU read service
			generic_service.eService := cip_service;
			generic_service.pReadData := ADR(data); //external buffer
			generic_service.udiReadDataSize := SIZEOF(data); //external buffer size

			//set indices			
			write_data[0] := TO_BYTE(io_index); //index
			write_data[1] := TO_BYTE(SHR(io_index,8));
			write_data[2] := io_subindex; //subindex
			generic_service.pWriteData := ADR(write_data); //internal buffer
			generic_service.udiWriteDataSize := 3; //internal buffer size
			
			generic_service.xExecute := TRUE; //start read
			state := 21;
			s_state := 'IOL-CALL - read start';
			
		21 : //wait for read response
			IF gs_busy AND o_timeout.Q THEN
				state := 999;
				s_state := 'IOL-CALL - read timeout';
			ELSIF gs_error THEN
				state := 999;
				s_state := 'IOL-CALL - read error';
			ELSIF gs_done THEN
				state := 40;
				s_state := 'IOL-CALL - read done';
			END_IF
			
		30 : //IOL-CALL to write
			cip_service := 76; //76 (0x4C) ISDU write service
			generic_service.pReadData := ADR(data); //external buffer
			generic_service.udiReadDataSize := SIZEOF(data); //external buffer size
			
			//set indices			
			write_data[0] := TO_BYTE(io_index); //index
			write_data[1] := TO_BYTE(SHR(io_index,8));
			write_data[2] := io_subindex; //subindex

			//append write data to internal buffer
			WagoSysPlainMem.MemCopySecure(	
				pDest := ADR(write_data)+3, //copy to internal buffer 
				udiDestSize := SIZEOF(write_data)-3, 
				pSource := ADR(data), //copy source ist the external buffer
				udisourceSize := io_length, //length to copy
				bPadding :=0);
			generic_service.pWriteData := ADR(write_data); //internal buffer pointer
			generic_service.udiWriteDataSize := io_length + 3; //internal buffer size
			
			generic_service.xExecute := TRUE; //start write
			state := 31;
			s_state := 'IOL-CALL - write start';
			
		31 : //wait for write response
			IF gs_busy AND o_timeout.Q THEN
				state := 999;
				s_state := 'IOL-CALL - write timeout';
			ELSIF gs_error THEN
				state := 999;
				s_state := 'IOL-CALL - write error';
			ELSIF gs_done THEN
				state := 40;
				s_state := 'IOL-CALL - write done';
			END_IF
			
		40 : //copy data
			WagoSysPlainMem.MemCopySecure(	
				pDest := ADR(s_iol_call_data), 
				udiDestSize := SIZEOF(s_iol_call_data), 
				pSource := ADR(data), 
				udisourceSize := rec_data_size, 
				bPadding :=0
			);					
			state := 41;
			s_state := 'IOL-CAll - processed response';
		
		41 : //done
			Reset( m_status := EVAL_STATUS.done, m_state := 'IOL-CALL - done');
		
		999 : //error
			Reset( m_status := EVAL_STATUS.error, m_state := WagoAppString.Concat3('error = ','', TO_STRING(generic_service.eError)));
			
		ELSE
			Reset(m_status := EVAL_STATUS.busy, m_state := 'IOL-CALL - state not recognised');
	END_CASE

	//state machine log
	IF s_state <> s_state_m1 THEN
		FOR ii := log_size TO 0 BY -1 DO
			IF ii > 0 THEN
				a_state[ii] := a_state[ii-1];
			ELSE
				a_state[ii] := s_state;
			END_IF
		END_FOR
	END_IF
	s_state_m1 := s_state;

	//timeout TON
	o_timeout(IN := (x_status=EVAL_STATUS.busy), PT := t_timeout);
ELSE
	IF x_status <> EVAL_STATUS.busy THEN
		// OTHERWISE ASSUME TIME GAP TO AVOID COMMS CONFLICT
		state := 0;
		x_status := EVAL_STATUS.rest;
		x_execute := FALSE;
		generic_service.xExecute := FALSE;
	END_IF
END_IF
flag_gap := TRUE;
o_timegap(IN := flag_gap, PT := t_timegap);

/////////////////////////////////////////////////////

METHOD PRIVATE reset
(* METHOD TO RESET THE FB TO AN INITIALISED STATE *) 
VAR_INPUT
	m_status : EVAL_STATUS;
	m_state : STRING;
END_VAR

x_status := m_status;
s_state := m_state;
x_execute := FALSE;
generic_service.xExecute := FALSE;
state := 1;

```

---

<div class="post-metadata">

### Author: ![Justin](https://avatars.discourse-cdn.com/v4/letter/j/3ab097/32.png) [@Justin](https://www.wago.community/u/Justin)
#### Post date: [May 31, 2025, 12:50pm UTC](https://www.wago.community/t/codesys-communicatioins-with-io-link-master-765-45x/1884/5 "2025-05-31T12:50:19Z")

</div>

Hi again, Your example is brilliant - many thanks for sharing. I have worked it into my project, but have a quick follow-up question: how can I check if a port is active? Given documentation for 765-4501, I thought that I could access this information from the event log (Object 65, 0x41), attribute 2 (current state; 1=stopped, 2=empty, 3=present, 4=full/overwrite, 5=full/stop). The parameters that I set for this query are:

```auto
	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

```

Unfortunately, this didn’t work as I had hoped. Grateful for any additional guidance.

---

<div class="post-metadata">

### Author: ![MikePsaltis](https://sea2.discourse-cdn.com/flex016/user_avatar/www.wago.community/mikepsaltis/32/47_2.png) [@MikePsaltis](https://www.wago.community/u/MikePsaltis)
#### Post date: [June 2, 2025, 3:01pm UTC](https://www.wago.community/t/codesys-communicatioins-with-io-link-master-765-45x/1884/6 "2025-06-02T15:01:25Z")

</div>

Great, thanks for sharing your code here!

You might be able to use the PQI data from the IO-Link master. This is part of the cyclic process image for the master.

 ![image](https://us1.discourse-cdn.com/flex016/uploads/wago/original/2X/8/8395ad134e88d8c7263c9ffcd9b81694b607f295.png)

 ![image](https://us1.discourse-cdn.com/flex016/uploads/wago/original/2X/3/34a466548106e8d26f63c7db734db8d7bc47a6d4.png)

---

<div class="post-metadata">

### Author: ![Justin](https://avatars.discourse-cdn.com/v4/letter/j/3ab097/32.png) [@Justin](https://www.wago.community/u/Justin)
#### Post date: [June 2, 2025, 3:56pm UTC](https://www.wago.community/t/codesys-communicatioins-with-io-link-master-765-45x/1884/7 "2025-06-02T15:56:29Z")

</div>

Yes, managed to work out integration with the PQI data today. Revised code that integrates this functionality providerd here:

```auto
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

```
