Crear un servidor web ESP32 con WebSockets en Arduino IDE
Imagina un proyecto en el que puedas controlar una bombilla a través de WiFi usando un ESP32. Aunque la implementación inicial puede parecer sencilla, ya que solo se necesita configurar el ESP32 en modo soft-AP o STA para servir una página web que muestre el estado del interruptor de la luz, hay un problema significativo. Cuando múltiples usuarios intentan controlar la bombilla simultáneamente, las interfaces de los navegadores pueden desincronizarse y no reflejar el estado real de la bombilla. Afortunadamente, la solución se encuentra en el uso del protocolo de comunicación WebSocket, que permite una comunicación bidireccional en tiempo real. Este tutorial te guiará a través del proceso de creación de un servidor web usando WebSockets con el ESP32, asegurando que el estado de la bombilla esté siempre sincronizado entre todos los usuarios conectados.
- ¿Qué es WebSocket?
- Descripción del proyecto
- Configuración del entorno de desarrollo Arduino
- Subiendo el código al ESP32
- Demostración del proyecto
- Explicación detallada del código
- Importación de bibliotecas necesarias
- Definición de constantes y variables
- Inicialización del servidor web y WebSocket
- Creación de la página web
- CSS para el diseño
- HTML del contenido de la página
- JavaScript para la funcionalidad del cliente
- Función setup()
- Función loop()
- Manejo de eventos WebSocket en el servidor
- Lectura de mensajes y transmisión
- Procesamiento de marcadores de posición en la página web
¿Qué es WebSocket?
WebSocket es un protocolo de comunicación que permite la comunicación bidireccional y en tiempo real entre un cliente y un servidor. A diferencia de una conexión HTTP tradicional, donde el cliente envía una solicitud y luego espera una respuesta del servidor antes de que se cierre la conexión, WebSocket mantiene la conexión abierta. Esto significa que tanto el cliente como el servidor pueden enviar y recibir mensajes en cualquier momento.
Las características clave de WebSocket incluyen:
- Conexión persistente: La conexión permanece abierta, lo que elimina la necesidad de volver a establecerla continuamente.
- Comunicación bidireccional: Tanto el cliente como el servidor pueden iniciar el envío de datos.
- Menor latencia: Al eliminar la sobrecarga de las solicitudes HTTP, se reduce el tiempo de respuesta.
- Ideal para aplicaciones en tiempo real: WebSocket es perfecto para aplicaciones que requieren actualizaciones instantáneas, como chats o control de dispositivos IoT.
A pesar de sus ventajas, es crucial recordar que implementar WebSockets puede añadir complejidad a tu proyecto. Por lo tanto, es recomendable utilizarlo solo en situaciones donde se necesite la transmisión de datos a múltiples clientes simultáneamente.
Descripción del proyecto
En este proyecto, utilizaremos el ESP32 para crear un servidor WebSocket que controle un LED incorporado mediante una interfaz web. La idea es crear una página que tenga un interruptor que permita a los usuarios cambiar el estado del GPIO 2, que está conectado al LED. Esta interfaz no solo mostrará el estado del LED, sino que también permitirá que cualquier acción realizada en un navegador se refleje instantáneamente en todos los demás navegadores conectados.
El ESP32 escuchará activamente en el puerto 80 para conexiones WebSocket entrantes. Cuando un usuario cambie el estado del interruptor en la página, se enviará un mensaje de "toggle" al ESP32, que cambiará el estado del LED y notificará a todos los clientes conectados, enviando un "1" o un "0" según corresponda. Esto asegura que todos los usuarios vean el mismo estado del LED.
Este proyecto puede servir como base para futuros experimentos en sistemas de automatización del hogar y otras aplicaciones de IoT. Por ejemplo, podrías expandirlo para controlar múltiples dispositivos, integrar sensores o incluso crear un sistema de iluminación inteligente más complejo.
Configuración del entorno de desarrollo Arduino
Para programar el ESP32, utilizaremos el IDE de Arduino. Asegúrate de tener el complemento de ESP32 instalado antes de continuar. Para construir un servidor WebSocket, utilizaremos la biblioteca ESPAsyncWebServer. Esta biblioteca depende de AsyncTCP, que también necesitarás instalar. Dado que estas bibliotecas no están disponibles en el Administrador de bibliotecas del IDE de Arduino, tendrás que instalarlas manualmente.
Para hacerlo, sigue estos pasos:
- Descarga las bibliotecas desde los siguientes enlaces:
- En el IDE de Arduino, navega a Sketch > Include Library > Add .ZIP Library y selecciona las bibliotecas descargadas.
Subiendo el código al ESP32
Una vez que hayas configurado el IDE, copia el siguiente código en tu entorno. Antes de subirlo al ESP32, asegúrate de modificar las siguientes líneas para incluir tus credenciales de red.
// Reemplaza con tus credenciales de red
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
Después de realizar estos cambios, puedes subir el código al ESP32.
// Importar las bibliotecas necesarias
#include
#include
#include
// Reemplaza con tus credenciales de red
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
bool ledState = 0;
const int ledPin = 2;
// Crear objeto AsyncWebServer en el puerto 80
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
// HTML de la página
const char index_html[] PROGMEM = R"rawliteral(
Servidor WebSocket ESP32
html{font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}
body{margin-top: 50px;}
h1{color: #444444;margin: 50px auto;}
p{font-size: 19px;color: #888;}
#state{font-weight: bold;color: #444;}
.switch{margin:25px auto;width:80px}
.toggle{display:none}
.toggle+label{display:block;position:relative;cursor:pointer;outline:0;user-select:none;padding:2px;width:80px;height:40px;background-color:#ddd;border-radius:40px}
.toggle+label:before,.toggle+label:after{display:block;position:absolute;top:1px;left:1px;bottom:1px;content:""}
.toggle+label:before{right:1px;background-color:#f1f1f1;border-radius:40px;transition:background .4s}
.toggle+label:after{width:40px;background-color:#fff;border-radius:20px;box-shadow:0 2px 5px rgba(0,0,0,.3);transition:margin .4s}
.toggle:checked+label:before{background-color:#4285f4}
.toggle:checked+label:after{margin-left:42px}
Servidor WebSocket ESP32
LED incorporado: %STATE%
window.addEventListener('load', function() {
var websocket = new WebSocket(`ws://${window.location.hostname}/ws`);
websocket.onopen = function(event) {
console.log('Conexión establecida');
}
websocket.onclose = function(event) {
console.log('Conexión cerrada');
}
websocket.onerror = function(error) {
console.log('Error');
};
websocket.onmessage = function(event) {
if (event.data == "1") {
document.getElementById('state').innerHTML = "ON";
document.getElementById('toggle-btn').checked = true;
} else {
document.getElementById('state').innerHTML = "OFF";
document.getElementById('toggle-btn').checked = false;
}
};
document.getElementById('toggle-btn').addEventListener('change', function() { websocket.send('toggle'); });
});
)rawliteral";
void handleWebSocketMessage(void *arg, uint8_t *data, size_t len) {
AwsFrameInfo *info = (AwsFrameInfo*)arg;
if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
data[len] = 0;
if (strcmp((char*)data, "toggle") == 0) {
ledState = !ledState;
ws.textAll(String(ledState));
}
}
}
// Otras funciones y setup aquí...
Demostración del proyecto
Después de subir el código, abre el Monitor Serial y establece la velocidad de baudios a 115200. Al presionar el botón EN en el ESP32, tardará un momento en conectarse a tu red y mostrará la dirección IP dinámica que obtuvo de tu enrutador.
A continuación, abre un navegador web y navega a la dirección IP que se muestra en el monitor serial. Deberías ver una página web que muestra el estado del LED incorporado y un interruptor para controlarlo.
También puedes usar tu teléfono o tablet para acceder a la misma dirección IP, asegurándote de que esté conectado a la misma red que tu computadora. Al hacer clic en el interruptor, observarás que no solo se activa o desactiva el LED, sino que el estado del LED se actualiza automáticamente en todos los navegadores abiertos.
En el Monitor Serial, podrás observar cómo los clientes se conectan y desconectan del ESP32, lo que proporciona una visión clara del funcionamiento del servidor.
Explicación detallada del código
Ahora, desglosaremos el código en partes más manejables para comprender su funcionamiento.
Importación de bibliotecas necesarias
El sketch comienza importando las siguientes bibliotecas:
WiFi.h
: proporciona métodos específicos de WiFi para el ESP32, que usamos para conectarlo a la red.ESPAsyncWebServer.h
: permite crear un servidor HTTP que admite puntos finales de WebSocket.AsyncTCP.h
: la biblioteca ESPAsyncWebServer depende de esta biblioteca para funcionar correctamente.
Definición de constantes y variables
En esta sección, se definen las credenciales de la red (SSID y contraseña) que debes reemplazar con tus propios datos de Wi-Fi. También se declaran dos variables: una para rastrear el estado actual del GPIO (ledState
) y otra para especificar el pin GPIO conectado al LED (ledPin
), que controlará el LED incorporado.
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
bool ledState = 0;
const int ledPin = 2;
Inicialización del servidor web y WebSocket
A continuación, se crea un objeto de la clase AsyncWebServer
llamado server
. Este objeto escucha en el puerto 80 para las solicitudes HTTP. También se crea un objeto de la clase AsyncWebSocket
denominado ws
, que establece el punto final del WebSocket en /ws
.
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
Creación de la página web
La constante index_html
almacena el código HTML que se mostrará en la página web, incluyendo un interruptor para controlar el LED y scripts para gestionar la conexión WebSocket. El uso del macro PROGMEM
permite que el contenido se almacene en la memoria del programa (flash) del ESP32 en lugar de la SRAM, optimizando el uso de la memoria.
CSS para el diseño
Dentro de las etiquetas <style></style>
se encuentra el código CSS que se utiliza para dar estilo a la página web. Puedes modificar este estilo para personalizar la apariencia de tu página.
HTML del contenido de la página
El contenido HTML real se encuentra entre las etiquetas <body></body>
, que incluye el título, el interruptor y el estado del LED. Los marcadores de posición %CHECK%
y %STATE%
se reemplazan con los valores apropiados en el momento en que se solicita la página.
JavaScript para la funcionalidad del cliente
El código JavaScript se encarga de establecer la conexión WebSocket, gestionar los datos intercambiados y actualizar los elementos de la página según sea necesario. Este código se ejecuta en el lado del cliente, permitiendo una experiencia interactiva.
Función setup()
Dentro de la función setup()
, se inicializan los puertos seriales para la depuración, se configura el pin del LED y se establece la conexión a la red Wi-Fi. También se configuran los manejadores de eventos para el WebSocket.
Función loop()
En este caso, el método cleanupClients()
se llama dentro de la función loop()
. Este método de la clase AsyncWebSocket
se encarga de limpiar las conexiones de clientes que ya no están activas, garantizando que el ESP32 no se vea sobrecargado por conexiones antiguas.
void loop() {
ws.cleanupClients();
}
Manejo de eventos WebSocket en el servidor
La función eventHandler()
maneja los eventos que se producen en el WebSocket, como conexiones, desconexiones y la recepción de datos. Este es el corazón de la comunicación entre el cliente y el servidor.
WS_EVT_CONNECT
: se activa cuando un nuevo cliente se conecta al servidor WebSocket.WS_EVT_DISCONNECT
: se activa cuando un cliente se desconecta.WS_EVT_DATA
: se activa al recibir datos desde un cliente.
Lectura de mensajes y transmisión
La función handleWebSocketMessage()
se encarga de procesar los mensajes entrantes. Cuando recibe un mensaje de "toggle", cambia el estado del LED y utiliza el método textAll()
para notificar a todos los clientes conectados sobre el nuevo estado.
Procesamiento de marcadores de posición en la página web
La función processor()
busca los marcadores de posición en el HTML y los reemplaza con los valores correspondientes antes de enviar la página al navegador del cliente. Esto garantiza que cada cliente vea el estado actualizado del LED al conectarse.
String processor(const String& var) {
if(var == "STATE"){
return ledState ? "ON" : "OFF";
}
if(var == "CHECK"){
return ledState ? "checked" : "";
}
return String();
}
Deja una respuesta
Estos temas te pueden interesar