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

javascript
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

html
<!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

json
{
  "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

bash
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:

text
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)

cpp
#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:

  1. Cambiar credenciales WiFi: Actualiza ssid y password

  2. IP del servidor: Cambia websocket_server por la IP de tu computadora

  3. Verificar GPIO: Asegúrate que el pin DHT11 coincida con tu conexión


Parte 3: Prueba y Verificación

Pasos de prueba:

  1. Iniciar servidor Node.js:

    bash
    npm start
  2. Subir código al ESP32 y abrir Monitor Serial

  3. Abrir interfaz web:
    Navegar a http://localhost:3000

  4. 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

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *