You dont have javascript enabled! Please enable it!

Caso práctico: Monitor de red local con ESP32

Caso práctico: Monitor de red local con ESP32 — hero

Objetivo y caso de uso

Qué construirás: Un Monitor de Red Local para Técnicos de Campo independiente utilizando un ESP32 configurado como Punto de Acceso por Software (SoftAP). Este transmite una red Wi-Fi segura y localizada para servir un panel de diagnóstico en tiempo real y aislado de internet directamente a un teléfono inteligente o tableta.

Por qué es importante / Casos de uso

  • Diagnóstico independiente de la infraestructura: Permite a los técnicos conectarse directamente a los equipos en entornos sin conexión, como campos agrícolas remotos, sótanos industriales profundos o sitios de nueva construcción.
  • Registro seguro de fallas de la máquina: Se interconecta con relés de falla industriales para que el personal pueda monitorear de manera segura los estados de la máquina a través del navegador, evitando la exposición física a paneles de control de alto voltaje.
  • Interfaz hombre-máquina (HMI) aislada: Ofrece un portal de configuración altamente seguro y aislado de internet (air-gapped) al que no se puede acceder desde la internet pública, reduciendo drásticamente las superficies de ataque de ciberseguridad.
  • Monitoreo de control de acceso: Actúa como un monitor localizado y temporal para puertas de racks de servidores o puertas de seguridad, registrando instantáneamente los estados de apertura/cierre.

Resultado esperado

  • El ESP32 transmite de manera confiable una red Wi-Fi protegida por WPA2 (SSID: ESP32-FieldMonitor) con un tiempo de conexión de cliente de <2 segundos.
  • Un panel web totalmente adaptable a dispositivos móviles se carga sin internet externo, renderizando elementos de la interfaz de usuario de diagnóstico.
  • El estado del hardware en tiempo real y los registros de fallas se actualizan en el navegador del cliente con una latencia de red de <50ms.

Audiencia: Desarrolladores de IoT Industrial, Técnicos de Campo, Ingenieros de Mantenimiento; Nivel: Intermedio

Arquitectura/flujo: ESP32 (Modo SoftAP) → Transmite SSID WPA2 → El teléfono inteligente del técnico se conecta → Servidor Web ESP32 → Sirve Panel HTML/JS & Transmite datos GPIO en tiempo real.

Nota educativa de validación

Antes de publicar este caso, el contenido pasó la puerta automática de validación de Prometeo con estado PASS. Para este perfil ESP32 DevKitC, el proyecto se comprobó como proyecto PlatformIO: el validador extrajo platformio.ini y src/main.cpp, creó un proyecto temporal y ejecutó pio run contra platform = espressif32, board = esp32dev y framework = arduino. También revisó la estructura del artículo, que los comandos sean copiables con guiones ASCII, y que no aparezcan stacks no soportados como ESP-IDF directo o placas ESP32 no acotadas.

Evidencia de validación publicada

  • Resultado automático: PASS.
  • Estructura parseada: 4 apartados, 2 tablas y 2 bloques de código detectados antes de publicar.
  • Código comprobado: 1 PlatformIO config + 1 ESP32 source/pio run.
  • Catálogo soportado: el texto se contrastó contra los perfiles de dispositivo validables de Prometeo y los stacks no soportados bloquean la publicación.
  • Hallazgos del informe: sin hallazgos bloqueantes.

Esta validación confirma compatibilidad sintáctica y de herramientas para el código publicado, pero no sustituye la prueba física sobre tu placa ESP32 DevKitC exacta, tu cableado, alimentación y entorno WiFi local.

Nota educativa de seguridad

Este proyecto es un prototipo educativo, no un producto certificado. Antes de encender la configuración, verifica el esquema de pines de la revisión exacta de tu placa ULX3S, mantén las señales de E/S de la FPGA a 3.3 V, nunca conectes 5 V directamente a los pines de E/S, desconecta la alimentación antes de cambiar el cableado y utiliza fuentes externas adecuadas para cargas, motores o servos mientras compartes la tierra (ground) solo cuando el cableado lo requiera.

Diagrama de bloques conceptual

Vista de alto nivel: qué entra, qué procesa cada bloque y qué sale del sistema.

Arquitectura funcional

ESP32 (Modo SoftAP)

Transmite SSID WPA2

El teléfono inteligente del técnico se co…

Servidor Web ESP32

Sirve Panel HTML/JS & Transmite datos GPI…

Flujo conceptual de señales y responsabilidades entre bloques del dispositivo.

Ruta de validación

Código fuente

PlatformIO build

Flash

Monitor serie

Resumen conceptual de las herramientas usadas para comprobar el material publicado.

Requisitos previos

Para completar con éxito este tutorial, necesitarás:
* Conocimiento básico de programación en C++ y conceptos de GPIO de microcontroladores.
* PlatformIO IDE instalado (ya sea como una extensión de Visual Studio Code o a través de la Interfaz de Línea de Comandos).
* Conocimiento básico de conceptos de redes, específicamente Puntos de Acceso Wi-Fi (SSID, WPA2) y direccionamiento IP.
* Un navegador web en un dispositivo con Wi-Fi (teléfono inteligente, tableta o computadora portátil) para ver el panel.


Materiales

  • Microcontrolador: ESP32 DevKitC (Modelo exacto: ESP32 DevKitC V4, típicamente equipado con el módulo ESP32-WROOM-32).
  • Dispositivo de entrada: Pulsador o interruptor de contacto seco (esto simulará un relé de falla de máquina externo o contacto de puerta).
  • Indicador visual: LED de estado estándar de 5mm (ej., azul o verde).
  • Componentes pasivos: 1x resistencia de 330Ω (limitadora de corriente para el LED).
  • Prototipado: Protoboard estándar y cables puente macho a macho.
  • Conexión: Cable micro-USB (debe soportar tanto energía como transferencia de datos; los cables de solo carga fallarán al flashear el dispositivo).

(Nota: Dependiendo de tu fabricante específico de ESP32 DevKitC, es posible que necesites instalar controladores USB a UART CP210x o CH34x en tu sistema operativo para permitir que PlatformIO reconozca el dispositivo).


Configuración/Conexión

La configuración de hardware utiliza las resistencias pull-up internas del ESP32 para la entrada del contacto, minimizando la necesidad de componentes pasivos externos. Cuando el interruptor de contacto está abierto, la resistencia interna lleva el pin a HIGH (ALTO). Cuando el interruptor está cerrado, conecta el pin a Tierra (LOW o BAJO).

Tabla de cableado:

ComponentePin ESP32 DevKitCConexión / DestinoDescripción
Interruptor de contactoGPIO 18Terminal 1 del interruptorConfigurado como INPUT_PULLUP.
Interruptor de contactoGNDTerminal 2 del interruptorLleva el GPIO 18 a LOW cuando está cerrado.
LED de estadoGPIO 19Ánodo del LED (Pata larga)Configurado como OUTPUT.
LED de estadoGNDCátodo del LED (Pata corta) a través de resistencia de 330ΩCompleta el circuito del LED de forma segura.

Notas importantes de hardware:
1. Asegúrate de usar una resistencia de 330Ω en serie con el LED de estado para evitar extraer corriente excesiva del pin GPIO del ESP32, lo cual podría dañar el microcontrolador.
2. No conectes el interruptor de contacto a ninguna fuente de voltaje externa. Debe actuar como un «contacto seco» (un simple cierre mecánico a Tierra).


Código validado

El proyecto requiere dos archivos dentro de tu espacio de trabajo de PlatformIO. El archivo platformio.ini configura el entorno de compilación, mientras que src/main.cpp contiene la lógica de la aplicación.

Configuración de PlatformIO

Crea o sobrescribe el archivo platformio.ini en la raíz del directorio de tu proyecto con la siguiente configuración. Esto asegura que se utilicen la definición correcta de la placa y la tasa de baudios del monitor serie.

; platformio.ini
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200

Código fuente de la aplicación

Crea o sobrescribe el archivo src/main.cpp con el siguiente código completo y compilable.

Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.

// src/main.cpp
#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>

// Hardware Pin Definitions
#define CONTACT_PIN 18
#define STATUS_LED_PIN 19

// SoftAP Network Credentials
const char* ssid = "ESP32-FieldMonitor";
const char* password = "admin1234"; // WPA2 requires a minimum of 8 characters

// Initialize the WebServer on port 80
WebServer server(80);

// Global state variables
int currentContactState = HIGH;
int lastContactState = HIGH;

// HTML Dashboard stored in Program Memory (PROGMEM) to save RAM
const char dashboard_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Field Monitor Dashboard</title>
  <style>
    body {
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
      text-align: center;
      background-color: #e9ecef;
      margin: 0;
      padding: 20px;
    }
    .card {
      background: white;
      max-width: 400px;
      margin: 40px auto;
      padding: 30px;
      border-radius: 12px;
      box-shadow: 0 8px 16px rgba(0,0,0,0.1);
    }
    h2 {
      color: #343a40;
      margin-top: 0;
    }
    .status-box {
      font-size: 1.8em;
      font-weight: bold;
      margin: 20px 0;
      padding: 20px;
      border-radius: 8px;
      transition: background-color 0.3s, color 0.3s;
    }
    .loading { background-color: #f8f9fa; color: #6c757d; border: 2px dashed #6c757d; }
    .closed { background-color: #d4edda; color: #155724; border: 2px solid #28a745; }
    .open { background-color: #f8d7da; color: #721c24; border: 2px solid #dc3545; }
    .footer {
      margin-top: 20px;
      font-size: 0.85em;
      color: #6c757d;
    }
  </style>
</head>
<body>
  <div class="card">
    <h2>Machine Contact Status</h2>
    <div id="contact-state" class="status-box loading">Awaiting Data...</div>
    <div class="footer">Auto-refreshing every 500ms via JSON API</div>
  </div>

  <script>
    // Asynchronous function to fetch status from the ESP32
    function fetchStatus() {
      fetch('/api/status')
        .then(response => {
          if (!response.ok) {
            throw new Error('Network response was not ok');
          }
          return response.json();
        })
        .then(data => {
          const statusDiv = document.getElementById('contact-state');
          // data.contact is 0 when closed (LOW) due to INPUT_PULLUP
          if(data.contact === 0) {
            statusDiv.innerHTML = "CONTACT CLOSED";
            statusDiv.className = "status-box closed";
          } else {
            statusDiv.innerHTML = "CONTACT OPEN";
            statusDiv.className = "status-box open";
          }
// ... continúa para miembros en el código completo validado ...

🔒 Parte del código validado es premium. Con el pase de 7 días o la suscripción mensual podrás consultar el archivo completo validado.

// src/main.cpp
#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>

// Hardware Pin Definitions
#define CONTACT_PIN 18
#define STATUS_LED_PIN 19

// SoftAP Network Credentials
const char* ssid = "ESP32-FieldMonitor";
const char* password = "admin1234"; // WPA2 requires a minimum of 8 characters

// Initialize the WebServer on port 80
WebServer server(80);

// Global state variables
int currentContactState = HIGH;
int lastContactState = HIGH;

// HTML Dashboard stored in Program Memory (PROGMEM) to save RAM
const char dashboard_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Field Monitor Dashboard</title>
  <style>
    body {
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
      text-align: center;
      background-color: #e9ecef;
      margin: 0;
      padding: 20px;
    }
    .card {
      background: white;
      max-width: 400px;
      margin: 40px auto;
      padding: 30px;
      border-radius: 12px;
      box-shadow: 0 8px 16px rgba(0,0,0,0.1);
    }
    h2 {
      color: #343a40;
      margin-top: 0;
    }
    .status-box {
      font-size: 1.8em;
      font-weight: bold;
      margin: 20px 0;
      padding: 20px;
      border-radius: 8px;
      transition: background-color 0.3s, color 0.3s;
    }
    .loading { background-color: #f8f9fa; color: #6c757d; border: 2px dashed #6c757d; }
    .closed { background-color: #d4edda; color: #155724; border: 2px solid #28a745; }
    .open { background-color: #f8d7da; color: #721c24; border: 2px solid #dc3545; }
    .footer {
      margin-top: 20px;
      font-size: 0.85em;
      color: #6c757d;
    }
  </style>
</head>
<body>
  <div class="card">
    <h2>Machine Contact Status</h2>
    <div id="contact-state" class="status-box loading">Awaiting Data...</div>
    <div class="footer">Auto-refreshing every 500ms via JSON API</div>
  </div>

  <script>
    // Asynchronous function to fetch status from the ESP32
    function fetchStatus() {
      fetch('/api/status')
        .then(response => {
          if (!response.ok) {
            throw new Error('Network response was not ok');
          }
          return response.json();
        })
        .then(data => {
          const statusDiv = document.getElementById('contact-state');
          // data.contact is 0 when closed (LOW) due to INPUT_PULLUP
          if(data.contact === 0) {
            statusDiv.innerHTML = "CONTACT CLOSED";
            statusDiv.className = "status-box closed";
          } else {
            statusDiv.innerHTML = "CONTACT OPEN";
            statusDiv.className = "status-box open";
          }
        })
        .catch(error => {
          console.error('Error fetching status:', error);
          const statusDiv = document.getElementById('contact-state');
          statusDiv.innerHTML = "CONNECTION LOST";
          statusDiv.className = "status-box loading";
        });
    }

    // Poll the API every 500 milliseconds
    setInterval(fetchStatus, 500);

    // Initial fetch immediately on load
    window.onload = fetchStatus;
  </script>
</body>
</html>
)rawliteral";

// Route Handler: Serve the main HTML dashboard
void handleRoot() {
  server.send(200, "text/html", dashboard_html);
  Serial.println("Dashboard accessed by a client.");
}

// Route Handler: Serve the JSON API for the dashboard to consume
void handleApiStatus() {
  // Construct a simple JSON string manually
  String jsonPayload = "{\"contact\": " + String(currentContactState) + "}";
  server.send(200, "application/json", jsonPayload);
}

// Route Handler: Handle 404 Not Found
void handleNotFound() {
  server.send(404, "text/plain", "404: Not Found");
}

void setup() {
  // Initialize Serial Monitor
  Serial.begin(115200);
  delay(1000); // Allow serial to stabilize
  Serial.println("\n--- ESP32 Field Monitor Initialization ---");

  // Configure Hardware Pins
  pinMode(CONTACT_PIN, INPUT_PULLUP);
  pinMode(STATUS_LED_PIN, OUTPUT);

  // Read initial state
  currentContactState = digitalRead(CONTACT_PIN);
  lastContactState = currentContactState;

  // Set initial LED state (ON when contact is closed/LOW)
  digitalWrite(STATUS_LED_PIN, (currentContactState == LOW) ? HIGH : LOW);

  // Configure Wi-Fi in Access Point (SoftAP) mode
  Serial.print("Configuring Access Point...");
  WiFi.softAP(ssid, password);

  IPAddress IP = WiFi.softAPIP();
  Serial.println(" Ready!");
  Serial.print("SoftAP SSID: ");
  Serial.println(ssid);
  Serial.print("SoftAP IP Address: ");
  Serial.println(IP);

  // Define Web Server Routing
  server.on("/", HTTP_GET, handleRoot);
  server.on("/api/status", HTTP_GET, handleApiStatus);
  server.onNotFound(handleNotFound);

  // Start the Web Server
  server.begin();
  Serial.println("HTTP Web Server started.");
}

void loop() {
  // Handle incoming HTTP client requests
  server.handleClient();

  // Read the physical contact state
  currentContactState = digitalRead(CONTACT_PIN);

  // Detect state changes to update the LED and log to Serial
  if (currentContactState != lastContactState) {
    // Debounce delay (basic implementation)
    delay(50);
    currentContactState = digitalRead(CONTACT_PIN);

    if (currentContactState != lastContactState) {
      if (currentContactState == LOW) {
        Serial.println("EVENT: Contact CLOSED (Active).");
        digitalWrite(STATUS_LED_PIN, HIGH); // Turn LED ON
      } else {
        Serial.println("EVENT: Contact OPEN (Inactive).");
        digitalWrite(STATUS_LED_PIN, LOW);  // Turn LED OFF
      }
      lastContactState = currentContactState;
    }
  }
}


Comandos de compilación/flasheo/ejecución

Usa la CLI de PlatformIO Core para compilar, cargar y monitorear tu proyecto. Abre tu terminal en el directorio raíz del proyecto (donde se encuentra platformio.ini) y ejecuta los siguientes comandos.

ComandoPropósito
pio runCompila el proyecto y verifica todas las dependencias y la sintaxis.
pio run --target uploadCompila y flashea el firmware compilado al ESP32 DevKitC.
pio device monitorAbre el monitor serie para ver los registros de ejecución a 115200 baudios.

Flujo de trabajo de ejecución:
1. Conecta el ESP32 DevKitC a tu computadora a través del cable micro-USB.
2. Ejecuta pio run para asegurarte de que el código se compila sin errores de sintaxis.
3. Ejecuta pio run --target upload. (Si la carga falla al conectar, mantén presionado el botón BOOT en el ESP32 DevKitC cuando veas «Connecting…» en la terminal).
4. Ejecuta pio device monitor para observar la secuencia de inicialización y verificar la dirección IP del SoftAP.


Validación paso a paso

Sigue estos puntos de control para asegurarte de que el prototipo funcione exactamente como está previsto.

  1. Verificar la inicialización del puerto serie
    • Acción: Observa la salida de la terminal inmediatamente después de ejecutar pio device monitor o presionar el botón EN (Reinicio) en el ESP32.
    • Observación esperada: La terminal imprime «— ESP32 Field Monitor Initialization —«, seguido del SSID ESP32-FieldMonitor y la IP 192.168.4.1.
    • Condición de aprobación: El ESP32 no se bloquea ni entra en un bucle de reinicios.
  2. Verificar la transmisión SoftAP
    • Acción: Abre la configuración de Wi-Fi en un teléfono inteligente o computadora portátil.
    • Observación esperada: Una red llamada ESP32-FieldMonitor aparece en la lista de redes disponibles.
    • Condición de aprobación: Puedes conectarte a la red con éxito usando la contraseña admin1234.
  3. Verificar la carga del panel web
    • Acción: Abre un navegador web en el dispositivo conectado y navega a http://192.168.4.1.
    • Observación esperada: Se carga el «Field Monitor Dashboard» (Panel del Monitor de Campo), mostrando una tarjeta estilizada. El monitor serie registra «Dashboard accessed by a client.» (Panel accedido por un cliente).
    • Condición de aprobación: La interfaz de usuario se renderiza correctamente sin CSS roto.
  4. Verificar entrada física y salida LED
    • *Acción

Encuentra este producto y/o libros sobre este tema en Amazon

Ir a Amazon

Como afiliado de Amazon, gano con las compras que cumplan los requisitos. Si compras a través de este enlace, ayudas a mantener este proyecto.

Quiz rápido

Pregunta 1: ¿Qué microcontrolador se utiliza como Punto de Acceso por Software (SoftAP) en este proyecto?




Pregunta 2: ¿Cuál es el propósito principal del proyecto descrito?




Pregunta 3: ¿Qué tecnología de red utiliza el ESP32 para transmitir la red Wi-Fi?




Pregunta 4: ¿Qué tipo de dispositivo se utiliza típicamente para visualizar el panel de diagnóstico?




Pregunta 5: ¿Por qué es útil este sistema en campos agrícolas remotos o sótanos industriales?




Pregunta 6: ¿Qué ventaja de seguridad ofrece el registro de fallas de la máquina a través de este sistema?




Pregunta 7: ¿Qué significa que la Interfaz Hombre-Máquina (HMI) esté 'aislada' (air-gapped)?




Pregunta 8: ¿Cómo afecta el aislamiento de internet a la ciberseguridad del sistema?




Pregunta 9: ¿Con qué tipo de componentes industriales se interconecta el sistema para monitorear estados de forma segura?




Pregunta 10: ¿Cómo accede el personal al panel de diagnóstico desde su dispositivo móvil?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Electronics & Computer Engineer

Ingeniero Superior en Electrónica de Telecomunicaciones e Ingeniero en Informática (titulaciones oficiales en España).

Sígueme:
Scroll al inicio