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