Skip to content
Taimoor Arshad
Back to blog

WiFi and MQTT on the ESP32

4 min read

Getting an ESP32 online over WiFi and pushing MQTT messages through HiveMQ. C++ firmware, Paho client, LEDs for feedback.

بسم الله الرحمن الرحيم

MQTT is the messaging standard for lightweight IoT, and the ESP32 is where a lot of my hardware experiments start. This post walks through getting an ESP32 connected to WiFi, subscribing to an MQTT broker, and publishing messages back, with LEDs on the board reacting to incoming commands so you can see the round-trip in real time.

If you're new to MQTT, the elevator pitch is that it's a publish/subscribe protocol built for constrained devices. You have a broker in the middle. Devices publish messages to topics, and other devices subscribe to those topics to receive them. It's small, it's reliable, and every major cloud IoT platform speaks it.

The problem MQTT solves

You could roll your own device-to-cloud messaging with HTTP or WebSockets, and for a hobby project that's fine. But HTTP has significant overhead per message: TCP handshake, TLS negotiation, HTTP headers. When you're running on a microcontroller with a small battery and a limited data plan, those bytes matter. MQTT keeps its packets small, keeps its connections open, and gives you QoS levels that let you trade throughput for reliability where it matters.

Proprietary protocols exist, but they lock you to a specific broker or cloud vendor. MQTT is open, standardized, and every serious broker (Mosquitto, HiveMQ, EMQX, AWS IoT Core, Azure IoT Hub) speaks it. Start with MQTT and you can port to a different broker later with almost no firmware change.

The stack

  • Firmware: C++ on the ESP32, built with PlatformIO.

  • Networking: the standard WiFi.h library.

  • MQTT client: Paho MQTT for C++ on the device, Paho MQTT for Python on the desktop.

  • Broker: HiveMQ's public broker for testing.

  • Feedback: LEDs on the ESP32 driven by incoming commands.

The Python test client on the desktop is worth calling out. It's tiny, but having a scripted way to publish and subscribe means I can test scenarios without touching the firmware. Change payloads, retention flags, QoS levels, all from the Python side. When you're debugging a communication issue, being able to isolate the firmware from the desktop side is huge.

How it works, end to end

The ESP32 boots and reads its WiFi credentials from a compile-time header (I don't check them into version control; a secrets.h.template file gets copied to secrets.h locally). It connects to the local network, waits for an IP, then hands control to the Paho client. Paho opens a TCP session to HiveMQ, subscribes to a control topic, and starts publishing a status heartbeat on a separate topic.

From the desktop, the Python script publishes messages to the control topic. Each message specifies an LED pattern: solid, blink, breathe, off. The ESP32's on-message handler decodes the payload and reconfigures the LED strip accordingly.

Why HiveMQ for testing

HiveMQ runs a free public broker specifically for prototyping. You get a clean broker you don't have to stand up yourself, and it's rate-limited enough that you can't accidentally spam it into oblivion. For a real deployment, you'd run your own broker (Mosquitto is the standard open-source choice) or use a hosted one, but for learning and prototyping, HiveMQ's public broker is hard to beat.

What worked

  • WiFi connection stayed up across multi-day runs, which is a lot better than the earlier ESP32 firmware releases where the stack would occasionally hang after a broker disconnect.

  • Both directions of the pub/sub link were solid at QoS 0 and QoS 1. I didn't need QoS 2 for this project, so I didn't test it thoroughly.

  • LED patterns responded to commands with under 100 ms of round-trip latency over the public broker, which is fast enough to feel real-time.

  • Multi-topic routing worked without special handling. Subscribe to a wildcard topic and the on-message handler gets called for every match.

Next steps

This is the foundation for a bigger IIoT setup I've been sketching, where the ESP32 becomes a sensor node feeding a control loop. A few things I want to add:

  • OTA firmware updates over the same MQTT connection, so I don't have to physically plug in a device to flash new firmware.

  • TLS on the broker connection with a real certificate. HiveMQ's public broker supports it, my firmware just needs to bring in the mbedTLS bits and load the CA cert.

  • Last-will messages so the broker publishes a "device offline" notification if the ESP32 drops the connection ungracefully. Useful for spotting a dead node in a fleet.

  • Structured payloads in JSON or CBOR instead of ad-hoc strings, so the desktop side doesn't have to parse a custom format.

Salaam.