Connecting lwIP to the EoE API
This is only necessary if the EtherCAT Library is used without GOAL. If it is used with GOAL the EoE interface is automatically connected to the TCP/IP stack of GOAL and the normal GOAL NET API can be used.
Configuring your project
Our deliveries of the EtherCAT library allready contain a driver that connects lwIP to the EoE API. You have to do the following steps:
add include paths to your IDE or Build System:
drivers/common/lwip
ext/lwip/src/includ
add EoE driver files:
drivers/common/lwip/ec_eoe_lwip.c
drivers/common/lwip/lwipopts.h
drivers/common/lwip/arch/cc.h
drivers/common/lwip/arch/perf.h
drivers/common/lwip/arch/sys_arch.h
add lwIP Source code:
ext/lwip/src/core/def.c
ext/lwip/src/core/dns.c
ext/lwip/src/core/inet_chksum.c
ext/lwip/src/core/init.c
ext/lwip/src/core/ip.c
ext/lwip/src/core/ipv4/etharp.c
ext/lwip/src/core/ipv4/icmp.c
ext/lwip/src/core/ipv4/igmp.c
ext/lwip/src/core/ipv4/ip4_addr.c
ext/lwip/src/core/ipv4/ip4.c
ext/lwip/src/core/ipv4/ip4_frag.c
ext/lwip/src/core/mem.c
ext/lwip/src/core/memp.c
ext/lwip/src/core/netif.c
ext/lwip/src/core/pbuf.c
ext/lwip/src/core/tcp.c
ext/lwip/src/core/tcp_in.c
ext/lwip/src/core/tcp_out.c
ext/lwip/src/core/timeouts.c
ext/lwip/src/core/udp.c
ext/lwip/src/netif/ethernet.c
ext/lwip/src/include/lwip/altcp.h
ext/lwip/src/include/lwip/api.h
ext/lwip/src/include/lwip/arch.h
ext/lwip/src/include/lwip/autoip.h
ext/lwip/src/include/lwip/debug.h
ext/lwip/src/include/lwip/def.h
ext/lwip/src/include/lwip/dhcp.h
ext/lwip/src/include/lwip/dns.h
ext/lwip/src/include/lwip/err.h
ext/lwip/src/include/lwip/etharp.h
ext/lwip/src/include/lwip/icmp6.h
ext/lwip/src/include/lwip/icmp.h
ext/lwip/src/include/lwip/igmp.h
ext/lwip/src/include/lwip/inet_chksum.h
ext/lwip/src/include/lwip/init.h
ext/lwip/src/include/lwip/ip4_addr.h
ext/lwip/src/include/lwip/ip4_frag.h
ext/lwip/src/include/lwip/ip4.h
ext/lwip/src/include/lwip/ip6_addr.h
ext/lwip/src/include/lwip/ip6_frag.h
ext/lwip/src/include/lwip/ip6.h
ext/lwip/src/include/lwip/ip_addr.h
ext/lwip/src/include/lwip/ip.h
ext/lwip/src/include/lwip/mem.h
ext/lwip/src/include/lwip/memp.h
ext/lwip/src/include/lwip/mld6.h
ext/lwip/src/include/lwip/nd6.h
ext/lwip/src/include/lwip/netbuf.h
ext/lwip/src/include/lwip/netdb.h
ext/lwip/src/include/lwip/netifapi.h
ext/lwip/src/include/lwip/netif.h
ext/lwip/src/include/netif/etharp.h
ext/lwip/src/include/netif/ethernet.h
ext/lwip/src/include/netif/ppp/ppp_impl.h
ext/lwip/src/include/netif/ppp/ppp_opts.h
ext/lwip/src/include/lwip/opt.h
ext/lwip/src/include/lwip/pbuf.h
ext/lwip/src/include/lwip/priv/api_msg.h
ext/lwip/src/include/lwip/priv/memp_priv.h
ext/lwip/src/include/lwip/priv/memp_std.h
ext/lwip/src/include/lwip/priv/nd6_priv.h
ext/lwip/src/include/lwip/priv/sockets_priv.h
ext/lwip/src/include/lwip/priv/tcpip_priv.h
ext/lwip/src/include/lwip/priv/tcp_priv.h
ext/lwip/src/include/lwip/prot/dhcp.h
ext/lwip/src/include/lwip/prot/dns.h
ext/lwip/src/include/lwip/prot/etharp.h
ext/lwip/src/include/lwip/prot/ethernet.h
ext/lwip/src/include/lwip/prot/icmp6.h
ext/lwip/src/include/lwip/prot/icmp.h
ext/lwip/src/include/lwip/prot/igmp.h
ext/lwip/src/include/lwip/prot/ip4.h
ext/lwip/src/include/lwip/prot/ip.h
ext/lwip/src/include/lwip/prot/tcp.h
ext/lwip/src/include/lwip/prot/udp.h
ext/lwip/src/include/lwip/raw.h
ext/lwip/src/include/lwip/snmp.h
ext/lwip/src/include/lwip/sockets.h
ext/lwip/src/include/lwip/stats.h
ext/lwip/src/include/lwip/sys.h
ext/lwip/src/include/lwip/tcpbase.h
ext/lwip/src/include/lwip/tcp.h
ext/lwip/src/include/lwip/timeouts.h
ext/lwip/src/include/lwip/udp.h
remove dummy EoE implementation from the sample application (if not done already)
examples/*/usr_eoe.c
Writing an lwIP Application
You can simply set up the EtherCAT Library as you usually do. You can use all API functions of the lwIP that are enabled with the current configuration. If you want to change the lwIP settings please have a look at the file drivers/common/lwip/lwipopts.h.
The following source code demonstrates how to set up an UDP Echo server.
/** @file
*
* @brief EtherCAT Demo Application for EoE
*
* Received UDP frames on port APPL_PORT will be reflected to sender.
*
* @copyright
* Copyright 2009-2021 port GmbH Halle/Saale.
* This software is protected Intellectual Property and may only be used
* according to the license agreement.
*/
#include "ecat_conf.h"
#include "ec_header.h"
#include "usr_dynod.h"
#include "lwip/opt.h"
#include "lwip/init.h"
#include "lwip/netif.h"
#include "netif/etharp.h"
#include "lwip/sys.h"
#include "lwip/udp.h"
#include "lwip/udp.h"
#include "lwip/tcp.h"
#include "lwip/dns.h"
#include "lwip/igmp.h"
#include "lwip/dns.h"
/****************************************************************************/
/* Local Defines */
/****************************************************************************/
#define APPL_PORT 12345
/****************************************************************************/
/* Global Variables */
/****************************************************************************/
extern BOOL_T shutDownFlag; /**< shut down application */
BOOL_T shutDownFlag = LIB_FALSE; /**< shut down application */
EC_INSTANCE_T ecatData; /**< EterCAT instance data */
/****************************************************************************/
/* Local Variables */
/****************************************************************************/
struct udp_pcb *pUdpPcbDesc = NULL; /**< pointer to LwIP UDP PCB */
/****************************************************************************/
/* Local prototypes */
/****************************************************************************/
static err_t ec_eoeLwipUdpOpen(
void
);
static void ec_eoeLwipUdpRecvCb(
void *pArg, /**< arg user supplied argument (udp_pcb.recv_arg) */
struct udp_pcb *pUdpPcbDesc, /**< pcb the udp_pcb which received data */
struct pbuf *pLwipBuffer, /**< pointer the packet buffer that was received */
const ip_addr_t *pAddrRemote, /**< addr of the remote IP address from which the packet was received */
u16_t remote_port /**< remote port which received the udp packet */
);
/****************************************************************************/
/** Set up the device and execute the EtherCAT Library
*
* This function is the application's entry point. It initializes the device and
* the EtherCAT Library. After setup the function @em ec_flushMbox() is called
* cyclically to execute the EtherCAT Library.
*/
int main(
void
)
{
EC_RET_T commonRet; /* EtherCAT Lib return value */
UNSIGNED8 ret; /* common return value */
BOOL_T err = LIB_FALSE; /* error flag */
/* hardware initialization, e.g GPIO configuration */
ret = ec_initDevice();
ec_logInfo("ec_iniDevice: 0x%02x", ret);
if (ret != 0) {
err = LIB_TRUE;
}
/* set up the timer for the EtherCAT Library */
ret = ec_initTimer();
ec_logInfo("ec_initTimer: 0x%02x", ret);
if (ret != 0) {
err = LIB_TRUE;
}
/* initialize the EtherCAT Library */
commonRet = ec_init(&ecatData);
ec_logInfo("ec_initEtherCAT: 0x%02x", commonRet);
if (commonRet != RET_OK) {
err = LIB_TRUE;
}
/* open application UDP channel */
if (ERR_OK != ec_eoeLwipUdpOpen()) {
err = LIB_TRUE;
}
/* enter application loop */
ec_logInfo("port GmbH EtherCAT Library %s.%s",EC_LIBRARY_VERSION, EC_LIBRARY_PATCH);
if(LIB_TRUE == err) {
shutDownFlag = LIB_TRUE;
}
while (shutDownFlag == LIB_FALSE) {
ec_loop(&ecatData);
}
return 0;
}
/****************************************************************************/
/** UDP Application Callback
*
* This function is called by the stack when an UDP datagram has been received.
*
* The incoming frame is reflected to sender.
*/
static void ec_eoeLwipUdpRecvCb(
void *pArg, /**< arg user supplied argument (udp_pcb.recv_arg) */
struct udp_pcb *pUdpPcbDesc, /**< pcb the udp_pcb which received data */
struct pbuf *pLwipBuffer, /**< pointer the packet buffer that was received */
const ip_addr_t *pAddrRemote, /**< addr of the remote IP address from which the packet was received */
u16_t remote_port /**< remote port which received the udp packet */
)
{
/* copy remote address and port */
memcpy(&pUdpPcbDesc->remote_ip, pAddrRemote, sizeof(ip_addr_t));
pUdpPcbDesc->remote_port = remote_port;
/* send message */
udp_send(pUdpPcbDesc, pLwipBuffer);
}
/****************************************************************************/
/** Open a network channel
*
* The Channel is bound to the local interfaces and remote interfaces IP
* address. The Channel is described by a udp_cp from lwIP which is stored
* as local variable.
*
* @retval ERR_OK - success
* @retval other - fail
*/
static err_t ec_eoeLwipUdpOpen(
void
)
{
err_t retValLwip; /* error value for lwIP */
/* check if channel is allraedy open */
if (NULL != pUdpPcbDesc) {
ec_logInfo("Couldn't create new UDP channel descriptor");
return ERR_OK;
}
/* Create a new UDP lwIP control block */
pUdpPcbDesc = udp_new();
if (NULL == pUdpPcbDesc) {
ec_logInfo("Couldn't create new UDP channel descriptor");
return ERR_MEM;
}
/* make addresses reusable */
pUdpPcbDesc->so_options |= SOF_REUSEADDR;
/* Open local port for any local IP address */
retValLwip = udp_bind(pUdpPcbDesc, IP_ADDR_ANY, APPL_PORT);
/* Check if operation was successful */
if (ERR_OK != retValLwip) {
ec_logInfo("Couldn't connect UDP channel");
return retValLwip;
}
/* Set UDP receive callback */
udp_recv(pUdpPcbDesc, ec_eoeLwipUdpRecvCb, NULL);
return ERR_OK;
}
Testing the Application
EoE Settings in TwinCAT
Select the EtherCAT Slave in the Solution Explorer and go to the Tab “EtherCAT.“ Press the button “Advanced Settings.“ A Pop-up will open. In the setting tree choose Mailbox → EoE. There you can configure the EoE Mailbox of the Slave as an IP Port. You can assign an IP address to the Slave, e.g. 192.168.1.2.
Test Setup
In order to test the UDP connection you have to connect a Test PC to the EtherCAT Slave. However you cannot connect it directly to an EtherCAT Slave Controller. You have to use a MAilbox Gateway, i.e. a device that splits Ethernet frames into EoE Datagrams and vice versa. The Mailbox Gateway could be the PLC itself or an additional EtherCAT Slave.
Test Program
The test program on the Test PC should be able to send and receive UDP datagrams. Please make sure the PC’s interface is in the same subnet as the EoE device, e.g. by using the IP address 192.168.1.1. The test program must send datagrams to the IP address of the EoE device (e.g. 192.168.1.2) and to the UDP port “APPL_PORT“ that was defined by the sample application above (e.g. 12345).
If you have netcat on your system you can use the following command:
nc -u 192.168.1.2 12345
Now you can enter an arbitrary text. When pressing enter the test is sent as UDP data to the EoE device. The sample application should echo back the received text. Therefore the text should appear again on the console.