CoE / SoE / AoE
Using the EtherCAT simulation it is possible to route the protocols CoE (Can over EtherCAT), SoE (Sercos over EtherCAT) and AoE (ADS over EtherCAT) further (e.g. to the PLC). In this way data can be read or written from the PLC via these protocols.
Routing of CoE and SoE to a PLC
The following two steps are required:
- 1. The EtherCAT simulation device must be notified of the NetId and the port to which the data are to be relayed. This is done via an ADS-Write to the NetID of the Simulation Device, PortNr.: 0xFFFF, IndexGroup: 0xFF01, IndexOffset:1
Example: Registering the mailbox recipient
//AMSNetID of Simulation Device
netIdSimu := '5.35.2.224.2.1';
//AMSNetID of PLC Project on Sim. Dev.
arrNetId[0]:= 5;
arrNetId[1]:= 35;
arrNetId[2]:= 2;
arrNetId[3]:= 224;
arrNetId[4]:= 1; //system ID 1.1
arrNetId[5]:= 1;
arrNetId[6]:= 16#53; //Port of PLC runtime (1)
arrNetId[7]:= 16#03; //Port of PLC runtime (2) (Port 851 = 16#353)
// sent registration to Simulation driver
fbAdsWrite(WRITE:= FALSE);
fbAdsWrite(NETID:= netIdSimu, PORT:= 16#FFFF, IDXGRP:= 16#FF01, IDXOFFS:= 1, SRCADDR:= ADR(arrNetId), LEN:= 8, WRITE:= TRUE);
- 2. Receiving and responding to the ADS indications. The mailbox service is stored in the IndexGroup, the PortID identifies the sending or receiving device, and the data to be accessed is identified through index and subindex.
Example: Intercepting and answering indications:
//capture all write requests
fbAdsWriteInd();
IF fbAdsWriteInd.VALID = TRUE THEN //true if a write command has been sent
IF fbAdsWriteInd.IDXGRP = 16#8000F302 THEN //F302 = CoE request, F420 = SoE request
//fill in your custom CoE Object Dictionary
// example reaction
IF fbAdsWriteInd.IDXOFFS = 16#80000000 THEN (*error*)
fbAdsWriteRes(
NETID:= fbAdsWriteInd.NETID,
PORT:= fbAdsWriteInd.PORT,
INVOKEID:= fbAdsWriteInd.INVOKEID,
RESULT:= 0,
RESPOND:= TRUE);
fbAdsWriteRes(RESPOND:= FALSE);
ELSE
fbAdsWriteRes(
NETID:= fbAdsWriteInd.NETID,
PORT:= fbAdsWriteInd.PORT,
INVOKEID:= fbAdsWriteInd.INVOKEID,
RESULT:= 0,
RESPOND:= TRUE);
fbAdsWriteRes(RESPOND:= FALSE);
END_IF
END_IF
fbAdsWriteInd(CLEAR:= TRUE);
fbAdsWriteInd(CLEAR:= FALSE);
END_IF
//capture all read requests
fbAdsReadInd();
IF fbAdsReadInd.VALID = TRUE THEN
IF fbAdsReadInd.IDXGRP = 16#8000F302 THEN (*CoE*)
// fill in your CoE Object Dictionary
// example reaction
IF fbAdsReadInd.IDXOFFS = 16#80000000 THEN (*error*)
fbAdsReadRes(
NETID:= fbAdsReadInd.NETID,
PORT:= fbAdsReadInd.PORT,
INVOKEID:= fbAdsReadInd.INVOKEID,
LEN:= 0,
RESULT:= 16#34567890,
RESPOND:= TRUE);
ELSE
IF fbAdsReadInd.IDXOFFS = 16#60000000 THEN (*short response*)
fbAdsReadRes(
NETID:= fbAdsReadInd.NETID,
PORT:= fbAdsReadInd.PORT,
INVOKEID:= fbAdsReadInd.INVOKEID,
DATAADDR:= ADR(arrNetId),
LEN:= 2,
RESULT:= 0,
RESPOND:= TRUE);
ELSE
fbAdsReadRes(
NETID:= fbAdsReadInd.NETID,
PORT:= fbAdsReadInd.PORT,
INVOKEID:= fbAdsReadInd.INVOKEID,
DATAADDR:= ADR(arrNetId),
LEN:= 6,
RESULT:= 0,
RESPOND:= TRUE);
END_IF
END_IF
fbAdsReadRes(RESPOND:= FALSE);
END_IF
fbAdsReadInd(CLEAR:= TRUE);
fbAdsReadInd(CLEAR:= FALSE);
END_IF
As shown in the sample, a check takes place to ascertain whether the indication is valid and if yes, whether it was sent by the simulation device. If both conditions are met, it is a relayed mailbox message that requires a corresponding response. The IndexGroup indicates the mailbox protocol (16#8000F302 for CoE, 16#8000F420 for SoE).
The sample described above can be downloaded here: Download
Sending CoE Emergency Error Messages
The sending of CoE Emergency error messages is carried out analogously as shown above via ADS Write to the NetId of the EtherCAT simulation device, PortNr.: Port number of the terminal that is to send the emergency, IndexGroup: 0x00FF41, IndexOffset:0.
Sending SoE Notifications
The sending of SoE Notifications error messages takes place analogously as shown above via ADS Write to the NetId of the EtherCAT simulation device, PortNr.: Port number of the terminal that is to send the notification, IndexGroup: 0x00FF42, IndexOffset: results from the type definition TETHERCAT_SOE_ADS_IOFFS:
typedef struct TETHERCAT_SOE_ADS_IOFFS
{
USHORT IDN;
union
{
struct
{
BYTE DataState : 1;
BYTE Name : 1;
BYTE Attribute : 1;
BYTE Unit : 1;
BYTE Min : 1;
BYTE Max : 1;
BYTE Value : 1;
BYTE Default : 1;
};
BYTE Elements;
};
BYTE DriveNo : 3;
BYTE Reserved2 : 4;
BYTE Command : 1;
} ETHERCAT_SOE_ADS_IOFFS
Example: 0x00401234 -> For the SoE IDN 1234 the value flag is addressed here.
Routing of AoE to a PLC
Registration of the mailbox recipient is identical to the description under CoE and SoE. The handling of AoE in the PLC also takes place in a similar structure, although several queries are involved, due to the functionality of AoE. A sample is shown below, which can be downloaded here: Download
fbAdsReadWriteInd();
IF fbAdsReadWriteInd.VALID = TRUE THEN
MEMCPY(ADR(arrTmp), fbAdsReadWriteInd.DATAADDR, fbAdsReadWriteInd.WRTLENGTH);
//copy of data in temp variable
//evaluate destination of AeE command (e.g. terminal)-first 6 byte in arrTmp
FOR Idx := 0 TO 5 DO
arrDest[Idx] := arrTmp[Idx];
END_FOR
IF smyDestId = F_CreateAmsNetId(arrDest) THEN
//evaluate port (different physical ports or different services of terminal may be possible)
IF fbAdsReadWriteInd.PORT = 16#1000 THEN //Io-Link Port 1
// evaluate command type - byte 16 : write = 3, read = 2
IF arrTmp[16] = 3 THEN // write command
//evaluate index group and index offset as shown in CoE and SoE
//index group: evaluate service (e.g. object register of Io-Link Terminal)
IF fbAdsReadWriteInd.IDXGRP = 16#8000F302 THEN
IF fbAdsReadWriteInd.IDXOFFS = 16#80000000 THEN //adressing of service
MEMCPY(ADR(arrTmp), fbAdsReadWriteInd.DATAADDR + 8, 8);
//change destination net ID and source net ID
MEMCPY(ADR(arrTmp) + 8, fbAdsReadWriteInd.DATAADDR, 8);
arrTmp[18].0:= 1; (*set Respond Bit*)
arrTmp[20]:= 4; (*Length*)
arrTmp[21]:= 0;
arrTmp[22]:= 0;
arrTmp[23]:= 0;
arrTmp[32]:= 0; (*Response Code*)
arrTmp[33]:= 0;
arrTmp[34]:= 0;
arrTmp[35]:= 0;
fbAdsReadWriteRes(
NETID:= fbAdsReadWriteInd.NETID,
PORT:= fbAdsReadWriteInd.PORT,
INVOKEID:= fbAdsReadWriteInd.INVOKEID,
DATAADDR:= ADR(arrTmp),
LEN:= 36, RESPOND:= TRUE);
END_IF
END_IF
ELSIF arrTmp[16] = 2 THEN //read command
IF fbAdsReadWriteInd.IDXGRP = 16#8000F302 THEN
IF fbAdsReadWriteInd.IDXOFFS = 16#80000000 THEN //adressing of service
MEMCPY(ADR(arrTmp), fbAdsReadWriteInd.DATAADDR + 8, 8);
//change destination net ID and source net ID
MEMCPY(ADR(arrTmp) + 8, fbAdsReadWriteInd.DATAADDR, 8);
arrTmp[18].0:= 1; (* set Respond Bit*)
arrTmp[20]:= 14; (*Len*)
arrTmp[21]:= 0;
arrTmp[22]:= 0;
arrTmp[23]:= 0;
arrTmp[32]:= 0; (*Response Code*)
arrTmp[33]:= 0;
arrTmp[34]:= 0;
arrTmp[35]:= 0;
arrTmp[36]:= 6; (*Len*)
arrTmp[37]:= 0;
arrTmp[38]:= 0;
arrTmp[39]:= 0;
arrTmp[40]:= arrNetId[0]; (*Daten*)
arrTmp[41]:= arrNetId[1];
arrTmp[42]:= arrNetId[2];
arrTmp[43]:= arrNetId[3];
arrTmp[44]:= arrNetId[4];
arrTmp[45]:= arrNetId[5];
fbAdsReadWriteRes(
NETID:= fbAdsReadWriteInd.NETID,
PORT:= fbAdsReadWriteInd.PORT,
INVOKEID:= fbAdsReadWriteInd.INVOKEID,
DATAADDR:= ADR(arrTmp),
LEN:= 46, RESPOND:= TRUE);
END_IF;
END_IF;
END_IF
END_IF
END_IF;
fbAdsReadWriteRes(
NETID:= fbAdsReadWriteInd.NETID,
PORT:= fbAdsReadWriteInd.PORT,
INVOKEID:= fbAdsReadWriteInd.INVOKEID,
DATAADDR:= ADR(arrTmp),
LEN:= fbAdsReadWriteInd.WRTLENGTH,
RESPOND:= TRUE);
fbAdsReadWriteRes(RESPOND:= FALSE);
fbAdsReadWriteInd(CLEAR:= TRUE);
fbAdsReadWriteInd(CLEAR:= FALSE);
END_IF