UltimaSerial lwIP TCP Example: How to write a TCP echo server (telnet)
 

Data logger
UltimaSerial

 

Windaq add-ons
Windaq Add-ons

 

Spectrogram
UltimaWaterfall

 

Ultimaserial XChart
XChart

 

FFT1024
FFT1024

 

Ultimaserial Classroom
Lessons

lwIP (lightweightIP)  is a popular free TCP/IP stack for many embedded processors. The following workshop builds a TCP echo server based on lwIP. It is like the "Hello World" program in any programming language.

Here we pick a real piece of hardware from Atmel:  EVK1100. Its processor is 32UC3A0512, but the codes below should work in other hardware. 

Software Development Platform: AVR32 with lwIP and FreeRTOS. 

To write a TCP echo server on EVK1100, using lwIP on FreeRTOS, under AVR32

  1. AVR32->File->New->Example, select EVK1100-SERVICES-LWIP example
  2. Give a Project Name: MyTestFromLwIP, then click Finish. This will create a new project under the Project Explorer pane.
  3. Open MyTestFromLwIP->src->CONFIG->conf-eth.h, and change ETHERNET_CONF_IPADDR0..3 to whatever IP address you wish to use. For example, 192.168.0.223. If you wish to change the MAC also, it is just a few lines up 
  4. Instead of create a new thread, which will require better understanding of FreeRTOS, we will modify an existing thread to TCP echo server.
  5. Open MyTestFromLwIP->src->NETWORK->BasicTFTP-> BasicTFTP.c
  6. All roads lead to Rome: There are several approaches to get the job done
    1. Raw API programming
      • As the naming suggested, this is the lowest level of programming where the other two based upon.
      • Reference about raw API can be found under MyTestFromLwIP->src->SOFTWARE_FRAMWORK->SERVICES->lwip-1.3.0->doc->rawapi.txt
      • Replace  portTASK_FUNCTION( vBasicTFTPServer, pvParameters ) with this:

        char mydata[1024];

        static void close_conn(struct tcp_pcb *pcb){
              tcp_arg(pcb, NULL);
              tcp_sent(pcb, NULL);
              tcp_recv(pcb, NULL);
              tcp_close(pcb);
        }

        static
        err_t echo_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err){
              int
        i;
             
        int
        len;
             
        char
        *pc;

              if
        (err == ERR_OK && p != NULL) {
                    /* Inform TCP that we have taken the data. */
                    tcp_recved(pcb, p->tot_len
        );  

                    //pointer to the pay load
                    pc=(
        char
        *)p->payload

                    //size of the pay load
                    len =p->
        tot_len

                    //copy to our own buffer
                    for
        (i=0; i<len; i++)mydata[i]= pc[i]; 

                     //Close TCP when receiving "X" 
                   
        if
        (mydata[0]=='X')close_conn(pcb); 

                   //Free the packet buffer

                    pbuf_free(p);

                    //check output buffer capacity
                    if
        (len >tcp_sndbuf(pcb)) len= tcp_sndbuf(pcb); 
                    //Send out the data
                    err = tcp_write(pcb, mydata, len, 0);
                    
        tcp_sent(pcb, NULL); /* No need to call back */
              }
        else
        {
                    pbuf_free(p);
              }

              if
        (err == ERR_OK && p == NULL) {
                    close_conn(pcb);
              }
             
        return
        ERR_OK;
        }

        static
        err_t echo_accept(void *arg, struct tcp_pcb *pcb, err_t err){
              LWIP_UNUSED_ARG(arg);
              LWIP_UNUSED_ARG(err);
              tcp_setprio(pcb, TCP_PRIO_MIN);
              tcp_recv(pcb, echo_recv);
              tcp_err(pcb, NULL);
        //Don't care about error here
              tcp_poll(pcb, NULL, 4);
        //No polling here
              return
        ERR_OK;
        }

        portTASK_FUNCTION( vBasicTFTPServer, pvParameters ){
              struct
        tcp_pcb *ptel_pcb;
              ptel_pcb = tcp_new();
              tcp_bind(ptel_pcb, IP_ADDR_ANY, 23);

             
        while
        (1){
                    ptel_pcb = tcp_listen(ptel_pcb);
                    tcp_accept(ptel_pcb, echo_accept);
              }
        }

    2. Network connection programming 
      • netconn is a layer on top of the raw APIs
      • Reference for these functions can be found in " Design and Implemention of the lwIP TCP/IP Stack" by Adam Dunkels
      • Replace  portTASK_FUNCTION( vBasicTFTPServer, pvParameters ) with this:

        static void EchoRequest( struct netconn *pxNetCon ) {
               
        struct netbuf *pxRxBuffer;
                portCHAR *pcRxString;
               
        unsigned portSHORT usLength;
                
               
        pxRxBuffer = netconn_recv( pxNetCon );< /FONT >
                
        if
        ( pxRxBuffer != NULL ){
                        netbuf_data( pxRxBuffer, ( 
        void
        * ) &pcRxString, &usLength );
                        if
        (  pcRxString != NULL){
                                netconn_write( pxNetCon, pcRxString, (
        u16_t
        ) usLength, NETCONN_COPY );
                        }
                netbuf_delete( pxRxBuffer );
                }
        }

        portTASK_FUNCTION( vBasicTFTPServer, pvParameters ){
                struct
        netconn *pxTCPListener, *pxNewConnection;
                pxTCPListener = netconn_new(
        NETCONN_TCP
        );
                
             
           netconn_bind(pxTCPListener, NULL, 23 );
                netconn_listen( pxTCPListener );


               
        for( ;; ){
                        pxNewConnection = netconn_accept(pxTCPListener);
                        if
        (pxNewConnection != NULL){
                                EchoRequest(pxNewConnection);
                        }
                }
        }

    3. Socket programming:
      • BSD socket layer is built on top of the netconn layer.
      • Replace  portTASK_FUNCTION( vBasicTFTPServer, pvParameters ) with this :

        portTASK_FUNCTION( vBasicTFTPServer, pvParameters ){
        int
        lSocket;
        struct
        sockaddr_in sLocalAddr;

        lSocket = lwip_socket(AF_INET, SOCK_STREAM, 0);
        if
        (lSocket < 0) return;

        memset((
        char
        *)&sLocalAddr, 0, sizeof(sLocalAddr));
        sLocalAddr.
        sin_family
        = AF_INET;
        sLocalAddr.
        sin_len = sizeof
        (sLocalAddr);
        sLocalAddr.
        sin_addr.s_addr
        = htonl(INADDR_ANY);
        sLocalAddr.
        sin_port
        = 23;

        if
        (lwip_bind(lSocket, (struct sockaddr *)&sLocalAddr, sizeof(sLocalAddr)) < 0) {
                lwip_close(lSocket);
               
        return
        ;
        }

        if
        ( lwip_listen(lSocket, 20) != 0 ){
                lwip_close(lSocket);
               
        return
        ;
        }

        while
        (1) {
                int
        clientfd;
                struct
        sockaddr_in client_addr;
                int addrlen=sizeof
        (client_addr);
                char
        buffer[1024];
                int
        nbytes;

                clientfd = lwip_accept(lSocket, (
        struct
        sockaddr*)&client_addr, (socklen_t)&addrlen);
                if
        (clientfd>0){
                    do
        {
                       
        nbytes=lwip_recv(clientfd, buffer, sizeof(buffer),0);
                        if
        (nbytes>0) lwip_send(clientfd, buffer, nbytes, 0);
                    }
          while
        (nbytes>0);

                     lwip_close(clientfd);
                  }
            }
            lwip_close(lSocket);
        }

  7. Pick one of the approaches above, build and run it, then you can telnet it from a PC, uisng "telnet 192.168.0.223", and it will echo any letter you type
  8. Watch out, due to the way FreeRTOS handles priority, make sure you either keep all threads in the same priority or make sure the higher priority thread goes into blocked state periodically, such as vTaskDelay( 1 ), or the highest priority thread will never let the lower ones to run.

  Click here for more lwIP examples

Find out how to upgrade to the latest lwIP

 

 Last update: 05/18/12