P4- Enviar datos en tiempo real de un esp32 a un servidor websocket desarrollado en node.js
Objetivo
Crear un sistema que capture datos de temperatura y humedad del sensor DHT11 usando ESP32 y los envíe en tiempo real a un servidor WebSocket desarrollado en Node.js.
Parte 1: Servidor WebSocket con Node.js
Materiales necesarios:
-
Node.js instalado https://nodejs.org/es/
-
Editor de código (VS Code recomendado) https://code.visualstudio.com/
Instrucciones paso a paso:
1. Crear la estructura del proyecto
Crear una carpeta llamada "esp32-websocket-server" Con terminal de windows o cmder ir a la carpeta Y ya dentro de la carperta en la linea de comandos teclear npm init -y
2. Instalar dependencias
npm install express ws cors
3. Código del servidor (server.js) Crear archivo dentro de la carpeta
const express = require('express'); const WebSocket = require('ws'); const http = require('http'); const cors = require('cors'); const app = express(); const server = http.createServer(app); const wss = new WebSocket.Server({ server }); // Middleware app.use(cors()); app.use(express.json()); app.use(express.static('public')); // Almacenar clientes conectados const clients = new Set(); // Configuración WebSocket wss.on('connection', (ws) => { console.log('✅ Cliente WebSocket conectado'); clients.add(ws); // Enviar mensaje de bienvenida ws.send(JSON.stringify({ type: 'connection', message: 'Conectado al servidor DHT11' })); // Manejar mensajes del cliente ws.on('message', (message) => { try { const data = JSON.parse(message); console.log('???? Mensaje recibido:', data); // Retransmitir a todos los clientes broadcast(JSON.stringify({ type: 'broadcast', data: data, timestamp: new Date().toISOString() })); } catch (error) { console.error('❌ Error procesando mensaje:', error); } }); // Manejar cierre de conexión ws.on('close', () => { console.log('❌ Cliente WebSocket desconectado'); clients.delete(ws); }); // Manejar errores ws.on('error', (error) => { console.error('???? Error WebSocket:', error); clients.delete(ws); }); }); // Función para enviar a todos los clientes function broadcast(message) { clients.forEach(client => { if (client.readyState === WebSocket.OPEN) { client.send(message); } }); } // Ruta para verificar estado del servidor app.get('/status', (req, res) => { res.json({ status: 'online', clients: clients.size, timestamp: new Date().toISOString() }); }); // Ruta para recibir datos HTTP (backup) app.post('/dht-data', (req, res) => { const { temperature, humidity, sensor } = req.body; console.log(`????️ Datos DHT11 recibidos - Temp: ${temperature}°C, Hum: ${humidity}%`); // Broadcast a clientes WebSocket broadcast(JSON.stringify({ type: 'dht-data', temperature: temperature, humidity: humidity, sensor: sensor || 'ESP32', timestamp: new Date().toISOString() })); res.json({ status: 'success', message: 'Datos recibidos' }); }); // Iniciar servidor const PORT = process.env.PORT || 3000; server.listen(PORT, () => { console.log(`???? Servidor WebSocket ejecutándose en http://localhost:${PORT}`); console.log(`???? WebSocket disponible en ws://localhost:${PORT}`); });
4. Interfaz web cliente (public/index.html)
Crear archivo dentro de la carpeta
<!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Monitor DHT11 - Tiempo Real</title> <style> body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; background-color: #f5f5f5; } .container { background: white; padding: 20px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .status { padding: 10px; border-radius: 5px; margin-bottom: 20px; text-align: center; } .connected { background: #d4edda; color: #155724; } .disconnected { background: #f8d7da; color: #721c24; } .data-card { display: flex; justify-content: space-around; margin: 20px 0; } .card { background: #007bff; color: white; padding: 20px; border-radius: 10px; text-align: center; flex: 1; margin: 0 10px; } .value { font-size: 2.5em; font-weight: bold; margin: 10px 0; } .unit { font-size: 1.2em; } .log { background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 5px; padding: 15px; height: 200px; overflow-y: auto; font-family: monospace; font-size: 0.9em; } .timestamp { color: #6c757d; font-size: 0.8em; } </style> </head> <body> <div class="container"> <h1>????️ Monitor DHT11 - Tiempo Real</h1> <div id="status" class="status disconnected"> ???? Desconectado del servidor WebSocket </div> <div class="data-card"> <div class="card"> <h3>????️ Temperatura</h3> <div id="temperature" class="value">--</div> <div class="unit">°C</div> </div> <div class="card"> <h3>???? Humedad</h3> <div id="humidity" class="value">--</div> <div class="unit">%</div> </div> </div> <div> <h3>???? Historial de Datos</h3> <div id="log" class="log"></div> </div> </div> <script> class DHTMonitor { constructor() { this.ws = null; this.isConnected = false; this.reconnectInterval = 5000; this.init(); } init() { this.connectWebSocket(); this.setupEventListeners(); } connectWebSocket() { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const wsUrl = `${protocol}//${window.location.host}`; this.ws = new WebSocket(wsUrl); this.ws.onopen = () => { console.log('✅ Conectado al servidor WebSocket'); this.updateStatus(true, '???? Conectado al servidor WebSocket'); this.addLog('Sistema conectado al servidor'); }; this.ws.onmessage = (event) => { try { const data = JSON.parse(event.data); this.handleMessage(data); } catch (error) { console.error('Error parsing message:', error); } }; this.ws.onclose = () => { console.log('❌ Conexión WebSocket cerrada'); this.updateStatus(false, '???? Desconectado - Reconectando...'); setTimeout(() => this.connectWebSocket(), this.reconnectInterval); }; this.ws.onerror = (error) => { console.error('???? Error WebSocket:', error); this.updateStatus(false, '???? Error de conexión'); }; } handleMessage(data) { switch(data.type) { case 'dht-data': this.updateDisplay(data); this.addLog(`Datos recibidos - Temp: ${data.temperature}°C, Hum: ${data.humidity}%`); break; case 'connection': this.addLog(data.message); break; case 'broadcast': this.addLog(`Broadcast: ${JSON.stringify(data.data)}`); break; default: console.log('Mensaje recibido:', data); } } updateDisplay(data) { document.getElementById('temperature').textContent = data.temperature || '--'; document.getElementById('humidity').textContent = data.humidity || '--'; } updateStatus(connected, message) { this.isConnected = connected; const statusElement = document.getElementById('status'); statusElement.textContent = message; statusElement.className = `status ${connected ? 'connected' : 'disconnected'}`; } addLog(message) { const logElement = document.getElementById('log'); const timestamp = new Date().toLocaleTimeString(); const logEntry = document.createElement('div'); logEntry.innerHTML = `<span class="timestamp">[${timestamp}]</span> ${message}`; logElement.appendChild(logEntry); logElement.scrollTop = logElement.scrollHeight; } setupEventListeners() { // Puedes agregar event listeners adicionales aquí } } // Inicializar la aplicación cuando el DOM esté listo document.addEventListener('DOMContentLoaded', () => { new DHTMonitor(); }); </script> </body> </html>
5. Package.json completo
Crear archivo dentro de la carpeta
{ "name": "esp32-websocket-server", "version": "1.0.0", "description": "Servidor WebSocket para datos DHT11 desde ESP32", "main": "server.js", "scripts": { "start": "node server.js", "dev": "nodemon server.js" }, "dependencies": { "express": "^4.18.2", "ws": "^8.13.0", "cors": "^2.8.5" }, "devDependencies": { "nodemon": "^2.0.22" } }
6. Ejecutar el servidor
npm start
El servidor estará disponible en http://localhost:3000
Parte 2: Código para ESP32 (Arduino IDE)
Materiales necesarios:
-
ESP32
-
Sensor DHT11
-
Cables jumper
-
Protoboard
Conexiones DHT11 – ESP32:
DHT11 VCC → ESP32 3.3V DHT11 GND → ESP32 GND DHT11 DATA → ESP32 GPIO 4
Instrucciones para el código ESP32:
1. Instalar librerías necesarias
En Arduino IDE, instalar:
-
DHT-sensor-library por Adafruit
-
ArduinoWebsockets por Gil Maimon
2. Código ESP32 (esp32_dht11_websocket.ino)
#include <WiFi.h> #include <WebSocketsClient.h> #include <DHT.h> // Configuración WiFi const char* ssid = "TU_RED_WIFI"; const char* password = "TU_PASSWORD_WIFI"; // Configuración WebSocket const char* websocket_server = "192.168.1.100"; // IP de tu servidor Node.js const int websocket_port = 3000; const char* websocket_path = "/"; // Configuración DHT11 #define DHT_PIN 4 #define DHT_TYPE DHT11 DHT dht(DHT_PIN, DHT_TYPE); // Variables WebSocket WebSocketsClient webSocket; bool websocketConnected = false; // Temporizadores unsigned long previousMillis = 0; const long interval = 5000; // Intervalo de envío (5 segundos) void setup() { Serial.begin(115200); // Inicializar DHT11 dht.begin(); // Conectar WiFi setupWiFi(); // Configurar WebSocket setupWebSocket(); Serial.println("???? Sistema DHT11 + WebSocket inicializado"); } void loop() { webSocket.loop(); unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { previousMillis = currentMillis; if (websocketConnected) { readAndSendDHTData(); } } } void setupWiFi() { Serial.println(); Serial.print("???? Conectando a "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.print("."); } Serial.println(); Serial.println("✅ WiFi conectado"); Serial.print("???? IP address: "); Serial.println(WiFi.localIP()); } void setupWebSocket() { webSocket.begin(websocket_server, websocket_port, websocket_path); webSocket.onEvent(webSocketEvent); webSocket.setReconnectInterval(5000); } void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { switch(type) { case WStype_DISCONNECTED: Serial.println("❌ WebSocket desconectado"); websocketConnected = false; break; case WStype_CONNECTED: Serial.println("✅ WebSocket conectado"); websocketConnected = true; // Enviar mensaje de conexión webSocket.sendTXT("{\"type\":\"esp32_connected\",\"message\":\"ESP32 DHT11 conectado\"}"); break; case WStype_TEXT: Serial.printf("???? Mensaje recibido: %s\n", payload); break; case WStype_ERROR: Serial.printf("???? Error WebSocket: %s\n", payload); break; } } void readAndSendDHTData() { // Leer temperatura y humedad float temperature = dht.readTemperature(); float humidity = dht.readHumidity(); // Verificar si la lectura fue exitosa if (isnan(temperature) || isnan(humidity)) { Serial.println("❌ Error leyendo el sensor DHT11"); return; } // Crear JSON con los datos String jsonData = "{"; jsonData += "\"type\":\"dht-data\","; jsonData += "\"sensor\":\"DHT11\","; jsonData += "\"temperature\":" + String(temperature, 1) + ","; jsonData += "\"humidity\":" + String(humidity, 1) + ","; jsonData += "\\"timestamp\\":\\"" + String(millis()) + "\\""; jsonData += "}"; // Enviar datos via WebSocket webSocket.sendTXT(jsonData); // Mostrar en monitor serial Serial.printf("???? Datos enviados - Temp: %.1f°C, Hum: %.1f%%\n", temperature, humidity); }
Configuración importante:
-
Cambiar credenciales WiFi: Actualiza
ssid
ypassword
-
IP del servidor: Cambia
websocket_server
por la IP de tu computadora -
Verificar GPIO: Asegúrate que el pin DHT11 coincida con tu conexión
Parte 3: Prueba y Verificación
Pasos de prueba:
-
Iniciar servidor Node.js:
npm start
-
Subir código al ESP32 y abrir Monitor Serial
-
Abrir interfaz web:
Navegar ahttp://localhost:3000
-
Verificar conexiones:
-
LED azul del ESP32 debería estar estable
-
Monitor Serial mostrará datos enviados
-
Interfaz web actualizará en tiempo real
-
Solución de problemas:
❌ ESP32 no conecta a WiFi:
-
Verificar SSID y password
-
Verificar que la red esté en 2.4GHz
❌ WebSocket no conecta:
-
Verificar IP del servidor en el código ESP32
-
Verificar que el firewall permita conexiones en puerto 3000
❌ Datos DHT11 incorrectos:
-
Verificar conexiones del sensor
-
Revisar que el sensor esté funcionando