TCP Client & Server Implementation on MCUXrpesso SDK --- part I--using LwIP Netconn API

cancel
Showing results for 
Search instead for 
Did you mean: 

TCP Client & Server Implementation on MCUXrpesso SDK --- part I--using LwIP Netconn API

100% helpful (1/1)

TCP Client & Server Implementation on MCUXrpesso SDK --- part I--using LwIP Netconn API

 

  1. Introduction

TCP Client & Server establishes a two-way connection between a server and a client. It is the most common communication model used by applications such as HTTP, Telnet, FTP, SSH and others

LwIP is a free light-weight TCP/IP stack in MCUXpresso SDK. It has three application programming interfaces (API):

RAW API:  it is the native API of LwIP. It enables the development of applications using event callbacks.

Netconn API:  it is a high level sequential API that requires the services of a real-time system (RTOS), The Netconn API enables multi-threaded operations

BSD Socket API: it is developed on top of the Netconn API.

In this article, I will introduce how to implement a TCP client & Server demo with LwIP Netconn API.  One EVK-RT1060 acts as TCP server, and others boards act as TCP clients.  They are connected through an Ethernet router.  They communicate via TCP protocol. If you press SW8 on the client board, it will toggle a LED on the Server board.

 

danielchen_0-1635259175564.png

 

This article is for beginners to understand the Netconn API on NXP MCUXpresso SDK.

 

2. Hardware configuration

PHY settings:

In this demo, we use phyksz8081, that is the default PHY in EVK-RT1060 board.  ENET port 0.

/*! @brief The ENET PHY address. */

#define BOARD_ENET0_PHY_ADDRESS (0x02U) /* Phy address of enet port 0. */

 

/* Address of PHY interface. */

#define EXAMPLE_PHY_ADDRESS BOARD_ENET0_PHY_ADDRESS

 

/* MDIO operations. */

#define EXAMPLE_MDIO_OPS enet_ops

 

/* PHY operations. */

#define EXAMPLE_PHY_OPS phyksz8081_ops

 

/* ENET clock frequency. */

#define EXAMPLE_CLOCK_FREQ CLOCK_GetFreq(kCLOCK_IpgClk)

 

Configure the external PHY, pull up the ENET_INT before RESET.

 

    GPIO_PinInit(GPIO1, 9, &gpio_config);

    GPIO_PinInit(GPIO1, 10, &gpio_config);

    /* pull up the ENET_INT before RESET. */

    GPIO_WritePinOutput(GPIO1, 10, 1);

    GPIO_WritePinOutput(GPIO1, 9, 0);

    delay();

    GPIO_WritePinOutput(GPIO1, 9, 1);

 

MAC settings:

In this demo, MAC address is defined in Macro configMAC_ADDR

 

/* MAC address configuration. */

#define configMAC_ADDR                     \

    {                                      \

        0x02, 0x12, 0x13, 0x10, 0x15, 0x11 \

    }

 3. 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

netifapi_netif_set_default(&fsl_netif0);

 

Bring the interface up, available for processing

    netifapi_netif_set_up(&fsl_netif0);

 

4. LwIP Initialization

Call tcpip_init to create a 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);

 

5. TCP Client & Server Implementation

For TCP communications, one host listens for incoming connection requests. When a request arrives, the server accepts it and data is transferred between the server and the client. The sequence of function calls for the client and a server participating in a TCP connection is show in below picture.

danielchen_1-1635259600410.png

The steps for establishing a TCP connection on the client side are the following:

Create a netconn using the netconn_new() function.

Connect the connection to the address of the server using the netconn_connect() function.

Send and receive data by means of the netconn_recv() and netconn_send() functions.

Close the connection by means of the netconn_close() function.

 

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

Create a connection with netconn_new() function;

Bind the connection 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 blocks until a client connects.

Send and receive data by means of netconn_send() and netconn_recv().

Close the connection by means of the netconn_close() function.

 

5.1 TCP SERVER IMPLEMENTATION

 

netconn_new() is used to create a new connection.

 

  conn = netconn_new(NETCONN_TCP);

netconn_bind() binds a connection to a local IP address and port.

  netconn_bind(conn, IP_ADDR_ANY, TCP_CUSTOM_PORT);

netconn_listen() function sets a TCP connection into a listening TCP connection.

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

  netconn_listen(conn);

 

netconn_accept() function accepts an incoming connection on a listening TCP connection.

   err = netconn_accept(conn, &newconn);

netconn_recv() function receives a message from a netconn.

netconn_send() function sends data to the currently connected remote IP/Port.

 

In this demo, if server board receives a ‘TOGGLE’ message from the client board, it will toggle a LED. (GPIO1/3,  need to connect a LED manually)

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

        PRINTF("Received  %s\n", buf->p->payload);

        do {

             netbuf_data(buf, &data, &len);

             tcp_rx_buf = (void *)data;

 

             if (tcp_rx_buf[0] == 'T')

             {

                 PRINTF("LED was toggled from client\r\n");

                GPIO_PortToggle(EXAMPLE_LED_GPIO, 1u << EXAMPLE_LED_GPIO_PIN);

                netconn_write(newconn, TcpReply, sizeof(TcpReply) , NETCONN_COPY );

             }

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

        netbuf_delete(buf);

      } //end of while(( err

 

 

5.2 TCP CLIENT IMPLEMENTATION

For the client side, the netconn structure is created:

  conn = netconn_new(NETCONN_TCP);

netconn_connect():  when a user issues a connect command, the stack creates a connection with another host. Before connect can instruct the stack to establish a connection, the user must pass a netconn and a sockaddr_in structure containing the destination IP address and port. In TCP, the handshaking packets will be exchanged.

 

  memset(&server_addr, 0, sizeof(server_addr));

  server_addr.sin_family = AF_INET;

  server_addr.sin_port = PP_HTONS(TCP_CUSTOM_PORT);

  server_addr.sin_addr.s_addr = server_ip_addr.addr;

 

  err = connect(client_sock, (struct sockaddr *)&server_addr, sizeof(server_addr));

  LWIP_ASSERT("connect fail, please start TCP server first !  ",  err == 0);

 

In this demo, a GPIO is initialized and interrupt is enabled. When SW8 is pressed, the client board will send ‘Toggle’ command to the TCP server.

  while (1)

  {

      if (g_InputSignal)

      {

          delay();

          if (1 == GPIO_PinRead(EXAMPLE_SW_GPIO, EXAMPLE_SW_GPIO_PIN))

          {

              PRINTF("%s is turned on.\r\n", EXAMPLE_SW_NAME);

              err = send(server_sock, Sendtext, sizeof(Sendtext) / sizeof(Sendtext[0]), 0);

 

          }

          /* Reset state of switch. */

          g_InputSignal = false;

      }  //end of if (g_InputSignal)

  }

 

 

 

 

 

 

 

 

 

 

 

Version history
Revision #:
1 of 1
Last update:
‎10-26-2021 07:55 AM
Updated by: