Ejemplo de servidor TCP ESP8266

Hay montones de ejemplos de servidor TCP utilizando ESP8266WiFi. Están pensados para hacer peticiones sin estado, al estilo del protocolo HTTP. En este tipo de peticiones, el cliente abre la conexión, envía la petición, recibe la respuesta y luego cierra la conexión, por eso se dice que no tienen estado. Aqui hay un ejemplo de este código, para un servidor HTTP, que es un servidor sin estado (stateless).

El modelo de programación consiste en realizar la gestión de la transacción en el bloque loop del programa. En dicho bucle, se crea y se destruye el objeto cliente. Eso supone que el cliente tiene que volver a abrir la conexión en la siguiente conexión.

Este modelo no es adecuado para clientes que abren la conexión una vez y esperan que el servidor no la cierre. Un ejemplo de este tipo de conexiones es el de los drivers de indilib.

Si necesitas crear un servidor TCP que no cierre la conexión, es conveniente organizar el codigo de otra forma.

Aqui comparto una solución de ejemplo de código.

/*
   SocketServer.ino

    Created on: 4.11.2023
    Author: miceno.atreides@gmail.com

*/

#include <ESP8266WiFi.h>

#define MAX_BAUD_RATE 115200
String strData = "";             // a string to hold incoming data

uint16_t port = 8888;            // Port number
WiFiServer server(port);

// WiFi parameters
const char* ssid = "MOVISTAR_3887";
const char* password = "r5PP867mGjxKX329YhSj";
WiFiClient client;

void setup() {
  Serial.begin(MAX_BAUD_RATE);
  while (Serial.available() > 0)
    ;

  WiFi.disconnect();  //Prevent connecting to wifi based on previous configuration
  ESP.eraseConfig();

  WiFi.mode(WIFI_STA);  // WiFi mode station (connect to wifi router only)

  // Connect to WiFi
  WiFi.begin(ssid, password);
  Serial.println();
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(200);
  }
  Serial.println("\nWiFi connected");
  Serial.print("START MIRRORING SERIAL: ");
  Serial.println(WiFi.localIP());

  // Start server
  server.begin();
}

void loop() {
  // Serial.println("looping..."); // DEBUG
  if (!client){
    // Only request a client in case there is no valid one.
    // The client should persist on the loop so it is a global variable.
    client = server.available();
  }

  if (client) {
    // Serial.println("client...");
    while (client.available() > 0) {
      char c;
      // Serial.println("data is available...");
      // read data from the connected client
      c = client.read();
      strData += c;
    }

    // Serial.println("\nNo more data available");
    if( strData.length()>0){
      Serial.printf("Message received is %s\n", strData.c_str());
      client.write("Response received");
      client.flush();
      strData = "";
    }
  }
}

La clave está en el código marcado:

if (!client){
    // Only request a client in case there is no valid one.
    // The client should persist on the loop so it is a global variable.
    client = server.available();
  }

En este caso, solamente debemos obtener un objeto WiFiClient si no tenemos uno disponible anteriormente. Se trata de obtener uno cuando sea necesario o cuando no esté disponible el anterior. Además, debemos asegurarnos de no llamar a client.stop() en el propio bucle, porque si no, cerraríamos la conexión y el cliente tendría que volver a abrirla.

Este patron de desarrollo se basa en la sobrecarga del operador bool en la clase WiFiClient.

Para verificar que esta solucion es robusta, puedes cargar el sketch anterior en un ESP8266, y por otro lado, desde un PC conectado a la misma WiFi, puedes construir un programa en Python para hacer de cliente. El programa Python abre la conexión con el servidor una única vez y envia continuamente peticiones.

De esta forma hemos construido un servidor TCP persistente, utilizando las librerías básicas de ESP8266. También puedes mejorar el codigo utilizando una librería asíncrona como ESPAsyncTCP.