¿Cómo crear un Servidor Web en el ESP32 con Arduino IDE? Aprende a controlar las Salidas desde el navegador

Libro Guía del Microcontrolador ESP32

En este proyecto vas a crear un servidor web autónomo con el ESP32 que controla dos LEDs usando el entorno de programación Arduino IDE. El servidor web es responsive (se adapta a móviles) y se puede acceder a él desde cualquier dispositivo con navegador dentro de la red local. Te mostraremos cómo crear el servidor web y cómo funciona el código paso a paso.

Si quieres aprender más sobre el ESP32 antes de empezar, te recomendamos leer primero la Guía de introducción al ESP32.

Resumen del proyecto: qué vas a construir

Antes de ir directos al proyecto, es importante describir qué hará nuestro servidor web para que sea más fácil seguir los pasos después:

El servidor web que vas a construir controla dos LEDs conectados al ESP32 en los GPIO 26 y GPIO 27. Puedes acceder al servidor web del ESP32 escribiendo su dirección IP en un navegador dentro de la red local. Al pulsar los botones que aparecen en la página web, puedes cambiar instantáneamente el estado de cada LED.

Este es solo un ejemplo simple para ilustrar cómo construir un servidor web que controle salidas. La idea es reemplazar esos LEDs por un relé u otro componente electrónico que quieras controlar: una lámpara, un ventilador, una persiana motorizada, etc. Con esta base, las aplicaciones son enormes.

Requisitos previos e instalación

Existe un complemento para Arduino IDE que te permite programar el ESP32 usando el lenguaje de programación de Arduino. Sigue el tutorial de instalación correspondiente a tu sistema operativo (Windows, macOS o Linux) antes de continuar.

Componentes necesarios

Para este tutorial necesitas: una placa de desarrollo ESP32 (en este ejemplo usamos la ESP32 DEVKIT DOIT con 36 pines), dos LEDs de 5 mm, dos resistencias de 330 Ω, una protoboard y cables jumper.

Esquema de conexión

Empieza construyendo el circuito. Conecta dos LEDs al ESP32: uno al GPIO 26 y otro al GPIO 27. Cada LED debe ir con su resistencia de 330 Ω en serie hacia GND, con el ánodo conectado al GPIO correspondiente.

Nota: Antes de montar el circuito, asegúrate de comprobar el pinout de tu placa concreta. La ubicación física de los GPIO cambia entre modelos, aunque la numeración lógica siempre se mantiene.

Código del servidor web del ESP32

Aquí te proporcionamos el código completo que crea el servidor web del ESP32. Copia el siguiente código en tu Arduino IDE, pero no lo subas todavía: primero necesitas hacer algunos cambios para que funcione en tu red.

/*
  Rui Santos
  Detalles completos del proyecto en https://randomnerdtutorials.com
*/

// Carga la librería WiFi
#include <WiFi.h>

// Reemplaza con las credenciales de tu red
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Establece el puerto del servidor web al 80
WiFiServer server(80);

// Variable para almacenar la petición HTTP
String header;

// Variables auxiliares para almacenar el estado actual de las salidas
String output26State = "off";
String output27State = "off";

// Asigna variables de salida a los pines GPIO
const int output26 = 26;
const int output27 = 27;

// Tiempo actual
unsigned long currentTime = millis();
// Tiempo previo
unsigned long previousTime = 0;
// Define el tiempo de timeout en milisegundos (ejemplo: 2000ms = 2s)
const long timeoutTime = 2000;

void setup() {
  Serial.begin(115200);
  // Inicializa las variables de salida como salidas
  pinMode(output26, OUTPUT);
  pinMode(output27, OUTPUT);
  // Establece las salidas a LOW
  digitalWrite(output26, LOW);
  digitalWrite(output27, LOW);

  // Conecta a la red WiFi con SSID y contraseña
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  // Imprime la dirección IP local e inicia el servidor web
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();
}

void loop(){
  WiFiClient client = server.available();   // Escucha clientes entrantes

  if (client) {                             // Si un nuevo cliente se conecta,
    currentTime = millis();
    previousTime = currentTime;
    Serial.println("New Client.");          // imprime un mensaje por el puerto serie
    String currentLine = "";                // crea un String para almacenar los datos entrantes del cliente
    while (client.connected() && currentTime - previousTime <= timeoutTime) {  // bucle mientras el cliente esté conectado
      currentTime = millis();
      if (client.available()) {             // si hay bytes para leer del cliente,
        char c = client.read();             // lee un byte, luego
        Serial.write(c);                    // lo imprime en el monitor serie
        header += c;
        if (c == '\n') {                    // si el byte es un salto de línea
          // si la línea actual está vacía, tienes dos saltos de línea seguidos.
          // ese es el final de la petición HTTP del cliente, así que envía una respuesta:
          if (currentLine.length() == 0) {
            // Las cabeceras HTTP siempre empiezan con un código de respuesta (ej. HTTP/1.1 200 OK)
            // y un content-type para que el cliente sepa qué viene, luego una línea en blanco:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();

            // Enciende y apaga los GPIOs
            if (header.indexOf("GET /26/on") >= 0) {
              Serial.println("GPIO 26 on");
              output26State = "on";
              digitalWrite(output26, HIGH);
            } else if (header.indexOf("GET /26/off") >= 0) {
              Serial.println("GPIO 26 off");
              output26State = "off";
              digitalWrite(output26, LOW);
            } else if (header.indexOf("GET /27/on") >= 0) {
              Serial.println("GPIO 27 on");
              output27State = "on";
              digitalWrite(output27, HIGH);
            } else if (header.indexOf("GET /27/off") >= 0) {
              Serial.println("GPIO 27 off");
              output27State = "off";
              digitalWrite(output27, LOW);
            }

            // Muestra la página web HTML
            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:,\">");
            // CSS para dar estilo a los botones on/off
            client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
            client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;");
            client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
            client.println(".button2 {background-color: #555555;}</style></head>");

            // Encabezado de la página web
            client.println("<body><h1>ESP32 Web Server</h1>");

            // Muestra el estado actual y los botones ON/OFF para GPIO 26
            client.println("<p>GPIO 26 - State " + output26State + "</p>");
            if (output26State=="off") {
              client.println("<p><a href=\"/26/on\"><button class=\"button\">ON</button></a></p>");
            } else {
              client.println("<p><a href=\"/26/off\"><button class=\"button button2\">OFF</button></a></p>");
            }

            // Muestra el estado actual y los botones ON/OFF para GPIO 27
            client.println("<p>GPIO 27 - State " + output27State + "</p>");
            if (output27State=="off") {
              client.println("<p><a href=\"/27/on\"><button class=\"button\">ON</button></a></p>");
            } else {
              client.println("<p><a href=\"/27/off\"><button class=\"button button2\">OFF</button></a></p>");
            }
            client.println("</body></html>");

            // La respuesta HTTP termina con otra línea en blanco
            client.println();
            // Sale del bucle while
            break;
          } else { // si tienes un salto de línea, limpia currentLine
            currentLine = "";
          }
        } else if (c != '\r') {  // si tienes cualquier otra cosa menos un retorno de carro,
          currentLine += c;      // lo añade al final de currentLine
        }
      }
    }
    // Limpia la variable header
    header = "";
    // Cierra la conexión
    client.stop();
    Serial.println("Client disconnected.");
    Serial.println("");
  }
}

Configurar tus credenciales de red

Necesitas modificar las siguientes líneas con las credenciales de tu red: el SSID y la contraseña. El código está bien comentado indicando dónde debes hacer los cambios:

const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Subir el código al ESP32

Ahora puedes subir el código y el servidor web funcionará inmediatamente. Sigue estos pasos para subir código al ESP32:

Paso 1: Conecta tu placa ESP32 al ordenador por USB.

Paso 2: En Arduino IDE, selecciona tu placa en Herramientas > Placa (en nuestro caso, la ESP32 DEVKIT DOIT).

Paso 3: Selecciona el puerto COM en Herramientas > Puerto.

Paso 4: Pulsa el botón de subir en Arduino IDE y espera unos segundos mientras el código se compila y se sube a tu placa.

Paso 5: Espera el mensaje “Done uploading” (Carga completada).

Encontrar la dirección IP del ESP32

Después de subir el código, abre el Monitor Serie a 115200 baudios. Pulsa el botón EN del ESP32 (reset). El ESP32 se conecta al Wi-Fi y muestra su dirección IP en el Monitor Serie. Copia esa dirección IP, porque la necesitarás para acceder al servidor web.

Acceder al servidor web

Para acceder al servidor web, abre tu navegador, pega la dirección IP del ESP32 y verás la página del servidor. En nuestro caso, la IP era 192.168.1.135, pero la tuya será diferente según tu red.

Si echas un vistazo al Monitor Serie, puedes ver qué está pasando en segundo plano. El ESP32 recibe una petición HTTP de un nuevo cliente (en este caso, tu navegador). También puedes ver otra información sobre la petición HTTP, como los headers y la URL solicitada.

Probar el servidor web

Ahora puedes probar si tu servidor web funciona correctamente. Pulsa los botones para controlar los LEDs. Al mismo tiempo, puedes echar un vistazo al Monitor Serie para ver qué está pasando en segundo plano.

Por ejemplo, cuando pulsas el botón para encender el GPIO 26, el ESP32 recibe una petición en la URL /26/on. Cuando el ESP32 recibe esa petición, enciende el LED conectado al GPIO 26 y actualiza su estado en la página web.

El botón del GPIO 27 funciona de forma similar. Prueba que todo funciona correctamente.

Cómo funciona el código paso a paso

En esta sección vamos a analizar el código en detalle para ver cómo funciona cada parte.

Librerías y credenciales

Lo primero que necesitas hacer es incluir la librería WiFi:

#include <WiFi.h>

Como mencionamos antes, debes insertar tu SSID y contraseña entre las comillas dobles de las siguientes líneas:

const char* ssid = "";
const char* password = "";

El puerto del servidor

Después, se establece el servidor web en el puerto 80, el puerto estándar para HTTP:

WiFiServer server(80);

Variables auxiliares

La siguiente línea crea una variable para almacenar la cabecera de la petición HTTP:

String header;

Después, se crean variables auxiliares para almacenar el estado actual de tus salidas. Si quieres añadir más salidas y guardar su estado, necesitas crear más variables:

String output26State = "off";
String output27State = "off";

También necesitas asignar un GPIO a cada una de tus salidas. Aquí usamos GPIO 26 y GPIO 27, pero puedes usar cualquier otro GPIO adecuado:

const int output26 = 26;
const int output27 = 27;

La función setup()

Ahora veamos el setup(). Primero, se inicia la comunicación serie a 115200 baudios con fines de depuración:

Serial.begin(115200);

También se definen los GPIOs como OUTPUT y se ponen a LOW:

pinMode(output26, OUTPUT);
pinMode(output27, OUTPUT);
digitalWrite(output26, LOW);
digitalWrite(output27, LOW);

Las siguientes líneas inician la conexión Wi-Fi con WiFi.begin(ssid, password), esperan a que la conexión se establezca e imprimen la dirección IP del ESP32 en el Monitor Serie:

WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
  delay(500);
  Serial.print(".");
}
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
server.begin();

La función loop()

En el loop() programamos qué ocurre cuando un nuevo cliente establece una conexión con el servidor web. El ESP32 está siempre escuchando clientes entrantes con la siguiente línea:

WiFiClient client = server.available();

Cuando se recibe una petición de un cliente, guardamos los datos entrantes. El bucle while que sigue se ejecutará mientras el cliente permanezca conectado. No recomendamos cambiar la siguiente parte del código a menos que sepas exactamente lo que estás haciendo.

Dentro del bucle, el ESP32 lee byte a byte la petición HTTP entrante. Cuando detecta dos saltos de línea consecutivos (\n), sabe que la petición HTTP ha terminado y envía la respuesta:

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

Estas líneas envían los headers HTTP: código de respuesta 200 OK, tipo de contenido text/html, y cierre de conexión.

Detectar qué botón se ha pulsado

La siguiente sección de sentencias if y else comprueba qué botón se pulsó en tu página web y controla las salidas en consecuencia. Como hemos visto antes, hacemos una petición a diferentes URLs según el botón pulsado:

if (header.indexOf("GET /26/on") >= 0) {
  Serial.println("GPIO 26 on");
  output26State = "on";
  digitalWrite(output26, HIGH);
} else if (header.indexOf("GET /26/off") >= 0) {
  Serial.println("GPIO 26 off");
  output26State = "off";
  digitalWrite(output26, LOW);
} else if (header.indexOf("GET /27/on") >= 0) {
  Serial.println("GPIO 27 on");
  output27State = "on";
  digitalWrite(output27, HIGH);
} else if (header.indexOf("GET /27/off") >= 0) {
  Serial.println("GPIO 27 off");
  output27State = "off";
  digitalWrite(output27, LOW);
}

Por ejemplo, si has pulsado el botón ON del GPIO 26, el ESP32 recibe una petición en la URL /26/on (puedes ver esa información en la cabecera HTTP del Monitor Serie). Así que comprobamos si la cabecera contiene la expresión GET /26/on. Si la contiene, cambiamos la variable output26State a “on” y el ESP32 enciende el LED.

Esto funciona de forma similar para los otros botones. Si quieres añadir más salidas, deberías modificar esta parte del código para incluirlas.

Mostrar la página HTML

Lo siguiente que necesitas hacer es crear la página web. El ESP32 enviará una respuesta a tu navegador con código HTML para construir la página web. La página web se envía al cliente usando la expresión client.println(). Debes poner lo que quieras enviar al cliente como argumento.

Lo primero que debemos enviar siempre es la siguiente línea, que indica que estamos enviando HTML:

client.println("<!DOCTYPE html><html>");

Después, la siguiente línea hace que la página web sea responsive en cualquier navegador:

client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");

Estilos CSS de la página

Después, tenemos algo de texto CSS para dar estilo a los botones y a la apariencia de la página web. Elegimos la fuente Helvetica, definimos el contenido para que se muestre como un bloque y alineado al centro:

client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");

Estilizamos los botones con color #4CAF50 (verde), sin borde, texto en blanco y con padding 16px 40px. También establecemos text-decoration: none, definimos el tamaño de fuente, el margen y el cursor como puntero:

client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;");
client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");

También definimos el estilo para un segundo botón, con las mismas propiedades del botón anterior pero con un color diferente. Este será el estilo para el botón OFF:

client.println(".button2 {background-color: #555555;}</style></head>");

El encabezado de la página

En la siguiente línea puedes establecer el primer encabezado de tu página web. Aquí tenemos “ESP32 Web Server”, pero puedes cambiar este texto por lo que quieras:

client.println("<h1>ESP32 Web Server</h1>");

Mostrar los botones y su estado

Después, se escribe un párrafo para mostrar el estado actual del GPIO 26. Como puedes ver, usamos la variable output26State, para que el estado se actualice al instante cuando esta variable cambie:

client.println("<p>GPIO 26 - State " + output26State + "</p>");

Después, mostramos el botón ON u OFF dependiendo del estado actual del GPIO. Si el estado actual del GPIO es OFF, mostramos el botón ON; si no, mostramos el botón OFF:

if (output26State=="off") {
  client.println("<p><a href=\"/26/on\"><button class=\"button\">ON</button></a></p>");
} else {
  client.println("<p><a href=\"/26/off\"><button class=\"button button2\">OFF</button></a></p>");
}

Usamos el mismo procedimiento para el GPIO 27.

Cerrar la conexión

Finalmente, cuando la respuesta termina, limpiamos la variable header y detenemos la conexión con el cliente usando client.stop():

header = "";
client.stop();

Conclusión: lo que has aprendido

En este tutorial te hemos mostrado cómo construir un servidor web con el ESP32. Te hemos mostrado un ejemplo simple que controla dos LEDs, pero la idea es reemplazar esos LEDs por un relé u otra salida que quieras controlar: una lámpara, un ventilador, una persiana, un sistema de riego, etc.

El servidor web es la base de la mayoría de proyectos IoT caseros. Una vez lo domines, podrás controlar cualquier dispositivo desde tu móvil o tu ordenador dentro de la red local, y con un poco más de trabajo, también desde fuera de casa.

Si quieres seguir profundizando en el ESP32, los siguientes pasos naturales son añadir autenticación al servidor, mejorar la interfaz web con estilos más elaborados, integrar el servidor con servicios en la nube o combinarlo con otros sensores para mostrar datos en tiempo real.

Vistas: 0

Descubre más desde CIBERED

Suscríbete y recibe las últimas entradas en tu correo electrónico.

Scroll al inicio