Un nodo ESP32 W5500 MQTT sustituye el Wi-Fi por un cable de red, lo que a menudo marca la diferencia entre "funciona casi siempre" y "simplemente funciona" dentro de un armario eléctrico metálico o en la planta de una fábrica. En este tutorial conectarás un ESP32 a Ethernet cableado con un módulo SPI W5500, o con un PHY LAN8720 en placas como la WT32-ETH01, gestionarás los eventos de enlace activo e inactivo y publicarás datos de máquinas en broker.connectmq.com. El código MQTT apenas cambia; el trabajo interesante está en la capa de red.
Por qué Ethernet cableado para MQTT con ESP32
El Wi-Fi es cómodo pero frágil en entornos industriales. Las envolventes de acero apantallan la señal, los motores y variadores generan ruido, los departamentos de TI restringen los clientes inalámbricos y los puntos de acceso se sustituyen o reconfiguran sin que nadie avise al equipo de automatización. Ethernet elimina la mayoría de estas variables:
| Factor | Wi-Fi | Ethernet cableado |
|---|---|---|
| Dentro de un armario metálico | Señal débil o nula | Sin efecto |
| Ruido eléctrico de los variadores | Puede interrumpir el enlace | Generalmente robusto con cable apantallado |
| Credenciales en el dispositivo | Se necesitan SSID y contraseña | Ninguna para la capa de enlace |
| Estabilidad de la latencia | Varía con la carga y las interferencias | Constante |
| Alimentación | Fuente aparte | PoE opcional con un módulo adecuado |
| Movilidad | Sí | No |
La contrapartida es el cableado y un montaje de hardware algo más complejo. Para un nodo fijo que lee una máquina, un contador o un dispositivo Modbus RS-485 y lo reenvía a MQTT, Ethernet cableado suele ser la decisión correcta.
¿W5500 o LAN8720?
Hay dos formas habituales de dar a un ESP32 un puerto Ethernet.
| W5500 | LAN8720 | |
|---|---|---|
| Interfaz | SPI, funciona con cualquier variante de ESP32 | RMII, necesita el EMAC interno del ESP32 original |
| Pines usados | 4 pines SPI más interrupción y reset | Unos 10 pines fijos |
| Hardware típico | Pequeño módulo W5500, muchas placas S3 con Ethernet | WT32-ETH01, Olimex ESP32-POE, LilyGO T-ETH |
| Rendimiento | De sobra para MQTT | Mayor, también más de lo que necesita MQTT |
| Soporte en Arduino-ESP32 3.x | Integrado en ETH.h |
Integrado en ETH.h |
Ambos están soportados de forma nativa por la librería ETH en Arduino-ESP32 3.x, y ambos se integran en la misma pila de red lwIP. Esa es la clave: en cuanto el enlace tiene dirección IP, NetworkClient, PubSubClient, DNS y TLS funcionan exactamente igual que por Wi-Fi. Si estás en el core 2.x, actualiza; el soporte del W5500 en ETH.h llegó con la 3.x y las antiguas librerías alternativas ya no son necesarias. La documentación de Arduino-ESP32 y los ejemplos de ETH incluidos en el core enumeran los PHY soportados.
Cableado de un módulo W5500
| Pin del W5500 | Pin del ESP32 | Nota |
|---|---|---|
| MOSI | GPIO 23 | Salida de datos SPI |
| MISO | GPIO 19 | Entrada de datos SPI |
| SCLK | GPIO 18 | Reloj SPI |
| SCS / CS | GPIO 5 | Chip select |
| INT | GPIO 4 | Interrupción, recomendado |
| RST | GPIO 14 | Reset |
| 3.3V | 3V3 | Algunos módulos tienen entrada de 5 V con regulador |
| GND | GND | Masa común |
Mantén cortos los cables SPI, por debajo de unos 10 cm en una protoboard. El W5500 puede consumir una corriente apreciable al transmitir, así que aliméntalo desde un raíl de 3,3 V sólido y no desde el débil regulador de una placa USB-serie. La mayoría de los problemas de enlace en protoboard resultan ser de alimentación o de cableado, no de software.
El sketch de ESP32 W5500 MQTT
Este sketch usa Arduino-ESP32 3.x y PubSubClient. Sigue el estado de Ethernet mediante eventos, conecta MQTT solo cuando el enlace tiene dirección IP y publica telemetría cada cinco segundos, incluida la velocidad del enlace.
#include <ETH.h>
#include <SPI.h>
#include <NetworkClient.h>
#include <PubSubClient.h>
// Pines del W5500
#define ETH_PHY_TYPE ETH_PHY_W5500
#define ETH_PHY_ADDR 1
#define ETH_PHY_CS 5
#define ETH_PHY_IRQ 4
#define ETH_PHY_RST 14
#define ETH_SPI_SCK 18
#define ETH_SPI_MISO 19
#define ETH_SPI_MOSI 23
const char* MQTT_HOST = "broker.connectmq.com";
const uint16_t MQTT_PORT = 1883;
const char* CLIENT_ID = "eth-node-6b2d90";
const char* BASE = "connectmq/demo/eth-node-6b2d90";
NetworkClient net;
PubSubClient mqtt(net);
volatile bool ethReady = false;
unsigned long lastAttempt = 0, lastPublish = 0;
void onNetworkEvent(arduino_event_id_t event) {
switch (event) {
case ARDUINO_EVENT_ETH_START:
ETH.setHostname("eth-node-6b2d90");
break;
case ARDUINO_EVENT_ETH_CONNECTED:
Serial.println("Ethernet link up");
break;
case ARDUINO_EVENT_ETH_GOT_IP:
Serial.printf("IP %s, %u Mbps, %s duplex\n", ETH.localIP().toString().c_str(),
ETH.linkSpeed(), ETH.fullDuplex() ? "full" : "half");
ethReady = true;
break;
case ARDUINO_EVENT_ETH_LOST_IP:
case ARDUINO_EVENT_ETH_DISCONNECTED:
Serial.println("Ethernet link down");
ethReady = false;
break;
case ARDUINO_EVENT_ETH_STOP:
ethReady = false;
break;
default:
break;
}
}
void onMessage(char* topic, byte* payload, unsigned int length) {
Serial.printf("Command %s: %.*s\n", topic, (int)length, (const char*)payload);
}
bool connectMqtt() {
String status = String(BASE) + "/status";
if (!mqtt.connect(CLIENT_ID, nullptr, nullptr, status.c_str(), 1, true, "offline")) {
Serial.printf("MQTT failed, state=%d\n", mqtt.state());
return false;
}
mqtt.publish(status.c_str(), "online", true);
mqtt.subscribe((String(BASE) + "/cmd/#").c_str(), 1);
Serial.println("MQTT connected over Ethernet");
return true;
}
void setup() {
Serial.begin(115200);
Network.onEvent(onNetworkEvent);
SPI.begin(ETH_SPI_SCK, ETH_SPI_MISO, ETH_SPI_MOSI);
if (!ETH.begin(ETH_PHY_TYPE, ETH_PHY_ADDR, ETH_PHY_CS, ETH_PHY_IRQ, ETH_PHY_RST, SPI)) {
Serial.println("W5500 not detected, check wiring");
}
// IP estática opcional para redes sin DHCP:
// ETH.config(IPAddress(192,168,10,50), IPAddress(192,168,10,1),
// IPAddress(255,255,255,0), IPAddress(192,168,10,1));
mqtt.setServer(MQTT_HOST, MQTT_PORT);
mqtt.setCallback(onMessage);
mqtt.setKeepAlive(30);
mqtt.setSocketTimeout(5);
}
void loop() {
if (!ethReady) {
if (mqtt.connected()) mqtt.disconnect();
return;
}
if (!mqtt.connected()) {
if (millis() - lastAttempt > 5000) { lastAttempt = millis(); connectMqtt(); }
return;
}
mqtt.loop();
if (millis() - lastPublish > 5000) {
lastPublish = millis();
char json[128];
snprintf(json, sizeof(json), "{\"uptime\":%lu,\"link\":%u,\"heap\":%u}",
millis() / 1000, ETH.linkSpeed(), ESP.getFreeHeap());
mqtt.publish((String(BASE) + "/telemetry").c_str(), json);
}
}
Usar una placa con LAN8720 en su lugar
En una WT32-ETH01 o una placa similar con LAN8720, elimina las líneas SPI y los defines del W5500 e inicia Ethernet con los parámetros RMII. Para la WT32-ETH01 los valores habituales son:
// tipo, dirección PHY, MDC, MDIO, pin de alimentación, modo de reloj
ETH.begin(ETH_PHY_LAN8720, 1, 23, 18, 16, ETH_CLOCK_GPIO0_IN);
Otras placas difieren en la dirección PHY, el pin de alimentación y el modo de reloj, así que consulta el esquema de la placa o el ejemplo del fabricante. Todo lo que viene después de ETH.begin(), incluidos los eventos y el código MQTT, queda idéntico.
Prueba el nodo
Conecta el cable, abre el monitor serie y busca la línea IP ... Mbps seguida de MQTT connected over Ethernet. Después suscríbete desde cualquier ordenador:
mosquitto_sub -h broker.connectmq.com -p 1883 -i watch-6b2d90 -v -t "connectmq/demo/eth-node-6b2d90/#"
Ahora desconecta el cable durante veinte segundos y vuelve a conectarlo. El log serie debería mostrar link down, ningún intento de conexión MQTT mientras el cable está fuera, y después link up, un nuevo evento de IP y una reconexión. En el suscriptor verás status offline del Last Will cuando expire el keep-alive, seguido de online tras la recuperación. Esta "prueba de tirar del cable" merece la pena en cada nuevo diseño de hardware; detecta problemas de alimentación y del PHY que una prueba en banco con un enlace estable nunca revela.
El servidor MQTT público gratuito vale para este ejercicio y para prototipos, pero sus datos son legibles por cualquiera y no es apto para producción. Los datos de máquinas deben ir con credenciales privadas.
Dar forma a los datos de máquinas para MQTT
La telemetría del sketch es deliberadamente mínima. En una máquina real, decide pronto qué va en cada mensaje y con qué frecuencia. Algunas reglas que funcionan bien en la práctica:
- Separa los datos rápidos de los lentos. Contadores, temperaturas y estados que cambian cada segundo van en un topic
telemetrya intervalo fijo. La configuración, la versión de firmware y los números de serie van en un topicinfo, publicado como retenido una vez tras cada conexión. - Publica los estados cuando cambian. Un estado de máquina como
RUNNING,IDLEoFAULTes más útil como mensaje retenido publicado solo cuando cambia que como un valor repetido cada cinco segundos. - Incluye unidades y una marca de tiempo.
{"spindleTempC":41.2,"ts":1760600000}se describe a sí mismo; un41.2a secas, no. - Mantén pequeños los payloads. Ethernet cableado tiene ancho de banda de sobra, pero el broker, tus dashboards y cualquier base de datos posterior pagan el precio de los mensajes verbosos.
Si publicas en un broker de ConnectMQ, estas convenciones también hacen que el explorador de topics en vivo sea mucho más fácil de leer durante la puesta en marcha, porque cada máquina aparece como una rama ordenada con un puñado de topics con sentido.
Lista de comprobación para despliegues industriales
- Reglas de firewall de salida. Las redes de planta suelen bloquearlo todo por defecto. Solicita TCP 8883 de salida hacia el host de tu broker; no necesitas reglas de entrada porque es el ESP32 quien inicia la conexión.
- Usa TLS en el 8883.
NetworkClientSecurefunciona por Ethernet exactamente igual que por Wi-Fi; sigue ESP32 MQTT SSL con WiFiClientSecure y cambia el nombre de la clase. - IP estática o reserva DHCP. Los equipos de operaciones quieren poder localizar el dispositivo. Una reserva DHCP suele ser más fácil de gestionar que una dirección fija en el código.
- NTP para las marcas de tiempo. Las redes industriales pueden bloquear el NTP público; usa el servidor horario de la planta.
- Watchdog. Combina el diseño orientado a eventos con el backoff de lógica de reconexión MQTT en ESP32 y un watchdog de hardware.
- Estructura de topics. Usa desde el primer día una jerarquía de estilo ISA-95 como
site/area/line/cell/device/telemetry; escala de un nodo a una planta entera. - Alimentación y puesta a tierra. Usa fuentes de carril DIN, cable apantallado donde haya variadores y valora los módulos PoE para eliminar una línea de alimentación aparte.
Alternativa: Ethernet con respaldo celular
Para emplazamientos remotos, algunos diseños combinan Ethernet con un módem celular como respaldo. La capa MQTT sigue siendo la misma; el firmware cambia el Client que entrega a PubSubClient. La guía de ESP32 con SIM7600 4G LTE MQTT muestra la parte celular.
De un nodo a una planta
En cuanto unos cuantos nodos publican, el broker se convierte en infraestructura compartida de la planta. Un broker MQTT gestionado elimina la necesidad de ejecutar y parchear un servidor en la red OT. ConnectMQ detecta automáticamente las estructuras de topics ISA-95 y Unified Namespace, de modo que, cuando tus topics siguen esa estructura, la vista de dispositivos del portal agrupa tus nodos Ethernet por planta, área y línea, y las reglas de alerta por umbral pueden señalar un valor que se sale de su rango normal. Usar un broker MQTT en la nube también permite que ingeniería y el soporte remoto vean los mismos datos en vivo sin acceso VPN a la planta, ya sea una línea de manufactura en Querétaro o una planta en Bilbao. Esos mismos topics pueden alimentar un gemelo digital 3D de la línea en el navegador. Para el itinerario completo de aprendizaje con ESP32, consulta el tutorial de IoT con ESP32.
Empieza con ConnectMQ
Crea un broker Serverless gratuito en app.connectmq.com, sin tarjeta de crédito. Da a cada nodo Ethernet sus propias credenciales y observa cómo aparecen los datos de tus máquinas en el explorador de topics en vivo en cuestión de minutos.
Preguntas frecuentes
¿Funciona PubSubClient por Ethernet en el ESP32?
Sí. PubSubClient acepta cualquier Client de Arduino, y en Arduino-ESP32 3.x NetworkClient funciona tanto por Wi-Fi como por Ethernet a través de la misma pila de red.
¿Puedo usar un W5500 con un ESP32-S3 o un ESP32-C3?
Sí. El W5500 se conecta por SPI, así que funciona en cualquier variante de ESP32 con pines SPI libres. El LAN8720 necesita el EMAC interno, que entre estos solo tiene el ESP32 original.
¿Necesito la librería Ethernet de Arduino para un W5500?
No en Arduino-ESP32 3.x. La librería ETH integrada soporta el W5500 directamente y se integra con lwIP, así que DNS, TLS y NetworkClient se comportan igual que en Wi-Fi.
¿Puede el ESP32 usar Wi-Fi y Ethernet a la vez?
Sí, ambas interfaces pueden estar activas. Asegúrate de que tu firmware sabe cuál lleva la conexión MQTT y de que gestiona los eventos de las dos.



