WebServer: Usa tu ESP32 como un servidor web

¿Se te ha ocurrido tener una página web en tu ESP32? Así es, puedes guardar una página dentro un ESP32 para mostrarla cuando alguien acceda a la dirección IP del mismo. Y aún más interesante es que desde esa página se pueden enviar instrucciones al ESP32.

WebServer es una de las funciones que puede desempeñar el ESP32. Como lo indica el nombre de la función, el ESP32 actuará como un servidor web. Esto quiere decir que alojará la página, recibirá solicitudes de clientes (otros equipos que accedan) y enviará respuestas a los mismos.

Se aplica protocolo HTTP para la comunicación entre cliente y servidor por lo que se puede hacer uso de los métodos GET y POST.

Utilidad de usar el ESP32 como WebServer

¿Qué utilidad tiene esto? El ejemplo que usaremos para este tutorial es el de almacenar una página web que podrás ver desde el navegador usando la IP del ESP32. Desde esa página podrás encender y apagar un diodo led que está soldado a la tarjeta de desarrollo.

Programación

En este ejemplo se usa el método de envío que se está utilizando es GET, por lo que la información de las solicitudes HTTP están directamente en la URL.

/*
* WebServer con el ESP32 para encender diodo led
* de la placa
* Henry Mera
* https://www.todomaker.com 
*/

 
#include <WiFi.h>       // Añade ibreria
#define LED 2           // Indica pin que se usará (D2)

String p="off";         // dato de apagado del led

// Credenciales de wifi 
// Reemplazar con datos de tu red
const char *ssid = "SSID";
const char *password = "Contraseña";

// Crea servidor llamado "server" y establece el puerto 80 para acceder
WiFiServer server(80);      

void setup() {
  //Configura D2 como salida y lo pone en un estado bajo (0)
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);

  //Inicia puerto serial a velocidad de 115200 baudios
  Serial.begin(115200);  
  Serial.println();
  Serial.println("Configurando");

  // Inicia conexión WiFi con las datos de la red especificados
  WiFi.begin(ssid, password);     //configurando las credenciales

  Serial.print("Conectandome");
  while (WiFi.status() != WL_CONNECTED)    // Espera conexión a la red
  {
    delay(500);
    Serial.print(".");                      // Imprime puntos 
  }

  Serial.println();
  Serial.print("Conectado, La dirección IP es: ");
  Serial.println(WiFi.localIP());                    // Imprime la IP del ESP32
 
  server.begin();                                   // Inicia el servidor
  Serial.println("Servidor iniciado");
}

void loop() {
  WiFiClient client = server.available();   // Recibe las conexciones de clientes

  if (client) {                             // Si hay un cliente
    Serial.println("Nuevo cliente.");       // Indica el acceso de un cliente
    String currentLine = "";                // Variable para datos de fin de linea
    while (client.connected()) {            // Cliente conectado
      if (client.available()) {             // Datos disponibles para ser leido
        char c = client.read();             // Lectura de byte en variable C
        Serial.write(c);                    // Muestra los datos
        
        if (c == '\n') {                    // Si el byte es un caracter de nuevo salto de linea

          if (currentLine.length() == 0) {    // Si no hay caracteres, entonces lanza el codgo HTML
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();
            
            client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");

            client.println("<style>html{font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
            client.println(".button {border: none;color: white;padding: 15px 32px; text-align: center;");
            client.println("text-decoration: none;display: inline-block;font-size: 16px; margin: 4px 2px;cursor: pointer;}");
            client.println(".button1 {background-color: #4CAF50;} /* Green */");
            client.println(".button2 {background-color: #008CBA;} /* Blue */");
            client.println("</style></head>");
            
            // Creación de botones 
            client.print("<body><h1>WebServer con ESP32</h1>");

            if(p=="off"){
              client.println("<button type='button' class='button button1' onClick=location.href='/LED=ON'> ENCENDER </button><br><br>");                          
                }
            else{
              client.println("<button type='button' class='button button2' onClick=location.href='/LED=OFF'> APAGAR </button><br><br>");                                               
              }
         
            client.print("</body></html>");
            client.println();
            
            // Rompe el codigo del while-loop
            break;
          } else {    // Limpiando variable
            currentLine = "";
          }
        } else if (c != '\r') {  // Si no hay retorno de carro
          currentLine += c;      // agrega al final de la linea
        }

        // Revisando el datos recibido del url
        if (currentLine.indexOf("GET /LED=ON") != -1) {
          digitalWrite(LED, HIGH);               // GET /LED=ON
          p="on";
        }
        if (currentLine.indexOf("GET /LED=OFF") != -1) {
          digitalWrite(LED, LOW);                // GET /LED=OFF
          p="off";
        }
      }
    }
    // cerrando la conexión
    client.stop();
    Serial.println("Cliente Desconectado");
  }
}

Explicación del código

Empezamos agregando la librería WiFi.h e indicando el pin del ESP32 que se usará para controlar el LED. También un dato de control que se usará más adelante con el WebServer.

Creamos una variables que guardarán los datos de la red WiFi a la que se conectará el ESP32. Estos datos son el nombre de la red (SSID) y la contraseña. Esta puede ser la red que tienes en tu casa o incluso una que hayas creado aunque no tenga acceso a internet.

Concluimos creando un servidor llamado server que operará mediante el puerto 80.

#include <WiFi.h>       // Añade ibreria
#define LED 2           // Indica pin que se usará (D2)

String p="off";         // dato de apagado del led

// Credenciales de wifi
// Reemplazar con datos de tu red
const char *ssid = "SSID";
const char *password = "Contraseña";

// Crea servidor llamado "server" y establece el puerto 80 para acceder
WiFiServer server(80);  

Dentro se setup configuraremos el pin para el led como salida y que inicie en un estado lógico de 0 (LOW) o apagado.

pinMode(LED, OUTPUT);
digitalWrite(LED, LOW);

Iniciamos el monitor serial con una velocidad de 115200 baudios. Posteriormente imprimimos un mensaje por el mismo.

  Serial.begin(115200);  
  Serial.println();
  Serial.println("Configurando");

Iniciaremos la conexión WiFi usando los datos de SSID y contraseña declarados anteriormente.

WiFi.begin(ssid, password);

Este ciclo imprime un punto tras otro mientras no se establezca conexión.

  while (WiFi.status() != WL_CONNECTED)    // Espera conexión a la red
  {
    delay(500);
    Serial.print(".");                      // Imprime puntos 
  }

Concluimos esta parte imprimiendo la IP que el módem le ha asignado al ESP32 e iniciando el servidor que anteriormente nombramos como server.

Serial.println();
Serial.print("Conectado, La dirección IP es: ");
Serial.println(WiFi.localIP());                    // Imprime la IP del ESP32
 
server.begin();                                   // Inicia el servidor
Serial.println("Servidor iniciado");

En la sección loop agregaremos el código correspondiente para mostrar el sitio web embebido en el ESP32. Desde allí también se realizan los cambios de acuerdo a las solicitudes realizadas desde el cliente.

Las solicitudes que ocurrirán al presionar un botón en la pantalla se hacen mediante el método GET, por lo que las mismas serán visibles desde la URL de la página.

Empezamos creando un cliente al que se nombrará client. Un cliente será cualquier dispositivo de la red que entré a la dirección del ESP32.

WiFiClient client = server.available();

Se crea una condición en la que si el cliente al entrado se avise mediante el monitor serial. Además se crea una variable llamada currentLine que usaremos después.

if (client) {                             // Si hay un cliente
    Serial.println("Nuevo cliente.");       // Indica el acceso de un cliente
    String currentLine = "";

Usaremos un ciclo while. Mientras el cliente esté conectado (connected) y haya datos (available) se guardarán los datos en una variable char llamada c y se imprimiran esos datos leídos del cliente.

    while (client.connected()) {            // Cliente conectado
      if (client.available()) {             // Datos disponibles para ser leido
        char c = client.read();             // Lectura de byte en variable C
        Serial.write(c);                    // Muestra los datos

Se crean unas condiciones en las que se toma en cuenta si el dato leído es un salto de línea (\n) y la longitud del valor (length) de currentLine es igual a 0 para que se cumplan.

if (c == '\n') {
  if (currentLine.length() == 0) {

Si las condiciones anteriores se cumplen se despliega el sitio web desde el ESP32 hacia el cliente en formato HTML. Para ello se llama a client y se le da la instrucción println para imrpimir el HTML una línea a la vez.

La parte que se está viendo corresponde a los parámetros y cabecera (head) de un documento HTML. Dentro de la cabecera se indican datos que no se suelen visualizar al navegar en la página. Por ejemplo, los estilos (style) CSS de los botones (.button) que se usarán después.

client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();

client.println("<!DOCTYPE html><html>");
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<link rel=\"icon\" href=\"data:,\">");

client.println("<style>html{font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
client.println(".button {border: none;color: white;padding: 15px 32px; text-align: center;");
client.println("text-decoration: none;display: inline-block;font-size: 16px; margin: 4px 2px;cursor: pointer;}");
client.println(".button1 {background-color: #4CAF50;} /* Green */");
client.println(".button2 {background-color: #008CBA;} /* Blue */");
client.println("</style></head>");

Procedemos a crear el cuerpo (body) de la página. Se muestra un título usando la etiqueta H1.

Después usando la variable de estado llamada p se decide el texto y estilo que tendrá le botón para encender o apagar el led.

Recordemos que la variable tenía el valor “off” y programamos el led para que empiece estando apagado. Por ello se mostrará el botón con el texto “ENCENDER” para indicarnos que esa será la acción que podremos realizar.

Siguiendo con el botón “ENCENDER” hay que mencionar que al hacer clic (onClick) sobre él nos llevará a la dirección “/LED-ON” (location.href). Este cambio será visible en la URL.

client.print("<body><h1>WebServer con ESP32</h1>");

if (p == "off") {
  client.println("<button type='button' class='button button1' onClick=location.href='/LED=ON'> ENCENDER </button><br><br>");
}
else {
  client.println("<button type='button' class='button button2' onClick=location.href='/LED=OFF'> APAGAR </button><br><br>");
}

client.print("</body></html>");
client.println();
break;
}

En caso de que la longitud del valor de la variable currentLine no sea 0 se procede a asignarle un valor vacío. Y que se si no hay un caracter de retorno de carro en c todo se concatene en currentLine.

else {    // Limpiando variable
  currentLine = "";
}
} else if (c != '\r') {  // Si no hay retorno de carro
  currentLine += c;      // agrega al final de la linea
}

La siguiente sección del código busca dentro de currentLine las direcciones que obtenemos al hacer clic en el botón de la página y modifica el estado del led de acuerdo a ello.

Por ejemplo. Si dentro hallamos “GET /LED=ON” quiere decir que presionamos el botón “ENCENDER”. En consecuencia, el pin del led se activará (HIGH) y la variable p cambiará a “on”.

Recuerda que en el código hay una sección que toma en cuenta el valor de p. En este caso al tener “on” el texto del botón será “APAGAR” y al hacer clic sobre él nos dirigirá a la dirección “/LED=ON”. Y así sucesivamente mientras se use el botón.

if (currentLine.indexOf("GET /LED=ON") != -1) {
  digitalWrite(LED, HIGH);               // GET /LED=ON
  p = "on";
}
if (currentLine.indexOf("GET /LED=OFF") != -1) {
  digitalWrite(LED, LOW);                // GET /LED=OFF
  p = "off";
}

Finalmente se cierra la conexión con el cliente.

client.stop();
Serial.println("Cliente Desconectado");

Demostración

Cuando el código empieza a ejecutarse podemos ver la dirección IP en el monitor serial.

Ahora debemos entrar a esa dirección desde el navegador de otro dispositivo conectado a la red, como un teléfono o computadora.

Cuando el sitio cargue veremos el título y botón que se creó con HTML en el ESP32.

Al mismo tiempo veremos un aviso de “nuevo cliente” en el monitor serial.

Aviso de nuevo cliente conectado.

El led del ESP32 inicia apagado y al podemos cambiar su estado al presionar el botón que dice “ENCENDER”.

ESP32 conectado con la luz del pin D2 apagada.
ESP32 conectado con la luz del pin D2 encendida.

También podremos ver el cambio en la URL del sitio al presionar el botón.

Webserver con luz apagada
Webserver con luz encendida

Conclusiones

El ESP32 puede funcionar como un servidor web (WebServer) para cumplir las funciones de guardar un sitio web y atender las solicitudes de clientes.

Se puede interactuar con el ESP32 mediante la página web embebida así como mostrar información.

Como cualquier otro servidor web se hace uso del protocolo HTTP para comunicarnos con los clientes.

Te invitamos a tomar el curso de Introducción al ESP32: Introducción al ESP32 | TodoMaker’s School

Previous Post
Next Post