Implementation of LWIP select ESP8266 FreeRTOS

In this article i will explain how to implement no block receiving over ESP8266. This project is in github

File in sum:

Source:

  • main.c: Entry point of oars program
  • private_ssid_config.h: SSID, end Wi-Fi Password.
  • reciver.c: Implement function that receive and transmit data
  • reciver.h: Header of all task

Related File:

  • .clang_complete: Configuration file for Clang Extension
  • Makefile: Makefile of project

Main of the program:

//<< main.c >>\\
void user_init(void) {
  uart_set_baud(0, 115200);
  printf("SDK version:%s\n", sdk_system_get_sdk_version());

  struct sdk_station_config config = {
    .ssid     = WIFI_SSID,
    .password = WIFI_PASS,
  };

  /* required to call wifi_set_opmode before station_set_config */
  sdk_wifi_set_opmode(STATION_MODE);
  sdk_wifi_station_set_config(&config);
  

  // Creo il task per il Server TCP
  xTaskCreate(&Task_ServerTCP, "Echo_Server", 256, NULL, s 2, NULL);
}

Echo server implementation:

Implementation of this echo server it's a copy of standard LWIP example with some fix. It use in high level library that do not look like C, no filedescriptor.We can analyze source code in details

void Task_ServerTCP(void *pvParameters) {
  struct netconn *conn, *newconn;
  err_t err;
  
  // Remove this useless warning
  LWIP_UNUSED_ARG(pvParameters);

  /* Alloc IP connection identifier */
  conn = netconn_new(NETCONN_TCP);

  /* 
   * Make bind to the port, over all ESP8266 interfaces.
   * I defined i MACRO for keep port Number, code readable increase.
   */
  netconn_bind(conn, NULL, PORT);
  printf("Binded Port:%d", PORT);

  /* Start listen over this connection  */
  netconn_listen(conn);

  while (true) {
    /* 

     * Ours program will block over this syscall, it will unlock on incoming connection.
     * When program is locked over system call, FreeRTOS will scheduled other task.
     * This echo server is implemented for accept only one connection for time, with this 
     * approach we must wait, connection close.
     * conn, is a listen connection, every time that new connection incoming, API instance new
     * connection called newconn
     */
    err = netconn_accept(conn, &newconn);
   
    /* Setup newconn NO-Block */
    netconn_set_recvtimeout(newconn, TIMEOUT_SELECT);// Timeout of sys call
    netconn_set_nonblocking(newconn, true);// Set Non Block for this connection

    printf("Accepted new connection\n");

    /* Control that newconn is OK */
    if (err == ERR_OK) {
      struct netbuf *buf;
      void  *data;
      u16_t  len;
      size_t Writed;

     /*
      * This is the heart of the program.
      * Check if something has been received with netconn_recv
      * Put return value in err and switch it.
      * If ERR_OK, something has been received.
      * if ERR_TIMEOUT, timeout occur and system call unlock this call.
      */
      while ((err = netconn_recv(newconn, &buf)),
             (err == ERR_OK || err == ERR_TIMEOUT)) {
        /*printf("Recved\n");*/
        switch (err) {
        case ERR_OK: {
          /*
           * ERR_OK, Some data has been received, are into buff variable.
           * So now move data from buff, to data, and setup len value.
           */
          do {
            netbuf_data(buf, &data, &len);
            Writed = 0;

            do { // Flush Buffer
              /*
               * Use write partly for write received data. We return same message.
               * Repeat while buffer is not empty.
               */
              netconn_write_partly(newconn, data, len, NETCONN_COPY, &Writed);
            } while (Writed != len);

            // err = netconn_write(newconn, data, len, NETCONN_COPY);
          } while (netbuf_next(buf) >= 0);// Continue while all is not received
          netbuf_delete(buf);// free memory.
        } break;

        case ERR_TIMEOUT: {
          // printf("Time out\r\n");
        /*
         * If recv receive no think every timeout unlock code.
         * In this section we can handle all time operation.
         */
          break;
        }

        default: continue;// Default return on recv
        }
      }
      // If client close we free memory
      netconn_close(newconn);
      netconn_delete(newconn);
    }
  }
}

Possible improvement

Response immediately to IO

Server keep same struct at every incoming transmission server return response message. This struct cannot work if response require sometime to be processed or executed. More physical, Axis motor.

pros:

  • Very simple code

cons:

  • low elastic

Receiving transmitting over Buffer:

Response: All data must e copied into buffer or queue that could contains enough data, you must decide how much enough is big.

Transmission: We must check that send queue is void every time, and send packet that could be send.

Execution time:

Is very import setup timeout, to be enough, or we use many tick that could be used from other task, or continue interrupt execution of other task, with lower priority without valid reason. Remember that contextual switch take operations. With long elaboration time, could be that you must open a new task, or broke execution of long task in many part, loop are very critical in this part of code.