ESP32 and Arduino

ESP32 W5500 MQTT: Wired Ethernet for Reliable Industrial Nodes

Use a W5500 SPI module or a LAN8720 PHY to put an ESP32 on wired Ethernet, handle cable and link events cleanly, and publish machine data over MQTT without depending on Wi-Fi.

ESP32 with a W5500 Ethernet module and network cable in an esp32 w5500 mqtt panel
Run this on your own MQTT brokerGet a private, managed MQTT broker on the Free plan and start from ready-made code samples.
Try it free Code samples No card required

An ESP32 W5500 MQTT node replaces Wi-Fi with a network cable, which is often the difference between "mostly works" and "just works" inside a metal control cabinet or on a factory floor. In this tutorial you will connect an ESP32 to wired Ethernet with a W5500 SPI module, or with a LAN8720 PHY on boards like the WT32-ETH01, handle link-up and link-down events, and publish machine data to broker.connectmq.com. The MQTT code barely changes; the interesting work is in the network layer.

Why Wired Ethernet for ESP32 MQTT

Wi-Fi is convenient but fragile in industrial environments. Steel enclosures shield the signal, motors and drives generate noise, IT departments restrict wireless clients, and access points get replaced or reconfigured without anyone telling the automation team. Ethernet removes most of these variables:

Factor Wi-Fi Wired Ethernet
Inside a metal cabinet Weak or no signal Unaffected
Electrical noise from drives Can disrupt the link Generally robust with shielded cable
Credentials on the device SSID and password needed None for the link layer
Latency consistency Varies with load and interference Steady
Power Separate supply Optional PoE with a suitable module
Mobility Yes No

The trade-off is cabling and a slightly more complex hardware setup. For a fixed node that reads a machine, a meter or a Modbus RS-485 device and forwards it to MQTT, wired Ethernet is usually the right call.

W5500 or LAN8720?

There are two common ways to give an ESP32 an Ethernet port.

W5500 LAN8720
Interface SPI, works with any ESP32 variant RMII, needs the internal EMAC of the original ESP32
Pins used 4 SPI pins plus interrupt and reset About 10 fixed pins
Typical hardware Small W5500 module, many S3 Ethernet boards WT32-ETH01, Olimex ESP32-POE, LilyGO T-ETH
Throughput Plenty for MQTT Higher, also more than MQTT needs
Support in Arduino-ESP32 3.x Built into ETH.h Built into ETH.h

Both are supported natively by the ETH library in Arduino-ESP32 3.x, and both feed into the same lwIP network stack. That is the key point: once the link has an IP address, NetworkClient, PubSubClient, DNS and TLS all work exactly as they do over Wi-Fi. If you are on core 2.x, upgrade; W5500 support in ETH.h arrived with 3.x, and the older workaround libraries are no longer needed. The Arduino-ESP32 documentation and the ETH examples bundled with the core list the supported PHYs.

Wiring a W5500 Module

W5500 pin ESP32 pin Note
MOSI GPIO 23 SPI data out
MISO GPIO 19 SPI data in
SCLK GPIO 18 SPI clock
SCS / CS GPIO 5 Chip select
INT GPIO 4 Interrupt, recommended
RST GPIO 14 Reset
3.3V 3V3 Some modules have a 5 V input with a regulator
GND GND Common ground

Keep SPI wires short, under about 10 cm on a breadboard. The W5500 can draw noticeable current during transmission, so power it from a solid 3.3 V rail rather than a weak USB-serial board regulator. Most link problems on a breadboard turn out to be power or wiring, not software.

The ESP32 W5500 MQTT Sketch

This sketch uses Arduino-ESP32 3.x and PubSubClient. It tracks the Ethernet state through events, connects MQTT only when the link has an IP address, and publishes telemetry every five seconds including link speed.

#include <ETH.h>
#include <SPI.h>
#include <NetworkClient.h>
#include <PubSubClient.h>

// W5500 pins
#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");
  }
  // Optional static IP for networks without 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);
  }
}

Using a LAN8720 board instead

On a WT32-ETH01 or similar LAN8720 board, remove the SPI lines and the W5500 defines and start Ethernet with the RMII parameters. For the WT32-ETH01 the common values are:

// type, PHY address, MDC, MDIO, power pin, clock mode
ETH.begin(ETH_PHY_LAN8720, 1, 23, 18, 16, ETH_CLOCK_GPIO0_IN);

Other boards differ in PHY address, power pin and clock mode, so check the board's schematic or vendor example. Everything after ETH.begin(), including the events and the MQTT code, stays identical.

Test the Node

Plug in the cable, open the serial monitor and look for the IP ... Mbps line followed by MQTT connected over Ethernet. Then subscribe from any computer:

mosquitto_sub -h broker.connectmq.com -p 1883 -i watch-6b2d90 -v -t "connectmq/demo/eth-node-6b2d90/#"

Now pull the cable for twenty seconds and plug it back in. The serial log should show link down, no MQTT connection attempts while the cable is out, then link up, a new IP event and a reconnect. On the subscriber you will see status offline from the Last Will once the keep-alive expires, followed by online after recovery. This "cable pull test" is worth doing on every new hardware design; it catches power and PHY problems that a bench test with a stable link never reveals.

The public MQTT test broker is fine for this exercise, but its data is world-readable. Machine data belongs on private credentials.

Shaping Machine Data for MQTT

The telemetry in the sketch is deliberately minimal. On a real machine, decide early what goes into each message and how often. A few rules that hold up well in practice:

  • Separate fast and slow data. Counters, temperatures and states that change every second go on a telemetry topic at a fixed interval. Configuration, firmware version and serial numbers go on an info topic, published retained once after each connect.
  • Publish on change for states. A machine state such as RUNNING, IDLE or FAULT is more useful as a retained message published only when it changes than as a value repeated every five seconds.
  • Include units and a timestamp. {"spindleTempC":41.2,"ts":1760600000} is self-describing; a bare 41.2 is not.
  • Keep payloads small. Wired Ethernet has plenty of bandwidth, but the broker, your dashboards and any database downstream all pay for verbose messages.

If you publish into a ConnectMQ broker, these conventions also make the live topic explorer far easier to read during commissioning, because each machine shows up as a tidy branch with a handful of meaningful topics.

Industrial Deployment Checklist

  1. Outbound firewall rules. Plant networks often block everything by default. Ask for outbound TCP 8883 to your broker host; you do not need any inbound rules because the ESP32 initiates the connection.
  2. Use TLS on 8883. NetworkClientSecure works over Ethernet exactly like over Wi-Fi; follow ESP32 MQTT SSL with WiFiClientSecure and swap the class name.
  3. Static IP or DHCP reservation. Operations teams want to find the device. A DHCP reservation is usually easier to manage than a hard-coded address.
  4. NTP for timestamps. Industrial networks may block public NTP; use the plant's time server.
  5. Watchdog. Combine the event-driven design with the backoff in ESP32 MQTT reconnect logic and a hardware watchdog.
  6. Topic structure. Use an ISA-95 style hierarchy such as site/area/line/cell/device/telemetry from day one; it scales from one node to a plant.
  7. Power and grounding. Use DIN-rail supplies, shielded cable where drives are present, and consider PoE modules to remove a separate power run.

Fallback: Ethernet with Cellular Backup

For remote sites, some designs combine Ethernet with a cellular modem as backup. The MQTT layer stays the same; the firmware switches the Client it hands to PubSubClient. The ESP32 with SIM7600 4G LTE MQTT guide shows the cellular side.

From One Node to a Plant

Once a few nodes are publishing, the broker becomes shared infrastructure for the plant. A hosted MQTT broker removes the need to run and patch a server on the OT network. ConnectMQ detects ISA-95 and Unified Namespace topic structures automatically, so when your topics follow that structure the portal's devices view groups your Ethernet nodes by site, area and line, and threshold alert rules can flag a value that leaves its normal range. Using an MQTT broker in the cloud also lets engineering and remote support see the same live data without VPN access into the plant. For the full ESP32 learning path, see the ESP32 IoT tutorial.

Get Started with ConnectMQ

Create a free Serverless broker at app.connectmq.com, no credit card required. Give each Ethernet node its own credentials and watch your machine data appear in the live topic explorer within minutes.

Frequently Asked Questions

Does PubSubClient work over Ethernet on the ESP32?

Yes. PubSubClient accepts any Arduino Client, and on Arduino-ESP32 3.x NetworkClient works over both Wi-Fi and Ethernet through the same network stack.

Can I use a W5500 with an ESP32-S3 or ESP32-C3?

Yes. The W5500 connects over SPI, so it works on any ESP32 variant with free SPI pins. The LAN8720 needs the internal EMAC, which only the original ESP32 has among these.

Do I need the Arduino Ethernet library for a W5500?

Not on Arduino-ESP32 3.x. The built-in ETH library supports the W5500 directly and integrates with lwIP, so DNS, TLS and NetworkClient behave as they do on Wi-Fi.

Can the ESP32 use Wi-Fi and Ethernet at the same time?

Yes, both interfaces can be active. Make sure your firmware knows which one carries the MQTT connection and handles events from both.

Related articles

ESP-IDF MQTT Example: Build an esp-mqtt Client from Scratch

Build an ESP-IDF project from an empty folder with the esp-mqtt component: Wi-Fi station setup, the MQTT event handler, publish and subscribe, MQTT 5 properties and TLS with the certificate bundle.

8 min read

Using esp-mqtt MQTT 5 Features on ESP32 with ESP-IDF

A hands-on ESP-IDF project that enables MQTT 5.0 in esp-mqtt and uses user properties, message expiry, topic aliases, session expiry and response topics against a real MQTT 5 broker.

7 min read

All articles →

Your private MQTT broker is a few clicks away

Create an account, get your broker credentials and connect your first device — no servers to install, patch or scale.