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

  Click here for more lwIP examples

Find out how to upgrade to the latest lwIP

 

 

 

 Last update: 04/08/10