Developing LwIP Application with Sequential API

Showing results for 
Search instead for 
Did you mean: 

Developing LwIP Application with Sequential API

Developing LwIP Application with Sequential API

LwIP can be used in two basic modes: Mainloop mode (“NO_SYS”)(no OS/RTOS running on target system) or OS mode (TCPIP thread) (there is an OS running on the target system). In mainloop mode, only raw API can be used. In OS mode, raw API and sequential APIs can be used.

In OS mode, the lwip stack and the application run in separate tasks. The application communicates with the LwIP stack through sequential API calls that sue the RTOS mailbox mechaniam for inter-process communicatioin.

This post is focusing on how to design a LwIP applicatioin in OS mode with sequential API in MCUXpresso SDK. It is for LwIP beginners. The code snipperts is from MCUXpresso SDK2.6.

For how to design a LwIP applicaton in mainloop mode (bare metal mode) with raw API, please refer to below link:

Developing LwIP Applications with Raw API  



Generally, a LwIP application inlcudes network interface setting up, LwIP stack initialization , using LwIP API, and configuration.


1. Starting a network interface

To create a new network interface, the user allocates space for a new struct netif (but does not initialize any part of it) and calls netifapi_netif_add:

    IP4_ADDR(&fsl_netif0_ipaddr, configIP_ADDR0, configIP_ADDR1, configIP_ADDR2, configIP_ADDR3);

    IP4_ADDR(&fsl_netif0_netmask, configNET_MASK0, configNET_MASK1, configNET_MASK2, configNET_MASK3);

IP4_ADDR(&fsl_netif0_gw, configGW_ADDR0, configGW_ADDR1, configGW_ADDR2, configGW_ADDR3);


    netifapi_netif_add(&fsl_netif0, &fsl_netif0_ipaddr, &fsl_netif0_netmask, &fsl_netif0_gw, &fsl_enet_config0,

                       ethernetif0_init, tcpip_input);

Pass tcpip_input API to netif_add API as input callback function that is called to pass ingress packets up in the protocol layer stack


next we need to bring the interface up

An interface that is “up” is available to your application for input and output, and “down” is the opposite state. Therefore, before you can use the interface, you must bring it up. This can be accomplished depending on how the interface gets its IP address.  We can use static IP address  or DHCP.

Set the network interface as the default network interface



Bring the interface up, available for processing


2. Initializing LwIP stack


Call tcpip_init to create tcpip_thread, this thread has exclusive access to LwIP core functions. Other threads communicate with this thread using message boxes. It also starts all the timers to make sure they are running in the right thread context.


tcpip_init(NULL, NULL);


void   tcpip_init(tcpip_init_done_fn initfunc, void *arg)




  tcpip_init_done = initfunc;

  tcpip_init_done_arg = arg;

  if (sys_mbox_new(&tcpip_mbox, TCPIP_MBOX_SIZE) != ERR_OK) {

    LWIP_ASSERT("failed to create tcpip_thread mbox", 0);



  if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) {

    LWIP_ASSERT("failed to create lock_tcpip_core", 0);






Priority of user task should not exceed the priority of tcpip_thread

  - In lwipopts.h, the priority of tcpip_thread

    #define TCPIP_THREAD_PRIO              2


3. Using sequential API

 As shown in the below figure, the steps for establishing a TCP connection on the client side are the following:

  • Create a connection using the netconn_new() function;
  • Connect to the address of the server using the netconn_connect() function;
  • Send and receive data by means of the netconn_recv() and netconn_write() functions.
  • Close the connection by means of the netconn_close() function.

The steps involved in establishing a TCP connection on the server side are as follows:

  • Create a TCP connection with the netconn_new() function;
  • Bind the server to an address using the netconn_bind() function;
  • Listen for connections with the netconn_listen() function;
  • Accept a connection with the netconn_accept() function. This call typically blocks until a client connects with the server.
  • Send and receive data by means of netconn_write() and netconn_recv().
  • Close the connection by means of the netconn_close() function.




static void

tcpecho_thread(void *arg)


  struct netconn *conn, *newconn;

  err_t err;



  /* Create a new connection identifier. */

  /* Bind connection to well known port number 7. */


  conn = netconn_new(NETCONN_TCP_IPV6);

  netconn_bind(conn, IP6_ADDR_ANY, 7);

#else /* LWIP_IPV6 */

  conn = netconn_new(NETCONN_TCP);

  netconn_bind(conn, IP_ADDR_ANY, 7);

#endif /* LWIP_IPV6 */

  LWIP_ERROR("tcpecho: invalid conn", (conn != NULL), return;);


  /* Tell connection to go into listening mode. */



  while (1) {


    /* Grab new connection. */

    err = netconn_accept(conn, &newconn);

    /*printf("accepted new connection %p\n", newconn);*/

    /* Process the new connection. */

    if (err == ERR_OK) {

      struct netbuf *buf;

      void *data;

      u16_t len;


      while ((err = netconn_recv(newconn, &buf)) == ERR_OK) {


        do {

             netbuf_data(buf, &data, &len);

             err = netconn_write(newconn, data, len, NETCONN_COPY);

#if 0

            if (err != ERR_OK) {

              printf("tcpecho: netconn_write: error \"%s\"\n", lwip_strerr(err));



        } while (netbuf_next(buf) >= 0);



      /*printf("Got EOF, looping\n");*/

      /* Close connection and discard connection identifier. */







From the tcpecho thread, we can see

First, one new TCP connection was called with parameter NETCONN_TCP  by API netconn_new.

#define netconn_new(t)                  netconn_new_with_proto_and_callback(t, 0, NULL)

struct netconn *

netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback)


  struct netconn *conn;




  conn = netconn_alloc(t, callback);

  if (conn != NULL) {

    err_t err;


    API_MSG_VAR_REF(msg).msg.n.proto = proto;

    API_MSG_VAR_REF(msg).conn = conn;

    err = netconn_apimsg(lwip_netconn_do_newconn, &API_MSG_VAR_REF(msg));

    if (err != ERR_OK) {

      LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL);

      LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox));


      LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox));

#endif /* LWIP_TCP */


      LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed));




      memp_free(MEMP_NETCONN, conn);


      return NULL;




  return conn;



Then, the newly created connection is then bound to port 7 (echo protocol) by calling the API function netconn_bind.


Next, the application starts the listening process on the connection by calling the API function netconn_listen.

In the infinite while(1) loop, the application waits for a new connection by calling the API function netconn_accept. This API will block the application task when there is no incoming connection.

When there is an incoming connection, the application can start receiving data by calling the API function netconn_recv. Incoming data are received in a netbuf.




Application can get the received data by calling the netbuf API function netbuf_data.


netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len)


  LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;);

  LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;);

  LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;);


  if (buf->ptr == NULL) {

    return ERR_BUF;


  *dataptr = buf->ptr->payload;

  *len = buf->ptr->len;

  return ERR_OK;



The received data is sent back (echoed) to the remote TCP client by calling the API function netconn_write.

Netconn_close and netconn_delete are used to respectively close and delete the netconn connection



4. Configuration LwIP

lwipopts.h is a user file that you can use to fully configure lwIP and all of its modules. You do not need to define every option that lwIP provides; if you do not define an option, a default value will be used. Therefore, your lwipopts.h provides a way to override much of the behavior of lwIP.

In multi theads mode, . We need to #define NO_SYS to 0.

Please refer to evkbimxrt1050_lwip_tcpecho_freertos\source\lwipopts.h




 * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain

 * critical regions during buffer allocation, deallocation and memory

 * allocation and deallocation.





 * NO_SYS==0: Use RTOS


#define NO_SYS 0


 * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c)


#define LWIP_NETCONN 1


 * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c)


#define LWIP_SOCKET 1



 * LWIP_SO_RCVTIMEO==1: Enable receive timeout for sockets/netconns and

 * SO_RCVTIMEO processing.










Don't I need to have an active network connection before making netif API calls? If there is no network cable attached when I call netif_dhcp_start(), but I attach it later, I never see the dhcp state variable go to DHCP_STATE_BOUND. So, I would think there would be a callback or a status variable that lets me know when the link goes up or down, yet I can't seem to find it in either MCUXpresso SDK or LwIP. How is link status detected and reported?


Version history
Revision #:
1 of 1
Last update:
‎11-09-2019 08:14 PM
Updated by: