6.10. ADS over EtherCAT (AoE)

To handle AoE object transfers within the application, the callbacks pfnAoeRead, pfnAoeWrite and/or pfnAoeReadWrite can be registered using esSetSlaveAoeObjectTransferCallbacks() or esSetSlaveSscApplication(), e.g. in myAppPrepare() of EcSimulatorHilDemo / EcSimulatorSilDemo.

See also AoE Simulator and Master Example

6.10.1. esAoeGetSlaveNetId

EC_T_DWORD esAoeGetSlaveNetId(EC_T_DWORD dwInstanceID, EC_T_DWORD dwSlaveId, EC_T_AOE_NETID *poAoeNetId)

Retrieve the NetID of a specific EtherCAT device.

Parameters
  • dwInstanceID – [in] Instance ID (Multiple EtherCAT Network Support)

  • dwSlaveId – [in] Slave ID

  • poAoeNetId – [out] AoE NetID of the corresponding slave

Returns

6.10.2. esAoeRead

EC_T_DWORD esAoeRead(EC_T_DWORD dwInstanceID, EC_T_DWORD dwSlaveId, EC_T_AOE_NETID *poTargetNetId, EC_T_WORD wTargetPort, EC_T_DWORD dwIndexGroup, EC_T_DWORD dwIndexOffset, EC_T_DWORD dwDataLen, EC_T_BYTE *pbyData, EC_T_DWORD *pdwDataOutLen, EC_T_DWORD *pdwErrorCode, EC_T_DWORD *pdwCmdResult, EC_T_DWORD dwTimeout)

Execute a AoE mailbox read request to an EtherCAT slave device.

This function may not be called from within the JobTask’s context.

Parameters
  • dwInstanceID – [in] Instance ID (Multiple EtherCAT Network Support)

  • dwSlaveId – [in] Slave ID

  • poTargetNetId – [in] Target NetID.

  • wTargetPort – [in] Target port.

  • dwIndexGroup – [in] AoE read command index group.

  • dwIndexOffset – [in] AoE read command index offset

  • dwDataLen – [in] Buffer length in bytes

  • pbyData – [out] Buffer receiving transferred data

  • pdwDataOutLen – [out] Number of bytes read from the target device

  • pdwErrorCode – [out] AoE response error code

  • pdwCmdResult – [out] AoE read command result code

  • dwTimeout – [in] Timeout [ms]. The function will block at most for this time.

Returns

6.10.3. esAoeWrite

EC_T_DWORD esAoeWrite(EC_T_DWORD dwInstanceID, EC_T_DWORD dwSlaveId, EC_T_AOE_NETID *poTargetNetId, EC_T_WORD wTargetPort, EC_T_DWORD dwIndexGroup, EC_T_DWORD dwIndexOffset, EC_T_DWORD dwDataLen, EC_T_BYTE *pbyData, EC_T_DWORD *pdwErrorCode, EC_T_DWORD *pdwCmdResult, EC_T_DWORD dwTimeout)

Execute a AoE mailbox write request to an EtherCAT slave device.

This function may not be called from within the JobTask’s context.

Parameters
  • dwInstanceID – [in] Instance ID (Multiple EtherCAT Network Support)

  • dwSlaveId – [in] Slave ID

  • poTargetNetId – [in] Target NetID

  • wTargetPort – [in] Target port

  • dwIndexGroup – [in] AoE write command index group

  • dwIndexOffset – [in] AoE write command index offset

  • dwDataLen – [in] Buffer length in bytes

  • pbyData – [in] Buffer containing transferred data

  • pdwErrorCode – [out] Pointer to AoE response error code

  • pdwCmdResult – [out] Pointer to AoE write command result code

  • dwTimeout – [in] Timeout [ms]. The function will block at most for this time.

Returns

6.10.4. esAoeReadWrite

EC_T_DWORD esAoeReadWrite(EC_T_DWORD dwInstanceID, EC_T_DWORD dwSlaveId, EC_T_AOE_NETID *poTargetNetId, EC_T_WORD wTargetPort, EC_T_DWORD dwIndexGroup, EC_T_DWORD dwIndexOffset, EC_T_DWORD dwReadDataLen, EC_T_DWORD dwWriteDataLen, EC_T_BYTE *pbyData, EC_T_DWORD *pdwDataOutLen, EC_T_DWORD *pdwErrorCode, EC_T_DWORD *pdwCmdResult, EC_T_DWORD dwTimeout)

Execute a AoE mailbox read/write request to an EtherCAT slave device.

Parameters
  • dwInstanceID – [in] Instance ID (Multiple EtherCAT Network Support)

  • dwSlaveId – [in] Slave ID

  • poTargetNetId – [in] Target NetID

  • wTargetPort – [in] Target port

  • dwIndexGroup – [in] AoE read/write command index group.

  • dwIndexOffset – [in] AoE read/write command index offset

  • dwReadDataLen – [in] Number of bytes to read from the target device.

  • dwWriteDataLen – [in] Number of bytes to read from the target device

  • pbyData – [in, out] Buffer containing and receiving transferred data

  • pdwDataOutLen – [out] Number of bytes read from the target device

  • pdwErrorCode – [out] Pointer to AoE response error code

  • pdwCmdResult – [out] Pointer to AoE write command result code

  • dwTimeout – [in] Timeout [ms]. The function will block at most for this time. EC_NOWAIT is not valid.

Returns

6.10.5. esSetSlaveAoeObjectTransferCallbacks

EC_T_DWORD esSetSlaveAoeObjectTransferCallbacks(EC_T_DWORD dwInstanceId, EC_T_WORD wCfgFixedAddress, EC_T_PF_AOE_READ_CB pfRead, EC_T_PF_AOE_WRITE_CB pfWrite, EC_T_PF_AOE_READWRITE_CB pfReadWrite, EC_T_VOID *pvContext)

Set AoE read/write callbacks.

Parameters
  • dwInstanceId – [in] Simulator Instance ID

  • wCfgFixedAddress – [in] Slave’s station address. 0: all slaves

  • pfRead – [in] Read callback function

  • pfWrite – [in] Write callback function

  • pfReadWrite – [in] ReadWrite callback function

  • pvContext – [in] pointer to context passed as first parameters to callback functions

Returns

EC_E_NOERROR or error code

6.10.6. AoE Simulator and Master Example

The following example demonstrates how to handle AoE object transfers within the simulator application:

AoE Simulator and Master Example

The following example handlers can be registered at the EC-Simulator:

/* EC_T_PF_AOE_READ_CB */
EC_T_DWORD EC_FNCALL myAppAoeReadObjectCallback(
    EC_T_VOID* /* pvContext */, EC_T_DWORD dwSimulatorId, EC_T_WORD wCfgFixedAddress,
    EC_T_AOE_NETID* /* poSenderNetId */, EC_T_WORD /* wSenderPort */, EC_T_AOE_NETID* /* poTargetNetId */, EC_T_WORD wTargetPort,
    EC_T_DWORD dwIndexGroup, EC_T_DWORD dwIndexOffset,
    EC_T_BYTE* pbyReadData, EC_T_DWORD* pdwReadDataLen,
    EC_T_DWORD* pdwErrorCode, EC_T_DWORD* pdwCmdResult)
{
    if (65535 != wTargetPort)
    {
        EC_SETDWORD(pdwErrorCode, EC_E_AOE_TARGET_PORT_NOT_FOUND);
        goto Exit;
    }

    if (0xF302 /* ADSIGRP_CANOPEN_SDO */ != dwIndexGroup)
    {
        EC_SETDWORD(pdwErrorCode, EC_E_AOE_INVALIDGRP);
        goto Exit;
    }

    /* check for object 0x2003, subindex 0, no complete access */
    if (0x20030000 != dwIndexOffset /* example value */)
    {
        EC_SETDWORD(pdwErrorCode, EC_E_AOE_NOTFOUND);
        goto Exit;
    }
    /* check for size of object 0x2003, subindex 0 */
    if (4 != EC_GETDWORD(pdwReadDataLen) /* UDINT: sizeof(EC_T_DWORD) */)
    {
        EC_SETDWORD(pdwErrorCode, EC_E_AOE_INVALIDSIZE);
        goto Exit;
    }

    /* all checks passed, return data and set success */
    EC_SET_FRM_DWORD(pbyReadData, 0x12345678);
    EC_SETDWORD(pdwErrorCode, EC_E_NOERROR);

Exit:
    EcLogMsg(EC_LOG_LEVEL_VERBOSE, (pEcLogContext, EC_LOG_LEVEL_VERBOSE, "myAppAoeReadObjectCallback(%d, %d, %d, 0x%04X, 0x%04X, %d bytes, 0x%08X, 0x%08X): %s (0x%08X)\n",
        dwSimulatorId, wCfgFixedAddress, wTargetPort,
        dwIndexGroup, dwIndexOffset, EC_GETDWORD(pdwReadDataLen), EC_GETDWORD(pdwErrorCode), EC_GETDWORD(pdwCmdResult),
        esGetText(dwSimulatorId, EC_GETDWORD(pdwErrorCode)), EC_GETDWORD(pdwErrorCode)));
    return EC_GETDWORD(pdwErrorCode);
}

/* EC_T_PF_AOE_WRITE_CB */
EC_T_DWORD EC_FNCALL myAppAoeWriteObjectCallback(
    EC_T_VOID* /* pvContext */, EC_T_DWORD dwSimulatorId, EC_T_WORD wCfgFixedAddress,
    EC_T_AOE_NETID* /* poSenderNetId */, EC_T_WORD /* wSenderPort */, EC_T_AOE_NETID* /* poTargetNetId */, EC_T_WORD wTargetPort,
    EC_T_DWORD dwIndexGroup, EC_T_DWORD dwIndexOffset,
    EC_T_BYTE* pbyWriteData, EC_T_DWORD dwWriteDataLen,
    EC_T_DWORD* pdwErrorCode, EC_T_DWORD* pdwCmdResult)
{
    EC_T_DWORD dwVal = 0;
    if (65535 != wTargetPort)
    {
        EC_SETDWORD(pdwErrorCode, EC_E_AOE_TARGET_PORT_NOT_FOUND);
        goto Exit;
    }

    if (0xF302 /* ADSIGRP_CANOPEN_SDO */ != dwIndexGroup)
    {
        EC_SETDWORD(pdwErrorCode, EC_E_AOE_INVALIDGRP);
        goto Exit;
    }

    /* check for object 0x2003, subindex 0, no complete access */
    if (0x20030000 != dwIndexOffset /* example value */)
    {
        EC_SETDWORD(pdwErrorCode, EC_E_AOE_NOTFOUND);
        goto Exit;
    }
    /* check for size of object 0x2003, subindex 0 */
    if (4 != dwWriteDataLen /* UDINT: sizeof(EC_T_DWORD) */)
    {
        EC_SETDWORD(pdwErrorCode, EC_E_AOE_INVALIDSIZE);
        goto Exit;
    }

    /* all checks passed, get data and set success */
    dwVal = EC_GET_FRM_DWORD(pbyWriteData);
    EC_SETDWORD(pdwErrorCode, EC_E_NOERROR);
Exit:
    EcLogMsg(EC_LOG_LEVEL_VERBOSE, (pEcLogContext, EC_LOG_LEVEL_VERBOSE, "myAppAoeWriteObjectCallback(%d, %d, %d, 0x%04X, 0x%04X, %d bytes, 0x%08X, 0x%08X) = %d (0x%08X): %s (0x%08X)\n",
        dwSimulatorId, wCfgFixedAddress, wTargetPort,
        dwIndexGroup, dwIndexOffset, dwWriteDataLen, EC_GETDWORD(pdwErrorCode), EC_GETDWORD(pdwCmdResult),
        dwVal, dwVal,
        esGetText(dwSimulatorId, EC_GETDWORD(pdwErrorCode)), EC_GETDWORD(pdwErrorCode)));
    return EC_GETDWORD(pdwErrorCode);
}

/* EC_T_PF_AOE_READWRITE_CB */
EC_T_DWORD EC_FNCALL myAppAoeReadWriteObjectCallback(
    EC_T_VOID* /* pvContext */, EC_T_DWORD dwSimulatorId, EC_T_WORD wCfgFixedAddress,
    EC_T_AOE_NETID* /* poSenderNetId */, EC_T_WORD /* wSenderPort */, EC_T_AOE_NETID* /* poTargetNetId */, EC_T_WORD wTargetPort,
    EC_T_DWORD dwIndexGroup, EC_T_DWORD dwIndexOffset,
    EC_T_BYTE* pbyReadData, EC_T_DWORD* pdwReadDataLen, EC_T_BYTE* pbyWriteData, EC_T_DWORD dwWriteDataLen,
    EC_T_DWORD* pdwErrorCode, EC_T_DWORD* pdwCmdResult)
{
    EC_T_DWORD dwVal = 0;
    if (65535 != wTargetPort)
    {
        EC_SETDWORD(pdwErrorCode, EC_E_AOE_TARGET_PORT_NOT_FOUND);
        goto Exit;
    }

    if (0xF302 /* ADSIGRP_CANOPEN_SDO */ != dwIndexGroup)
    {
        EC_SETDWORD(pdwErrorCode, EC_E_AOE_INVALIDGRP);
        goto Exit;
    }

    /* check for object 0x2003, subindex 0, no complete access */
    if (0x20030000 != dwIndexOffset /* example value */)
    {
        EC_SETDWORD(pdwErrorCode, EC_E_AOE_NOTFOUND);
        goto Exit;
    }
    /* check for size of object 0x2003, subindex 0 */
    if ((4 != EC_GETDWORD(pdwReadDataLen) /* UDINT: sizeof(EC_T_DWORD) */)
        || (4 != dwWriteDataLen /* UDINT: sizeof(EC_T_DWORD) */))
    {
        EC_SETDWORD(pdwErrorCode, EC_E_AOE_INVALIDSIZE);
        goto Exit;
    }

    /* all checks passed, set and get data and set success */
    EC_SET_FRM_DWORD(pbyReadData, 0x12345678);
    dwVal = EC_GET_FRM_DWORD(pbyWriteData);
    EC_SETDWORD(pdwErrorCode, EC_E_NOERROR);

Exit:

    EcLogMsg(EC_LOG_LEVEL_VERBOSE, (pEcLogContext, EC_LOG_LEVEL_VERBOSE, "myAppAoeReadWriteObjectCallback(%d, %d, %d, 0x%04X, 0x%04X, read %d bytes, write %d bytes, 0x%08X, 0x%08X) = %d (0x%08X): %s (0x%08X)\n",
        dwSimulatorId, wCfgFixedAddress, wTargetPort,
        dwIndexGroup, dwIndexOffset, EC_GETDWORD(pdwReadDataLen), dwWriteDataLen, EC_GETDWORD(pdwErrorCode), EC_GETDWORD(pdwCmdResult),
        dwVal, dwVal,
        esGetText(dwSimulatorId, EC_GETDWORD(pdwErrorCode)), EC_GETDWORD(pdwErrorCode)));
    return EC_GETDWORD(pdwErrorCode);
}

The following example demonstrates objects transfers between Master and Simulator:

    EC_T_DWORD dwRes = EC_E_ERROR;
    EC_T_DWORD dwRetVal = EC_E_ERROR;
    EC_T_BYTE  abyWriteData[sizeof(EC_T_DWORD)];
    EC_T_BYTE  abyReadData[sizeof(EC_T_DWORD)];
    EC_T_BYTE  abyReadWriteData[sizeof(EC_T_DWORD)];
    EC_T_DWORD dwDataOutLen = 0;
    EC_T_AOE_NETID oAoeNetId;
    EC_T_DWORD dwSlaveId = emGetSlaveId(dwMasterId, wSlaveAddress);
    EC_T_DWORD dwErrorCode = EC_E_ERROR;
    EC_T_DWORD dwCmdResult = EC_E_ERROR;

    OsMemset(abyWriteData, 0, sizeof(abyWriteData));
    OsMemset(abyReadData, 0, sizeof(abyReadData));
    OsMemset(abyReadWriteData, 0, sizeof(abyReadWriteData));

    /* EC-Master: get configured AoE net ID */
    dwRes = emAoeGetSlaveNetId(dwMasterId, dwSlaveId, &oAoeNetId);
    if (dwRes != EC_E_NOERROR)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "emAoeGetSlaveNetId failed: %s (0x%lx))\n", ecatGetText(dwRes), dwRes));
        dwRetVal = dwRes;
        goto Exit;
    }

    /* EC-Simulator: register AoE handlers */
    dwRes = esSetSlaveAoeObjectTransferCallbacks(dwSimulatorId, wSlaveAddress,
        myAppAoeReadObjectCallback, myAppAoeWriteObjectCallback, myAppAoeReadWriteObjectCallback, pAppContext);
    if (dwRes != EC_E_NOERROR)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "esSetSlaveAoeObjectTransferCallbacks failed: %s (0x%lx))\n", esGetText(dwSimulatorId, dwRes), dwRes));
        dwRetVal = dwRes;
        goto Exit;
    }

    /* EC-Master: set PREOP state */
    dwRes = emSetMasterState(dwMasterId, 30000, eEcatState_PREOP);
    if (dwRes != EC_E_NOERROR)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "emSetMasterState failed: %s (0x%lx))\n", ecatGetText(dwRes), dwRes));
        dwRetVal = dwRes;
        goto Exit;
    }

#define AOE_TARGET_PORT     65535
#define AOE_INDEX_GROUP     0xF302  /* ADSIGRP_CANOPEN_SDO */

    /* CoE objects to access via AoE */
#define AOE_COE_OBJ_IDX            0x2003
#define AOE_COE_OBJ_SUBINDEX       0
#define AOE_COE_OBJ_COMPLETEACCESS EC_FALSE

    /* Bit 16-31: index, Bit 8: complete access, Bit 0-7: subindex */
#define AOE_INDEX_OFFSET ((AOE_COE_OBJ_IDX << 16) | (AOE_COE_OBJ_COMPLETEACCESS << 8) | AOE_COE_OBJ_SUBINDEX)

    /* EC-Master: write AoE to slave */
    EC_SET_FRM_DWORD(abyWriteData, 0x11223344 /* example value */);
    dwRes = emAoeWrite(dwMasterId, dwSlaveId, &oAoeNetId, AOE_TARGET_PORT, AOE_INDEX_GROUP, AOE_INDEX_OFFSET,
        sizeof(EC_T_DWORD), abyWriteData, &dwErrorCode, &dwCmdResult, MBX_TIMEOUT);
    if (dwRes != EC_E_NOERROR)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "emAoeWrite failed: %s (0x%lx))\n", ecatGetText(dwRes), dwRes));
        dwRetVal = dwRes;
        goto Exit;
    }

    /* EC-Master: read AoE from slave */
    EC_SET_FRM_DWORD(abyReadData, 0);
    dwRes = emAoeRead(dwMasterId, dwSlaveId, &oAoeNetId, AOE_TARGET_PORT, AOE_INDEX_GROUP, AOE_INDEX_OFFSET,
        sizeof(EC_T_DWORD), abyReadData, &dwDataOutLen, &dwErrorCode, &dwCmdResult, MBX_TIMEOUT);
    if (dwRes != EC_E_NOERROR)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "emAoeRead failed: %s (0x%lx))\n", ecatGetText(dwRes), dwRes));
        dwRetVal = dwRes;
        goto Exit;
    }

    /* EC-Master: read + write AoE from/to slave */
    EC_SET_FRM_DWORD(abyReadWriteData, 0x11223344 /* example value */);
    dwRes = emAoeReadWrite(dwMasterId, dwSlaveId, &oAoeNetId, AOE_TARGET_PORT, AOE_INDEX_GROUP, AOE_INDEX_OFFSET,
        sizeof(EC_T_DWORD), sizeof(EC_T_DWORD), abyReadWriteData, &dwDataOutLen, &dwErrorCode, &dwCmdResult, MBX_TIMEOUT);
    if (dwRes != EC_E_NOERROR)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "emAoeReadWrite failed: %s (0x%lx))\n", ecatGetText(dwRes), dwRes));
        dwRetVal = dwRes;
        goto Exit;
    }

    /* EC-Simulator: write AoE to slave */
    EC_SET_FRM_DWORD(abyWriteData, 0x11223344 /* example value */);
    dwRes = esAoeWrite(dwSimulatorId, dwSlaveId, &oAoeNetId, AOE_TARGET_PORT, AOE_INDEX_GROUP, AOE_INDEX_OFFSET,
        sizeof(EC_T_DWORD), abyWriteData, &dwErrorCode, &dwCmdResult, MBX_TIMEOUT);
    if (dwRes != EC_E_NOERROR)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "emAoeWrite failed: %s (0x%lx))\n", ecatGetText(dwRes), dwRes));
        dwRetVal = dwRes;
        goto Exit;
    }

    /* EC-Simulator: read AoE from slave */
    EC_SET_FRM_DWORD(abyReadData, 0);
    dwRes = esAoeRead(dwSimulatorId, dwSlaveId, &oAoeNetId, AOE_TARGET_PORT, AOE_INDEX_GROUP, AOE_INDEX_OFFSET,
        sizeof(EC_T_DWORD), abyReadData, &dwDataOutLen, &dwErrorCode, &dwCmdResult, MBX_TIMEOUT);
    if (dwRes != EC_E_NOERROR)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "emAoeRead failed: %s (0x%lx))\n", ecatGetText(dwRes), dwRes));
        dwRetVal = dwRes;
        goto Exit;
    }

    /* EC-Simulator: read + write AoE from/to slave */
    EC_SET_FRM_DWORD(abyReadWriteData, 0x11223344 /* example value */);
    dwRes = esAoeReadWrite(dwSimulatorId, dwSlaveId, &oAoeNetId, AOE_TARGET_PORT, AOE_INDEX_GROUP, AOE_INDEX_OFFSET,
        sizeof(EC_T_DWORD), sizeof(EC_T_DWORD), abyReadWriteData, &dwDataOutLen, &dwErrorCode, &dwCmdResult, MBX_TIMEOUT);
    if (dwRes != EC_E_NOERROR)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "emAoeReadWrite failed: %s (0x%lx))\n", ecatGetText(dwRes), dwRes));
        dwRetVal = dwRes;
        goto Exit;
    }

6.11. CAN application protocol over EtherCAT (CoE)

To handle CoE object transfers within the application, the callbacks pfnCoeRead and/or pfnCoeWrite can be registered using esSetSlaveCoeObjectTransferCallbacks() or esSetSlaveSscApplication(), e.g. in myAppPrepare() of EcSimulatorHilDemo / EcSimulatorSilDemo.

See also CoE transfer Simulator and Master Example

6.11.1. esExtendSlaveCoeObjectDictionary

EC_T_DWORD esExtendSlaveCoeObjectDictionary(EC_T_DWORD dwInstanceId, EC_T_WORD wCfgFixedAddress, struct _EC_T_COE_DICTIONARY_DESC *pDict)

Add data types and / or objects to slave’s CoE object dictionary.

Parameters
  • dwInstanceId – [in] Simulator Instance ID

  • wCfgFixedAddress – [in] Slave’s station address

  • pDict – [in] Pointer to dictionary descriptor

Returns

EC_E_NOERROR or error code

6.11.2. esDeleteSlaveCoeObject

EC_T_DWORD esDeleteSlaveCoeObject(EC_T_DWORD dwInstanceId, EC_T_WORD wCfgFixedAddress, EC_T_WORD wObjectIndex)

Delete object from slave’s CoE object dictionary.

Parameters
  • dwInstanceId – [in] Simulator Instance ID

  • wCfgFixedAddress – [in] Slave’s station address

  • wObjectIndex – [in] Object index

Returns

EC_E_NOERROR or error code

6.11.3. esClearSlaveCoeObjectDictionary

EC_T_DWORD esClearSlaveCoeObjectDictionary(EC_T_DWORD dwInstanceId, EC_T_WORD wCfgFixedAddress)

Delete all objects from slave’s CoE object dictionary.

Parameters
  • dwInstanceId – [in] Simulator Instance ID

  • wCfgFixedAddress – [in] Slave’s station address

Returns

EC_E_NOERROR or error code

6.11.4. esResetSlaveCoeObjectDictionary

EC_T_DWORD esResetSlaveCoeObjectDictionary(EC_T_DWORD dwInstanceId, EC_T_WORD wCfgFixedAddress)

Reset all objects from slave’s CoE object dictionary to default.

Parameters
  • dwInstanceId – [in] Simulator Instance ID

  • wCfgFixedAddress – [in] Slave’s station address

Returns

EC_E_NOERROR or error code

6.11.5. esSetSlaveCoeObjectTransferCallbacks

EC_T_DWORD esSetSlaveCoeObjectTransferCallbacks(EC_T_DWORD dwInstanceId, EC_T_WORD wCfgFixedAddress, EC_T_WORD wObjectIndex, EC_T_PF_COE_READ_CB pfRead, EC_T_PF_COE_WRITE_CB pfWrite, EC_T_VOID *pvContext)

Set SDO upload / download transfer callbacks.

Note

: pfRead / pfWrite can also be registered if object dictionary unavailable at EC-Simulator. esClearSlaveCoeObjectDictionary() has to be called first. See esClearSlaveCoeObjectDictionary(). In case of using an ENI and the simulator’s default dictionary wObjectIndex must be set to 0xFFFF.

Parameters
  • dwInstanceId[in] Simulator Instance ID

  • wCfgFixedAddress[in] Slave’s station address. 0: All slaves

  • wObjectIndex[in] Object index. 0xFFFF: all objects

  • pfRead[in] Upload callback function. EC_NULL: clears callback if already registered

  • pfWrite[in] Download callback function. EC_NULL: clears callback if already registered

  • pvContext[in] Arbitrarily application-defined parameter passed to transfer callback functions

Returns

typedef EC_T_VOID (*EC_T_PF_COE_READ_CB)(EC_T_VOID *pvContext, EC_T_DWORD dwSimulatorId, EC_T_WORD wCfgFixedAddress, EC_T_WORD wIndex, EC_T_BYTE bySubindex, EC_T_DWORD dwSize, EC_T_BYTE *pbyData, EC_T_DWORD *pdwOutDataLen, EC_T_BOOL bCompleteAccess, EC_T_BOOL bCheckReadAccess, EC_T_DWORD *pdwErrorCode)

CoE Read Object Callback: Handle read requests from the master in the simulator application. The simulator has pre-handled the request and sets pdwErrorCode accordingly.

Parameters
  • pvContext[in] Arbitrarily application-defined parameter passed to callback

  • dwSimulatorId[in] Simulator Instance ID

  • wCfgFixedAddress[in] Slave fixed address from master request

  • wIndex[in] Object Index from master request, e.g. 0x1018

  • bySubindex[in] Object SubIndex from master request

  • dwSize[in] Read buffer size according to MbxIn Sync Manager size

  • pbyData[inout] Read data buffer: pre-filled by the simulator if the object exists in the simulated slave’s object dictionary and is read-able in the current EtherCAT state

  • pdwOutDataLen[inout] Read data length: pre-filled by the simulator if the object exists in the simulated slave’s object dictionary and is read-able in the current EtherCAT state, else the simulator application shall set the value accordingly

  • bCompleteAccess[in] EC_TRUE: CoE SDO Upload with Complete Access. See also EC_MAILBOX_FLAG_SDO_COMPLETE

  • bCheckReadAccess[in] EC_TRUE: The simulator application should check the read access, e.g. if the object does not exist in the simulated slave’s object dictionary

  • pdwErrorCode[inout] Current transfer error code pre-filled by the simulator. The simulator application shall set the current transfer error accordingly within the callback.

typedef EC_T_VOID (*EC_T_PF_COE_WRITE_CB)(EC_T_VOID *pvContext, EC_T_DWORD dwSimulatorId, EC_T_WORD wCfgFixedAddress, EC_T_WORD wIndex, EC_T_BYTE bySubindex, EC_T_DWORD dwSize, EC_T_BYTE *pbyData, EC_T_BOOL bCompleteAccess, EC_T_BOOL bCheckWriteAccess, EC_T_DWORD *pdwErrorCode)

CoE Write Object Callback: Handle write requests from the master in the simulator application. The simulator has pre-handled the request and sets pdwErrorCode accordingly.

Parameters
  • pvContext[in] Arbitrarily application-defined parameter passed to callback

  • dwSimulatorId[in] Simulator Instance ID

  • wCfgFixedAddress[in] Slave fixed address from master request

  • wIndex[in] Object Index from master request, e.g. 0x40A2

  • bySubindex[in] Object SubIndex from master request

  • dwSize[in] Write data length

  • pbyData[inout] Write data buffer: pre-filled by the simulator if the object exists in the simulated slave’s object dictionary and is write-able in the current EtherCAT state

  • bCompleteAccess[in] EC_TRUE: CoE SDO Download with Complete Access. See also EC_MAILBOX_FLAG_SDO_COMPLETE

  • bCheckWriteAccess[in] EC_TRUE: The simulator application should check the write access, e.g. if the object does not exist in the simulated slave’s object dictionary

  • pdwErrorCode[inout] Current transfer error code pre-filled by the simulator. The simulator application shall set the current transfer error accordingly within the callback.

6.11.6. esCoeSdoDownload

EC_T_DWORD esCoeSdoDownload(EC_T_DWORD dwInstanceID, EC_T_DWORD dwSlaveId, EC_T_WORD wObIndex, EC_T_BYTE byObSubIndex, EC_T_BYTE *pbyData, EC_T_DWORD dwDataLen, EC_T_DWORD dwTimeout, EC_T_DWORD dwFlags)

Execute a CoE SDO download to an EtherCAT slave device.

This function may not be called from within the JobTask’s context.

Parameters
  • dwInstanceID – [in] Instance ID (Multiple EtherCAT Network Support)

  • dwSlaveId – [in] Slave ID

  • wObIndex – [in] Object Index

  • byObSubIndex – [in] Object SubIndex. If Complete Access only 0 or 1 allowed

  • pbyData – [in] Buffer containing transferred data

  • dwDataLen – [in] Buffer length in bytes

  • dwTimeout – [in] Timeout [ms]

  • dwFlags – [in] Mailbox Flags. Bit 0: set if Complete Access (EC_MAILBOX_FLAG_SDO_COMPLETE).

Returns

6.11.7. esCoeSdoUpload

EC_T_DWORD esCoeSdoUpload(EC_T_DWORD dwInstanceID, EC_T_DWORD dwSlaveId, EC_T_WORD wObIndex, EC_T_BYTE byObSubIndex, EC_T_BYTE *pbyData, EC_T_DWORD dwDataLen, EC_T_DWORD *pdwOutDataLen, EC_T_DWORD dwTimeout, EC_T_DWORD dwFlags)

Execute a CoE SDO upload from an EtherCAT slave device to the master.

This function may not be called from within the JobTask’s context.

Parameters
  • dwInstanceID – [in] Instance ID (Multiple EtherCAT Network Support)

  • dwSlaveId – [in] Slave ID

  • wObIndex – [in] Object index

  • byObSubIndex – [in] Object SubIndex. If Complete Access only 0 or 1 allowed

  • pbyData – [out] Buffer receiving transferred data

  • dwDataLen – [in] Buffer length in bytes

  • pdwOutDataLen – [out] Length of received data in bytes

  • dwTimeout – [in] Timeout [ms]

  • dwFlags – [in] Mailbox Flags. Bit 0: set if Complete Access (EC_MAILBOX_FLAG_SDO_COMPLETE).

Returns

6.11.8. esCoeGetODListReq

EC_T_DWORD esCoeGetODListReq(EC_T_DWORD dwInstanceID, EC_T_MBXTFER *pMbxTfer, EC_T_DWORD dwSlaveId, EC_T_COE_ODLIST_TYPE eListType, EC_T_DWORD dwTimeout)

Gets a list of object IDs that are available in a slave.

A unique transfer ID must be written into EC_T_MBXTFER.dwTferId. EC_NOTIFY_MBOXRCV is given on completion.

This function may not be called from within the JobTask’s context.

Note

The mailbox transfer object will receive the slave response containing the list type followed by the list itself. Therefore the buffer must be 2 bytes bigger than the expected list size.

Parameters
  • dwInstanceID – [in] Instance ID (Multiple EtherCAT Network Support)

  • pMbxTfer – [in] Mailbox transfer

  • dwSlaveId – [in] Slave ID

  • eListType – [in] which object types shall be transferred

  • dwTimeout – [in] Timeout [ms]. The function will block at most for this time. If the timeout value is set to EC_NOWAIT the function will return immediately.

Returns

6.11.9. esCoeGetObjectDescReq

EC_T_DWORD esCoeGetObjectDescReq(EC_T_DWORD dwInstanceID, EC_T_MBXTFER *pMbxTfer, EC_T_DWORD dwSlaveId, EC_T_WORD wObIndex, EC_T_DWORD dwTimeout)

Determines the description of a specific object.

A unique transfer ID must be written into EC_T_MBXTFER.dwTferId. EC_NOTIFY_MBOXRCV is given on completion. This function may not be called from within the JobTask’s context.

Parameters
  • dwInstanceID – [in] Instance ID (Multiple EtherCAT Network Support)

  • pMbxTfer – [in] Mailbox transfer object

  • dwSlaveId – [in] Slave ID

  • wObIndex – [in] Object index

  • dwTimeout – [in] Timeout [ms]. The function will block at most for this time. If the timeout value is set to EC_NOWAIT the function will return immediately.

Returns

struct EC_T_COE_OBDESC

Public Members

EC_T_WORD wObIndex

Index in the object dictionary

EC_T_WORD wDataType

Data type of the object

EC_T_BYTE byObjCode

Object code, see Table 62, ETG.1000 section 6

EC_T_BYTE byObjCategory

Object category

EC_T_BYTE byMaxNumSubIndex

Maximum sub index number

EC_T_WORD wObNameLen

Length of the object name

EC_T_WORD wStationAddress

Station address of the slave

EC_T_CHAR *pchObName

Object name (not NULL terminated!)

6.11.10. esCoeGetEntryDescReq

EC_T_DWORD esCoeGetEntryDescReq(EC_T_DWORD dwInstanceID, EC_T_MBXTFER *pMbxTfer, EC_T_DWORD dwSlaveId, EC_T_WORD wObIndex, EC_T_BYTE byObSubIndex, EC_T_BYTE byValueInfo, EC_T_DWORD dwTimeout)

Determines the description of a specific object entry.

A unique transfer ID must be written into EC_T_MBXTFER.dwTferId. EC_NOTIFY_MBOXRCV is given on completion. This function may not be called from within the JobTask’s context.

Parameters
  • dwInstanceID – [in] Instance ID (Multiple EtherCAT Network Support)

  • pMbxTfer – [in] Mailbox transfer object

  • dwSlaveId – [in] Slave ID

  • wObIndex – [in] Object Index

  • byObSubIndex – [in] Object SubIndex

  • byValueInfo – [in] The value info bit mask includes which elements shall be in the response. See Value info flags for available values.

  • dwTimeout – [in] Timeout [ms]. The function will block at most for this time. If the timeout value is set to EC_NOWAIT the function will return immediately

Returns

struct EC_T_COE_ENTRYDESC

Public Members

EC_T_WORD wObIndex

Index in the object dictionary

EC_T_BYTE byObSubIndex

Sub index in the object dictionary

EC_T_BYTE byValueInfo

Bit mask which information is included in pbyData. See Value info flags

EC_T_WORD wDataType

Object data type according to ETG.1000

EC_T_WORD wBitLen

Object size (number of bits)

EC_T_BYTE byObAccess

Access rights. See Object access flags

EC_T_BOOL bRxPdoMapping

Object is mappable in a RxPDO

EC_T_BOOL bTxPdoMapping

Object is mappable in a TxPDO

EC_T_BOOL bObCanBeUsedForBackup

Object can be used for backup

EC_T_BOOL bObCanBeUsedForSettings

Object can be used for settings

EC_T_WORD wStationAddress

Station address of the slave

EC_T_WORD wDataLen

Size of the remaining object data

EC_T_BYTE *pbyData

Remaining object data: dwUnitType, pbyDefaultValue, pbyMinValue, pbyMaxValue, pbyDescription

(see ETG.1000.5 and ETG.1000.6)

See szUnitType, szDefaultValue, szMinValue, szMaxValue, szDescription in CoeReadObjectDictionary() in EcSdoServices.cpp as an example for evaluating EC_T_COE_ENTRYDESC.pbyData.

6.11.11. CoE transfer Simulator and Master Example

The following example demonstrates how to handle CoE object transfers within the simulator application:

CoE transfer Simulator and Master Example

The following example handlers can be registered at the EC-Simulator:

/* Length check in application if Object Dictionary unavailable at EC-Simulator */
static EC_T_DWORD myAppCoeObjectGetDataLen(
    EC_T_VOID* /* pvContext */, EC_T_WORD /* wCfgFixedAddress */,
    EC_T_WORD wObjectIndex, EC_T_BYTE bySubindex,
    EC_T_BOOL /* bCompleteAccess */, EC_T_DWORD* pdwDataLen)
{
    EC_T_DWORD dwRetVal = EC_E_SDO_ABORTCODE_INDEX;

    /* example for 0x1018 Identity Object, SubIndex 1 Vendor ID */
    if ((0x1018 == wObjectIndex) && (1 == bySubindex))
    {
        *pdwDataLen = 4;
        dwRetVal = EC_E_NOERROR;
    }

    return dwRetVal;
}

/* EC_T_PF_COE_READ_CB */
static EC_T_VOID EC_FNCALL myAppCoeReadObjectCallback(
    EC_T_VOID* pvContext, EC_T_DWORD /* dwSimulatorId */, EC_T_WORD wCfgFixedAddress,
    EC_T_WORD wObjectIndex, EC_T_BYTE bySubindex, 
    EC_T_DWORD dwBufSize, EC_T_BYTE* pbyData, EC_T_DWORD* pdwOutDataLen, 
    EC_T_BOOL bCompleteAccess, 
    EC_T_BOOL bCheckReadAccess /* EC_TRUE: Object Dictionary unavailable at EC-Simulator */,
    EC_T_DWORD* pdwErrorCode /* see EC_E_SDO_ABORTCODE_... */)
{
    EC_T_DWORD dwDataLen = *pdwOutDataLen;

    /* check transfer parameters in application if Object Dictionary unavailable at EC-Simulator */
    if (bCheckReadAccess)
    {
        *pdwErrorCode = myAppCoeObjectGetDataLen(pvContext, wCfgFixedAddress, wObjectIndex, bySubindex, bCompleteAccess, &dwDataLen);
        if ((EC_E_NOERROR == *pdwErrorCode) && (dwBufSize < dwDataLen))
        {
            *pdwErrorCode = EC_E_SDO_ABORTCODE_DATA_LENGTH_TOO_LOW;
        }
    }
    if (EC_E_NOERROR == *pdwErrorCode)
    {
        /* provide data. example for 0x1018 Identity Object, SubIndex 1 Vendor ID */
        if ((0x1018 == wObjectIndex) && (1 == bySubindex))
        {
            OsDbgAssert(4 == dwDataLen); EC_SET_FRM_DWORD((EC_T_DWORD*)pbyData, 0x12345678);
        }
    }
    else
    {
        dwDataLen = 0;
    }

    *pdwOutDataLen = dwDataLen;
    return;
}

/* EC_T_PF_COE_WRITE_CB */
static EC_T_VOID EC_FNCALL myAppCoeWriteObjectCallback(
    EC_T_VOID* /* pvContext */, EC_T_DWORD /* dwSimulatorId */, EC_T_WORD /* wCfgFixedAddress */,
    EC_T_WORD wObjectIndex, EC_T_BYTE bySubindex,
    EC_T_DWORD dwSize, EC_T_BYTE* pbyData,
    EC_T_BOOL /* bCompleteAccess */,
    EC_T_BOOL bCheckWriteAccess /* EC_TRUE if Object Dictionary unavailable at EC-Simulator */,
    EC_T_DWORD* pdwErrorCode)
{
    OsDbgAssert(!bCheckWriteAccess /* Object Dictionary available */
                || (EC_E_SDO_ABORTCODE_INDEX == *pdwErrorCode));

    if ((0x2000 == wObjectIndex) && (0 == bySubindex))
    {
        /* check write access if Object Dictionary unavailable at EC-Simulator */
        if (bCheckWriteAccess && (2 != dwSize))
        {
            if (dwSize > 2)
            {
                *pdwErrorCode = EC_E_SDO_ABORTCODE_DATA_LENGTH_TOO_HIGH;
            }
            else
            {
                *pdwErrorCode = EC_E_SDO_ABORTCODE_DATA_LENGTH_TOO_LOW;
            }
            return;
        }
        /* handle data, e.g. myCoeSdoWrite(wObjectIndex, bySubindex, pbyData); */
        EcLogMsg(EC_LOG_LEVEL_INFO, (pEcLogContext, EC_LOG_LEVEL_INFO, "myAppCoeWriteObjectCallback: 0x%04X, %d: 0x%04X)\n", wObjectIndex, bySubindex, EC_GET_FRM_WORD(pbyData)));

        *pdwErrorCode = EC_E_NOERROR;
        return;
    }
}

The following example demonstrates objects transfers between Master and Simulator:

    EC_T_DWORD dwRes = EC_E_ERROR;
    EC_T_DWORD dwRetVal = EC_E_ERROR;
    EC_T_BYTE  abyWriteData[sizeof(EC_T_WORD)];
    EC_T_BYTE  abyReadData[sizeof(EC_T_DWORD)];
    EC_T_DWORD dwDataOutLen = 0;

    OsMemset(abyWriteData, 0, sizeof(abyWriteData));
    OsMemset(abyReadData, 0, sizeof(abyReadData));

    /* EC-Simulator: clear Object Dictionary */
    dwRes = esClearSlaveCoeObjectDictionary(dwSimulatorId, wSlaveAddress);
    if (dwRes != EC_E_NOERROR)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "esClearSlaveCoeObjectDictionary failed: %s (0x%lx))\n", esGetText(dwSimulatorId, dwRes), dwRes));
        dwRetVal = dwRes;
        goto Exit;
    }

    /* EC-Simulator: register CoE handlers */
    dwRes = esSetSlaveCoeObjectTransferCallbacks(dwSimulatorId, wSlaveAddress, 0xFFFF /* all objects */,
                                                 myAppCoeReadObjectCallback, myAppCoeWriteObjectCallback, pAppContext);
    if (dwRes != EC_E_NOERROR)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "esSetSlaveCoeObjectTransferCallbacks failed: %s (0x%lx))\n", esGetText(dwSimulatorId, dwRes), dwRes));
        dwRetVal = dwRes;
        goto Exit;
    }

    /* EC-Master: set PREOP state */
    dwRes = emSetMasterState(dwMasterId, 30000, eEcatState_PREOP);
    if (dwRes != EC_E_NOERROR)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "emSetMasterState failed: %s (0x%lx))\n", ecatGetText(dwRes), dwRes));
        dwRetVal = dwRes;
        goto Exit;
    }

    /* EC-Master: write CoE to slave */
    EC_SET_FRM_WORD(abyWriteData, 0x1234 /* example value */);
    dwRes = emCoeSdoDownload(dwMasterId, emGetSlaveId(dwMasterId, wSlaveAddress), 0x2000, 0, abyWriteData, sizeof(EC_T_WORD), 5000 /* timeout */, 0);
    if (dwRes != EC_E_NOERROR)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "emCoeSdoDownload failed: %s (0x%lx))\n", ecatGetText(dwRes), dwRes));
        dwRetVal = dwRes;
        goto Exit;
    }

    /* EC-Master: read CoE from slave */
    EC_SET_FRM_DWORD(abyReadData, 0);
    dwRes = emCoeSdoUpload(dwMasterId, emGetSlaveId(dwMasterId, wSlaveAddress), 0x1018, 1 /* vendor id */, abyReadData, sizeof(EC_T_DWORD), &dwDataOutLen, 5000 /* timeout */, 0);
    if (dwRes != EC_E_NOERROR)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "emCoeSdoUpload failed: %s (0x%lx))\n", ecatGetText(dwRes), dwRes));
        dwRetVal = dwRes;
        goto Exit;
    }

    /* EC-Simulator: write CoE to slave */
    EC_SET_FRM_WORD(abyWriteData, 0x1234 /* example value */);
    dwRes = esCoeSdoDownload(dwSimulatorId, esGetSlaveId(dwSimulatorId, wSlaveAddress), 0x2000, 0, abyWriteData, sizeof(EC_T_WORD), 5000 /* timeout */, 0);
    if (dwRes != EC_E_NOERROR)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "esCoeSdoDownload failed: %s (0x%lx))\n", ecatGetText(dwRes), dwRes));
        dwRetVal = dwRes;
        goto Exit;
    }

    /* EC-Simulator: read CoE from slave */
    EC_SET_FRM_DWORD(abyReadData, 0);
    dwRes = esCoeSdoUpload(dwSimulatorId, esGetSlaveId(dwSimulatorId, wSlaveAddress), 0x1018, 1 /* vendor id */, abyReadData, sizeof(EC_T_DWORD), &dwDataOutLen, 5000 /* timeout */, 0);
    if (dwRes != EC_E_NOERROR)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "emCoeSdoUpload failed: %s (0x%lx))\n", ecatGetText(dwRes), dwRes));
        dwRetVal = dwRes;
        goto Exit;
    }

6.12. Ethernet over EtherCAT (EoE)

To handle EoE frames within the application, the callback pfnEoeReceive must be registered using esSetSlaveSscApplication(), e.g. in myAppPrepare() of EcSimulatorHilDemo / EcSimulatorSilDemo.

See also EoE Ping Example

6.12.1. esEoeSendFrame

EC_T_DWORD esEoeSendFrame(EC_T_DWORD dwInstanceId, EC_T_WORD wCfgFixedAddress, EC_T_BYTE *pbyFrame, EC_T_DWORD dwFrameLen)

Send EoE frame (queued)

Returns

EC_E_NOERROR or error code

6.12.2. esGetCfgSlaveEoeInfo

EC_T_DWORD esGetCfgSlaveEoeInfo(EC_T_DWORD dwInstanceID, EC_T_BOOL bFixedAddressing, EC_T_WORD wSlaveAddress, EC_T_CFG_SLAVE_EOE_INFO *pSlaveEoeInfo)

Return EoE information about a configured slave from the ENI file.

Parameters
  • dwInstanceID – [in] Instance ID (Multiple EtherCAT Network Support)

  • bFixedAddressing – [in] EC_TRUE: use station address, EC_FALSE: use AutoInc address

  • wSlaveAddress – [in] Slave address according bFixedAddressing

  • pSlaveEoeInfo – [out] Information about the slave

Returns

Content of EC_T_CFG_SLAVE_EOE_INFO is subject to be extended.

struct EC_T_CFG_SLAVE_EOE_INFO

Public Members

EC_T_DWORD dwSlaveId

[out] Slave ID

EC_T_BOOL bMacAddr

[out] Indicates whether the MAC address could be read and is valid

EC_T_BYTE abyMacAddr[6]

[out] MAC address

EC_T_BOOL bIpAddr

[out] Indicates whether the IP address could be read and is valid

EC_T_IPADDR oIpAddr

[out] IP address

EC_T_BOOL bSubnetMask

[out] Indicates whether the subnet mask could be read and is valid

EC_T_IPADDR oSubnetMask

[out] Subnet mask

EC_T_BOOL bDefaultGateway

[out] Indicates whether the default gateway could be read and is valid

EC_T_IPADDR oDefaultGateway

[out] Default gateway

EC_T_BOOL bDnsServer

[out] Indicates whether the DNS server could be read and is valid

EC_T_IPADDR oDnsServer

[out] DNS server

EC_T_BOOL bDnsName

[out] Indicates whether the DNS name could be read and is valid

EC_T_CHAR szDnsName[32]

[out] DNS name

esGetCfgSlaveEoeInfo() Example
    EC_T_CFG_SLAVE_EOE_INFO oInfo;
    OsMemset(&oInfo, 0, sizeof(EC_T_CFG_SLAVE_EOE_INFO));

    dwRes = esGetCfgSlaveEoeInfo(pAppContext->dwInstanceId, EC_TRUE, wSlaveAddress, &oInfo);
    if (dwRes != EC_E_NOERROR)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "esGetCfgSlaveEoeInfo failed: %s (0x%lx))\n", 
            esGetText(pAppContext->dwInstanceId, dwRes), dwRes));
        dwRetVal = dwRes;
        goto Exit;
    }

    EcLogMsg(EC_LOG_LEVEL_INFO, (pEcLogContext, EC_LOG_LEVEL_INFO, "esGetCfgSlaveEoeInfo(%d): "
        "MAC address: %02X:%02X:%02X:%02X:%02X:%02X, IP address: %d.%d.%d.%d, subnet mask: %d.%d.%d.%d, "
        "default gateway: %d.%d.%d.%d, DNS server: %d.%d.%d.%d, DNS name: %s\n", wSlaveAddress,
        oInfo.abyMacAddr[0], oInfo.abyMacAddr[1], oInfo.abyMacAddr[2],
        oInfo.abyMacAddr[3], oInfo.abyMacAddr[4], oInfo.abyMacAddr[5],

        oInfo.oIpAddr.sAddr.by[0], oInfo.oIpAddr.sAddr.by[1], oInfo.oIpAddr.sAddr.by[2], oInfo.oIpAddr.sAddr.by[3],
        oInfo.oSubnetMask.sAddr.by[0], oInfo.oSubnetMask.sAddr.by[1], oInfo.oSubnetMask.sAddr.by[2], oInfo.oSubnetMask.sAddr.by[3],

        oInfo.oDefaultGateway.sAddr.by[0], oInfo.oDefaultGateway.sAddr.by[1], oInfo.oDefaultGateway.sAddr.by[2], oInfo.oDefaultGateway.sAddr.by[3],
        oInfo.oDnsServer.sAddr.by[0], oInfo.oDnsServer.sAddr.by[1], oInfo.oDnsServer.sAddr.by[2], oInfo.oDnsServer.sAddr.by[3], oInfo.szDnsName));

6.12.3. EoE Ping Example

The following example demonstrates how to customize EoE simulation using esSetSlaveSscApplication() and esEoeSendFrame().

EoE Ping Example

The following code demonstrates how to receive EoE frames from the Master and send answers back.

/********************************************************************************/
/** \brief  EoE ARP request and PING request context structure
*/
typedef struct _T_MY_EOE_CONTEXT
{
    T_EC_DEMO_APP_CONTEXT* pAppContext;
    EC_T_WORD              wSlaveAddress;
    ETHERNET_ADDRESS       oMacAddress;
    EC_T_IPADDR            oIpV4Address;
    EC_T_LINK_FRAMEDESC    oSendFrame;
} T_MY_EOE_CONTEXT;

/********************************************************************************/
/** \brief  EoE ARP request and PING request handler
*
* \return  pReplyFrameDesc->dwSize > 0 if handled with reply
*/
static EC_T_VOID myAppProcessEoeFrameArpAndPing(EC_T_LINK_FRAMEDESC* pRequestFrameDesc, EC_T_LINK_FRAMEDESC* pReplyFrameDesc, ETHERNET_ADDRESS MacAddress, EC_T_IPADDR IpV4Address)
{
    ETHERNET_FRAME* pRequest = (ETHERNET_FRAME*)pRequestFrameDesc->pbyFrame;
    ETHERNET_FRAME* pReply = (ETHERNET_FRAME*)pReplyFrameDesc->pbyFrame;

    /* prepare reply */
    OsMemcpy(pReply, pRequest, pRequestFrameDesc->dwSize);
    pReply->Destination = pRequest->Source;
    pReply->Source = MacAddress;
    pReplyFrameDesc->dwSize = 0;

    /* handle ARP / ping */
    switch (EC_ETHFRM_GET_FRAMETYPE(pRequest))
    {
    case ETHERNET_FRAME_TYPE_ARP:
    {
        EC_ARP_IP_HEADER* pArpRequest = (EC_ARP_IP_HEADER*)EC_ENDOF(pRequest); /* skip ETHERNET_FRAME header */
        EC_ARP_IP_HEADER* pArpReply = (EC_ARP_IP_HEADER*)EC_ENDOF(pReply); /* skip ETHERNET_FRAME header */

        /* only Ethernet MAC and IPv4 ARP requests supported */
        if ((EC_NTOHS(pArpRequest->wHwAddressType) == EC_ARP_HW_TYPE_ETHERNET)
            && (EC_NTOHS(pArpRequest->wProtocolType) == ETHERNET_FRAME_TYPE_IP)
            && (pArpRequest->byHwAddressLength == ETHERNET_ADDRESS_LEN)
            && (pArpRequest->byProtocolAddressLength == EC_IPv4_ADDRESS_LEN)
            && (EC_NTOHS(pArpRequest->wOpCode) == EC_ARP_OPCODE_REQUEST))
        {
            /* only answer ARP request if IP address matches */
            if ((BroadcastEthernetAddress == pRequest->Destination)
                && (pArpRequest->Address.IpV4.DestinationIp == IpV4Address))
            {
                pArpReply->Address.IpV4.DestinationMac = pArpRequest->Address.IpV4.SourceMac;
                pArpReply->Address.IpV4.SourceMac = MacAddress;

                pArpReply->Address.IpV4.DestinationIp = pArpRequest->Address.IpV4.SourceIp;
                pArpReply->Address.IpV4.SourceIp = IpV4Address;

                pArpReply->wOpCode = EC_HTONS(EC_ARP_OPCODE_REPLY);

                /* reply valid */
                pReplyFrameDesc->dwSize = ETHERNET_FRAME_LEN + EC_ARP_IPv4_HEADER_LEN;
            }
        }
    } break;
    case ETHERNET_FRAME_TYPE_IP:
    {
        if (((EC_IP_HEADER*)EC_ENDOF(pRequest))->byProtocol == EC_IP_PROTOCOL_ICMP)
        {
            EC_ICMP_HEADER* pIcmpRequest = (EC_ICMP_HEADER*)EC_ENDOF(pRequest); /* skip ETHERNET_FRAME header */
            EC_ICMP_HEADER* pIcmpReply = (EC_ICMP_HEADER*)EC_ENDOF(pReply); /* skip ETHERNET_FRAME header */

            /* only answer PING request if MAC address and IP address match */
            if ((pIcmpRequest->byType == EC_ICMP_TYPE_ECHO)
                && (pRequest->Destination == MacAddress)
                && (pIcmpRequest->IpHdr.dwDstAddr == IpV4Address))
            {
                EC_T_WORD wLen = EC_NTOHS(pIcmpRequest->IpHdr.wTotalLength);

                /* swap src and dest ip address */
                pIcmpReply->IpHdr.dwSrcAddr = pIcmpRequest->IpHdr.dwDstAddr;
                pIcmpReply->IpHdr.dwDstAddr = pIcmpRequest->IpHdr.dwSrcAddr;

                pIcmpReply->byType = EC_ICMP_TYPE_ECHO_REPLY;

                pIcmpReply->IpHdr.wCheckSum = 0;
                pIcmpReply->IpHdr.wCheckSum = EC_HTONS(EcCalculateCrcRfc1071((EC_T_WORD*)&pIcmpReply->IpHdr, EC_IP_HEADER_MINIMUM_LEN));

                pIcmpRequest->wCheckSum = 0;
                pIcmpRequest->wCheckSum = EC_HTONS(EcCalculateCrcRfc1071((EC_T_WORD*)&pIcmpReply->byType /* skip IpHdr */, (EC_T_WORD)(wLen - EC_IP_HEADER_MINIMUM_LEN)));

                /* reply valid */
                pReplyFrameDesc->dwSize = (EC_T_WORD)(ETHERNET_FRAME_LEN + wLen);
            }
        }
    } break;
    }
}

static EC_T_VOID myAppEoe_APPL_EoeReceive(EC_T_VOID* pvContext, EC_T_DWORD dwSimulatorId, EC_T_WORD wCfgFixedAddress, EC_T_WORD* pwFrame, EC_T_WORD wFrameSize)
{
    T_MY_EOE_CONTEXT* poContext = (T_MY_EOE_CONTEXT*)pvContext;
    EC_T_LINK_FRAMEDESC oReceiveFrame;
    oReceiveFrame.pbyFrame = (EC_T_BYTE*)pwFrame;
    oReceiveFrame.dwSize = wFrameSize;

    /* call ARP request and ping request handler and send reply if filled by handler */
    myAppProcessEoeFrameArpAndPing(&oReceiveFrame, &poContext->oSendFrame, poContext->oMacAddress, poContext->oIpV4Address);
    if (poContext->oSendFrame.dwSize > 0)
    {
        esEoeSendFrame(dwSimulatorId, wCfgFixedAddress, poContext->oSendFrame.pbyFrame, poContext->oSendFrame.dwSize);
    }
}

static EC_T_VOID myAppEoe_APPL_EoeSettingInd(EC_T_VOID* pvContext, EC_T_DWORD /* dwSimulatorId */, EC_T_WORD /* wCfgFixedAddress */, EC_T_WORD* pwMacAddress, EC_T_WORD* pwIpV4Address,
    EC_T_WORD* /* pwIpV4SubNetMask */, EC_T_WORD* /* pwIpV4DefaultGateway */, EC_T_WORD* /* pwDnsIpV4Address */)
{
    T_MY_EOE_CONTEXT* poContext = (T_MY_EOE_CONTEXT*)pvContext;
    poContext->oMacAddress = *((ETHERNET_ADDRESS*)pwMacAddress);
    poContext->oIpV4Address.dwAddr = EC_GETDWORD(pwIpV4Address);
}

static EC_INLINESTART EC_T_DWORD myAppEoeInit(T_EC_DEMO_APP_CONTEXT* pAppContext, T_MY_EOE_CONTEXT* poContext, EC_T_WORD wSlaveAddress)
{
    EC_T_DWORD dwRetVal = EC_E_ERROR;
    EC_T_DWORD dwRes    = EC_E_ERROR;

    poContext->pAppContext = pAppContext;
    poContext->wSlaveAddress = wSlaveAddress;

    poContext->oSendFrame.pbyFrame = (EC_T_BYTE*)OsMalloc(ETHERNET_MAX_FRAMEBUF_LEN /* 1536 */);
    if (EC_NULL == poContext->oSendFrame.pbyFrame)
    {
        dwRetVal = EC_E_NOMEMORY;
        goto Exit;
    }

    {
        struct _EC_T_SLAVE_SSC_APPL_DESC oSlaveApplDesc;
        OsMemset(&oSlaveApplDesc, 0, sizeof(struct _EC_T_SLAVE_SSC_APPL_DESC));

        oSlaveApplDesc.dwSignature = SIMULATOR_SIGNATURE;
        oSlaveApplDesc.dwSize = sizeof(struct _EC_T_SLAVE_SSC_APPL_DESC);
        oSlaveApplDesc.szName = (EC_T_CHAR*)"MyAppEoeSlaveAppl";
        oSlaveApplDesc.pvContext = poContext; /* first parameter of static wrapper functions */

        oSlaveApplDesc.pfnEoeReceive = myAppEoe_APPL_EoeReceive;
        oSlaveApplDesc.pfnEoeSettingInd = myAppEoe_APPL_EoeSettingInd;

        /* register callbacks at slave */
        dwRes = esSetSlaveSscApplication(pAppContext->dwInstanceId, wSlaveAddress, &oSlaveApplDesc);
        if (EC_E_NOERROR != dwRes)
        {
            dwRetVal = dwRes;
            goto Exit;
        }
    }

    dwRetVal = EC_E_NOERROR;
Exit:
    if (EC_E_NOERROR != dwRetVal)
    {
        SafeOsFree(poContext->oSendFrame.pbyFrame);
    }

    return dwRetVal;
} EC_INLINESTOP

The callbacks need a context. Each slave needs its individual context. If there is only one slave to be registered, a global context can be declared and used:

T_MY_EOE_CONTEXT G_oContext;

The callbacks must be registered using esSetSlaveSscApplication(), e.g. in myAppPrepare() of EcSimulatorHilDemo / EcSimulatorSilDemo:

    OsMemset(&G_oContext, 0, sizeof(G_oContext));
    dwRes = myAppEoeInit(pAppContext, &G_oContext, wSlaveAddress);
    if (EC_E_NOERROR != dwRes)
    {
        dwRetVal = dwRes;
        goto Exit;
    }

6.13. File access over EtherCAT (FoE)

The following examples demonstrate how to customize FoE simulation using esSetSlaveSscApplication() .

FoE Download Example

The following code demonstrates how to receive FoE from the Master and store it in a buffer. The data received in this example is stored in T_MY_CONTEXT::abyFileBuf :

#define MYAPP_FOE_BUFFER_SIZE (35264)
typedef struct
{
    EC_T_BYTE abyFileBuf[MYAPP_FOE_BUFFER_SIZE];
    EC_T_WORD wFileBufLen;

    /* ... */
} T_MY_CONTEXT;

The callback myAppFoeWrite() handles download requests from the Master, myAppFoeWriteData() copies the FoE payload from the mailbox to T_MY_CONTEXT::abyFileBuf :

extern "C" EC_T_WORD EC_FNCALL myAppFoeWrite(
    EC_T_VOID* pvContext, EC_T_DWORD /* dwSimulatorId */, EC_T_WORD /* wCfgFixedAddress */, 
    EC_T_WORD*, EC_T_WORD, EC_T_DWORD)
{
    T_MY_CONTEXT* poContext = (T_MY_CONTEXT*)pvContext;
    poContext->wFileBufLen = 0;

    return 0; /* accept file download (ECAT_FOE_ERRCODE_... denies download) */
}

extern "C" EC_T_WORD EC_FNCALL myAppFoeWriteData(
    EC_T_VOID* pvContext, EC_T_DWORD /* dwSimulatorId */, EC_T_WORD /* wCfgFixedAddress */,
    EC_T_WORD* pData, EC_T_WORD wSize, EC_T_BYTE bDataFollowing)
{
    T_MY_CONTEXT* poContext = (T_MY_CONTEXT*)pvContext;
    if (poContext->wFileBufLen + wSize > sizeof(poContext->abyFileBuf))
    {
        return ECAT_FOE_ERRCODE_DISKFULL; /* abort transfer */
    }

    OsMemcpy(&poContext->abyFileBuf[poContext->wFileBufLen], pData, wSize);
    poContext->wFileBufLen = poContext->wFileBufLen + wSize;

    /* FoE ACK segment at Master (more segments follow / download finished) */
    return bDataFollowing ? SSC_FOE_ACK : SSC_FOE_ACKFINISHED;
}

See type EC_PF_SSC_APPL_FOE_WRITE .

The callbacks must be registered using esSetSlaveSscApplication(), e.g. in myAppPrepare() of EcSimulatorHilDemo / EcSimulatorSilDemo:

struct _EC_T_SLAVE_SSC_APPL_DESC oSlaveApp;
OsMemset(&oSlaveApp, 0, sizeof(struct _EC_T_SLAVE_SSC_APPL_DESC));

T_MY_CONTEXT* poSlaveApplContext = &G_oSlaveApplContext;
OsMemset(poSlaveApplContext, 0, sizeof(G_oSlaveApplContext));

oSlaveApp.dwSignature = SIMULATOR_SIGNATURE;
oSlaveApp.dwSize = sizeof(struct _EC_T_SLAVE_SSC_APPL_DESC);
oSlaveApp.szName = (EC_T_CHAR*)"mySlaveAppl";
oSlaveApp.pvContext = poSlaveApplContext; /* pvContext of callbacks */
oSlaveApp.pfnFoeWrite = myAppFoeWrite;
oSlaveApp.pfnFoeWriteData = myAppFoeWriteData;

/* register SlaveSscApplication call-backs (Master in INIT) */
dwRes = esSetSlaveSscApplication(dwSimulatorId, wSlaveAddress, &oSlaveApp);
if (dwRes != EC_E_NOERROR)
{
    goto Exit;
}

The following code at Master downloads the file to the slave:

/* set Master PREOP */
dwRes = emSetMasterState(dwMasterId, 30000 /* dwTimeout */, eEcatState_PREOP);
if (dwRes != EC_E_NOERROR)
{
    goto Exit;
}

/* download file */
dwRes = emFoeFileDownload(dwMasterId, emGetSlaveId(dwMasterId, wSlaveAddress),
    (EC_T_CHAR*)"foe.dat", (EC_T_DWORD)OsStrlen((EC_T_CHAR*)"foe.dat"),
    G_abyFileBuf, MYAPP_FOE_BUFFER_SIZE, 0 /* dwPassword */, 30000 /* dwTimeout */);
if (dwRes != EC_E_NOERROR)
{
    goto Exit;
}
FoE Upload Example

The following code demonstrates how to serve FoE data from a local buffer to the Master. The data served in this example is stored in T_MY_CONTEXT::abyFileBuf :

#define MYAPP_FOE_BUFFER_SIZE (35264)
typedef struct
{
    EC_T_BYTE abyFileBuf[MYAPP_FOE_BUFFER_SIZE];
    EC_T_WORD wFileBufLen;

    /* ... */
} T_MY_CONTEXT;

myAppFoeRead() handles upload requests from the Master, myAppFoeReadData() copies the FoE payload from T_MY_CONTEXT::abyFileBuf to the mailbox:

extern "C" EC_T_WORD EC_FNCALL myAppFoeReadData(
    EC_T_VOID* pvContext, EC_T_DWORD /* dwSimulatorId */, EC_T_WORD /* wCfgFixedAddress */,
    EC_T_DWORD dwOffset, EC_T_WORD wMaxBlockSize, EC_T_WORD* pData)
{
    T_MY_CONTEXT* poContext = (T_MY_CONTEXT*)pvContext;
    if (dwOffset > poContext->wFileBufLen)
    {
        return 0;
    }

    if (dwOffset + wMaxBlockSize > poContext->wFileBufLen)
    {
        wMaxBlockSize = EC_LOWORD(poContext->wFileBufLen - dwOffset);
    }

    if (wMaxBlockSize > 0)
    {
        OsMemcpy(pData, &poContext->abyFileBuf[dwOffset], wMaxBlockSize);
    }

    return wMaxBlockSize; /* amount of data read */
}

extern "C" EC_T_WORD EC_FNCALL myAppFoeRead(
    EC_T_VOID* pvContext, EC_T_DWORD dwSimulatorId, EC_T_WORD wCfgFixedAddress,
    EC_T_WORD*, EC_T_WORD, EC_T_DWORD, EC_T_WORD wMaxBlockSize, EC_T_WORD* pData)
{
    /* APPL_FoeRead contains returning first data */
    return myAppFoeReadData(pvContext, dwSimulatorId, wCfgFixedAddress, 0, wMaxBlockSize, pData);
}

The callbacks must be registered using esSetSlaveSscApplication(), e.g. in myAppPrepare() of EcSimulatorHilDemo / EcSimulatorSilDemo:

    struct _EC_T_SLAVE_SSC_APPL_DESC oSlaveApp;
    OsMemset(&oSlaveApp, 0, sizeof(struct _EC_T_SLAVE_SSC_APPL_DESC));

    T_MY_CONTEXT* poSlaveApplContext = &G_oSlaveApplContext;
    OsMemset(poSlaveApplContext, 0, sizeof(G_oSlaveApplContext));

    /* pattern for file content */
    OsMemset(poSlaveApplContext->abyFileBuf, 0xAA, MYAPP_FOE_BUFFER_SIZE);
    poSlaveApplContext->wFileBufLen = MYAPP_FOE_BUFFER_SIZE;

    oSlaveApp.dwSignature = SIMULATOR_SIGNATURE;
    oSlaveApp.dwSize = sizeof(struct _EC_T_SLAVE_SSC_APPL_DESC);
    oSlaveApp.szName = (EC_T_CHAR*)"mySlaveAppl";
    oSlaveApp.pvContext = poSlaveApplContext; /* pvContext of callbacks */
    oSlaveApp.pfnFoeRead = myAppFoeRead;
    oSlaveApp.pfnFoeReadData = myAppFoeReadData;

    /* register SlaveSscApplication callbacks (Master in INIT) */
    dwRes = esSetSlaveSscApplication(dwSimulatorId, wSlaveAddress, &oSlaveApp);
    if (dwRes != EC_E_NOERROR)
    {
        goto Exit;
    }

The following code at Master uploads the file from the slave:

    /* set Master PREOP */
    dwRes = emSetMasterState(dwMasterId, 30000 /* dwTimeout */, eEcatState_PREOP);
    if (dwRes != EC_E_NOERROR)
    {
        goto Exit;
    }

    /* upload file */
    dwRes = emFoeFileUpload(dwMasterId, emGetSlaveId(dwMasterId, wSlaveAddress),
        (EC_T_CHAR*)"foe.dat", (EC_T_DWORD)OsStrlen((EC_T_CHAR*)"foe.dat"),
        G_abyFileBuf, MYAPP_FOE_BUFFER_SIZE, &dwOutDataLen, 0 /* dwPassword */, 30000 /* dwTimeout */);
    if (dwRes != EC_E_NOERROR)
    {
        goto Exit;
    }

6.14. Vendor specific access over EtherCAT (VoE)

See also VoE Receive Example

6.14.1. esVoeSend

EC_T_DWORD esVoeSend(EC_T_DWORD dwInstanceId, EC_T_WORD wCfgFixedAddress, EC_T_WORD wDstFixedAddress, EC_T_VOID *pvData, EC_T_DWORD dwDataLen)

Fill “Mailbox In” (SM1) with VoE data to be polled by Master. The slave must have VoE enabled, see ESI: /EtherCATInfo/Descriptions/Devices/Device/Mailbox/VoE, ENI/EXI: /EtherCATConfig/Config/Slave/Mailbox/Protocol/VoE.

Parameters
  • dwInstanceId – [in] Simulator Instance ID

  • wCfgFixedAddress – [in] Slave’s station address

  • wDstFixedAddress – [in] Destination address

  • pvData – [in] Data

  • dwDataLen – [in] Data length

Returns

EC_E_NOERROR or error code

6.14.2. esSetVoeReceiveCallback

EC_T_DWORD esSetVoeReceiveCallback(EC_T_DWORD dwInstanceId, EC_T_WORD wCfgFixedAddress, EC_T_PF_VOE_RECEIVE_CB pfVoeReceive, EC_T_VOID *pvContext)

Set VoE data received callback for “Mailbox Out” (SM0) Within the callback, no EC-Simulator API may be called. The slave must have VoE enabled, see ESI: /EtherCATInfo/Descriptions/Devices/Device/Mailbox/VoE, ENI/EXI: /EtherCATConfig/Config/Slave/Mailbox/Protocol/VoE.

Parameters
  • dwInstanceId – [in] Simulator Instance ID

  • wCfgFixedAddress – [in] Slave’s station address

  • pfVoeReceive – [in] Receive callback function

  • pvContext – [in] Receive callback function context

Returns

6.14.3. VoE Receive Example

The following examples demonstrate how to register a handler for VoE writes from the Master or from other slaves.

VoE Receive Example

The following code demonstrates how to send VoE from the Master to the slave and store it in a buffer at the Simulator. The data received in this example is stored in T_MY_CONTEXT::abyVoeBuf :

#define MYAPP_VOE_BUFFER_SIZE (512)
typedef struct
{
    EC_T_BYTE  abyVoeBuf[MYAPP_VOE_BUFFER_SIZE];
    EC_T_DWORD dwVoeBufLen;
    EC_T_VOID* pvEvent;

    /* ... */
} T_MY_CONTEXT;

myAppVoeReceiveCallback() handles write requests from the Master and copies the received VoE payload from the mailbox to T_MY_CONTEXT::abyVoeBuf :

EC_T_BYTE EC_FNCALL myAppVoeReceiveCallback(
    EC_T_VOID* pvContext, EC_T_DWORD /* dwSimulatorId */,
    EC_T_WORD /* wCfgFixedAddress VoE receiver address, e.g. 1001 */,
    EC_T_WORD /* wSrcFixedAddress VoE sender address, 0: Master */,
    EC_T_VOID* pvData, EC_T_DWORD dwDataLen)
{
    T_MY_CONTEXT* poContext = (T_MY_CONTEXT*)pvContext;

    OsMemcpy(poContext->abyVoeBuf, pvData, EC_AT_MOST(MYAPP_VOE_BUFFER_SIZE, dwDataLen));
    poContext->dwVoeBufLen = dwDataLen;

    /* No EC-Simulator APIs may be issued from within the callback context.
       Signalize VoE data received for deferred processing in other thread. */
    OsSetEvent(poContext->pvEvent);

    return 0;
}

The following code shows how to register myAppVoeReceiveCallback() and send VoE from the Master to the simulated slave:

    EC_T_DWORD dwRes = EC_E_ERROR;
    EC_T_DWORD dwRetVal = EC_E_ERROR;

#define MYAPP_VOE_SEND_BUF_LEN sizeof(EC_T_DWORD)
    EC_T_BYTE abyVoeSendBuf[MYAPP_VOE_SEND_BUF_LEN];
    OsMemset(abyVoeSendBuf, 0, MYAPP_VOE_SEND_BUF_LEN);

    T_MY_CONTEXT oContext;
    OsMemset(&oContext, 0, sizeof(T_MY_CONTEXT));
    oContext.pvEvent = OsCreateEvent();
    if (EC_NULL == oContext.pvEvent)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "OsCreateEvent failed\n"));
        dwRetVal = EC_E_NOMEMORY;
        goto Exit;
    }

    /* EC-Simulator: register VoE handler */
    dwRes = esSetVoeReceiveCallback(dwSimulatorId, 1001, myAppVoeReceiveCallback, &oContext);
    if (dwRes != EC_E_NOERROR)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "esSetVoeReceiveCallback failed: %s (0x%lx))\n", esGetText(dwSimulatorId, dwRes), dwRes));
        dwRetVal = dwRes;
        goto Exit;
    }

    /* EC-Master: set PREOP state */
    dwRes = emSetMasterState(dwMasterId, 30000, eEcatState_PREOP);
    if (dwRes != EC_E_NOERROR)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "emSetMasterState failed: %s (0x%lx))\n", ecatGetText(dwRes), dwRes));
        dwRetVal = dwRes;
        goto Exit;
    }

    /* send VoE to slave */
    EC_SET_FRM_DWORD(abyVoeSendBuf, 0x12345678);
    dwRes = emVoeWrite(dwMasterId, emGetSlaveId(dwMasterId, 1001), abyVoeSendBuf, MYAPP_VOE_SEND_BUF_LEN, 5000);
    if (dwRes != EC_E_NOERROR)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "emVoeWrite failed: %s (0x%lx))\n", ecatGetText(dwRes), dwRes));
        dwRetVal = dwRes;
        goto Exit;
    }

    /* wait for slave received data in callback */
    dwRes = OsWaitForEvent(oContext.pvEvent, 5000);
    if (dwRes != EC_E_NOERROR)
    {
        EcLogMsg(EC_LOG_LEVEL_ERROR, (pEcLogContext, EC_LOG_LEVEL_ERROR, "OsWaitForEvent failed: %s (0x%lx))\n", esGetText(dwSimulatorId, dwRes), dwRes));
        dwRetVal = dwRes;
        goto Exit;
    }

    dwRetVal = EC_E_NOERROR;
Exit:
    SafeOsDeleteEvent(oContext.pvEvent);