Find out how to link an ESP32 with an MQTT broker using the Arduino IDE. This guide shows how to set up the network, send telemetry data, receive command messages, and deal with connection losses.
Quick answer
First, connect an ESP32 to an MQTT broker. Install the PubSubClient and WiFi libraries in the Arduino IDE. Use WiFi.h to join your local WiβFi network. Then create a PubSubClient object that points to your brokerβs IP or domain, using port 1883. In the main loop, call client.loop() so the library can keep the connection alive. Add a reconnection routine to handle any network drops. This ESP32 MQTT tutorial for beginners gives a readyβtoβuse code example for publishing sensor data and receiving control commands.
Introduction to MQTT on the ESP32
Message Queuing Telemetry Transport (MQTT) is a lightweight, publishβsubscribe network protocol made for devices with limited resources. Traditional HTTP uses a heavy requestβresponse style, but MQTT lets microcontrollers such as the ESP32 talk asynchronously over TCP/IP. That makes it very efficient for batteryβpowered IoT nodes and remote telemetry systems. In an MQTT setup, devices donβt send messages straight to each other. They connect to a central hub known as an MQTT broker. Clients publish messages to named channels called topics, and other clients subscribe to those topics to get the data.
The ESP32 works as an MQTT client, so it can send sensor readings and receive remote control commands while using little processing power and bandwidth.
For a closely related example, see How to Connect ESP32 to WiFi Using Arduino IDE.
ESP32 MQTT Data Flow
This flow diagram represents the path of telemetry and command data between the ESP32, the network, and the end-user application.
- 1ESP32 Client
Reads local GPIO sensors and formats payloads as lightweight JSON strings.
Edge hardware layer
- 2Wi-Fi Router
Routes standard TCP/IP packets between the local ESP32 node and the wider internet.
Local network layer
- 3MQTT Broker
Acts as the central message router, receiving published data on topics and distributing them to subscribers.
Cloud routing layer
- 4Subscriber Application
Receives real-time telemetry updates and publishes control commands back to the broker.
User interface layer
Hardware and Software Prerequisites
To follow this ESP32 MQTT tutorial for beginners, you need a regular ESP32 development board, such as the ESP32-WROOM-32D, and a micro-USB cable to program it. For software, install the Arduino IDE and set it up for ESP32 boards. You also have to add the PubSubClient library from Nick O'Leary. It provides a simple API for working with MQTT connections on Arduinoβcompatible hardware. To add it, open the Arduino IDE, go to Sketch β Include Library β Manage Libraries, type βPubSubClientβ in the search box, and click Install. Finally, you need access to a local or public MQTT broker.
Public brokers such as broker.hivemq.com or broker.emqx.io are good for testing, but avoid them for private or sensitive telemetry data.
For a closely related example, see How to Build a Reliable Smart Irrigation System Using ESP32.
ESP32 MQTT Connection Architecture
Before you start coding, it helps to know how the ESP32 works with the network layers. First, the ESP32 uses its builtβin WiβFi radio to make a local link to your wireless router. Once an IP address is assigned by DHCP, the ESP32 creates a standard TCP socket that points to the IP address or domain name of the MQTT broker on port 1883 (or on port 8883 for encrypted TLS traffic). After the TCP connection is set up, the ESP32 sends an MQTT CONNECT packet that includes its unique client ID, any credentials you choose, and keepβalive settings. The broker replies with a CONNACK (Connection Acknowledged) packet.
When the link is active, the ESP32 can publish data to telemetry topics or subscribe to control topics. The broker then routes messages, so any data sent to a subscribed topic is delivered to the ESP32 right away.
For a closely related example, see Security of Wireless Sensor Networks in the Era of Artificial Intelligence and IoT: A Systematic Literature Review.
MQTT Quality of Service (QoS) Levels
MQTT offers three Quality of Service levels to balance message delivery reliability against network overhead on resource-constrained microcontrollers.
| Factor | Engineering view | Why it matters |
|---|---|---|
| QoS 0 (At most once) | Fire and forget. The message is sent once with no confirmation. Lowest overhead, but messages can be lost. | Best for non-critical sensor streams |
| QoS 1 (At least once) | The broker acknowledges receipt. If the acknowledgment is lost, the message is sent again, potentially causing duplicates. | Standard choice for telemetry |
| QoS 2 (Exactly once) | A four-step handshake ensures the message is delivered exactly once without duplication. Highest overhead. | Best for billing or critical control |
Step-by-Step Code Implementation
The following C++ sketch establishes a secure Wi-Fi connection, connects to a public MQTT broker, publishes analog sensor readings from pin 34 every five seconds, and listens for control commands on a subscription topic. When a message containing '1' is received, it turns on the onboard LED (typically mapped to GPIO 2 on most ESP32 development boards).
#include
#include
// Network Credentials
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
// MQTT Broker Settings
const char* mqtt_server = "broker.hivemq.com";
const int mqtt_port = 1883;
const char* topic_publish = "esp32/telemetry";
const char* topic_subscribe = "esp32/commands";
WiFiClient espClient;
PubSubClient client(espClient);
unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE (50)
char msg[MSG_BUFFER_SIZE];
void setup_wifi() {
delay(10);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("Wi-Fi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
String messageTemp;
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
messageTemp += (char)payload[i];
}
Serial.println();
if (String(topic) == topic_subscribe) {
if (messageTemp == "1") {
digitalWrite(2, HIGH);
Serial.println("LED turned ON");
} else if (messageTemp == "0") {
digitalWrite(2, LOW);
Serial.println("LED turned OFF");
}
}
}
void reconnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
String clientId = "ESP32Client-";
clientId += String(random(0xffff), HEX);
if (client.connect(clientId.c_str())) {
Serial.println("connected");
client.subscribe(topic_subscribe);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}
void setup() {
pinMode(2, OUTPUT);
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
unsigned long now = millis();
if (now - lastMsg > 5000) {
lastMsg = now;
int sensorValue = analogRead(34);
snprintf(msg, MSG_BUFFER_SIZE, "{"sensor_val": %d}", sensorValue);
Serial.print("Publish message: ");
Serial.println(msg);
client.publish(topic_publish, msg);
}
}
Understanding Callbacks and the Main Loop
Two functions are essential for this code to work: the callback function and client.loop(). The callback is set up in setup() with client.setCallback(callback). When a message arrives on a topic you subscribed to, the broker sends it to the ESP32, which then runs the callback. The message data comes as an array of raw bytes, so you need to cast those bytes to characters to read them as strings or numbers. You must call client.loop() repeatedly in the main loop. It acts as the heartbeat of the MQTT engine. It handles incoming packets, runs callbacks, and sends regular keepβalive ping packets to the broker.
If client.loop() is blocked by long delays (for example, using delay() inside the loop), the broker will think the ESP32 is offline and will close the socket. Use nonβblocking timing methods like millis() for any periodic work.
Understanding MQTT Connection Return Codes
When the connection to the MQTT broker fails, the client.state() method returns an integer code. Use this key reference to diagnose connection dropouts.
The broker did not respond within the keep-alive window. Check network latency.
The client could not establish a network connection. Verify broker address and port.
The broker rejected the credentials. Verify access control settings.
The client is not authorised to connect. Check broker ACL rules.
Troubleshooting Common Connection Issues
When you work with the ESP32 and MQTT, you will often see connection problems. If the ESP32 cannot reach the broker, look at the serial monitor. A typical sign is a negative return code (rc) shown by the reconnect() function. For instance, a return code of -2 means the client cannot join the network, which usually means a DNS problem or a wrong broker address. A return code of -4 indicates a connection timeout, which typically means the network is unstable. A common mistake is using the same client ID on more than one device. MQTT brokers need each client to have a unique ID.
If two ESP32 devices try to connect with the same client ID, the broker will keep dropping one so the other can stay connected. This creates a loop of disconnects and reconnects. Create a random client ID with the random() function when you set up the connection to avoid this problem.
Best Practices for Deploying IoT Nodes
While public MQTT brokers on port 1883 are ideal for testing, they lack encryption and authentication. For production deployments, secure your nodes by using username and password authentication in the client.connect() method. Additionally, implement TLS/SSL using WiFiClientSecure instead of the standard WiFiClient. This encrypts the payload and credentials, preventing eavesdropping and man-in-the-middle attacks on public networks. Finally, manage your device's power footprint. If your ESP32 runs on batteries, continuously keeping a Wi-Fi and MQTT socket open will drain the battery within hours. Consider using ESP32 deep sleep modes. Before entering deep sleep, publish your telemetry data, wait for the transaction to complete, and then shut down the radio.
Upon waking, the ESP32 will reboot, reconnect to the network, publish the new data, and return to sleep, extending battery life significantly.
Useful research starting points
Key takeaways
- MQTT is an efficient, low-overhead publish-subscribe protocol ideal for ESP32-based IoT nodes.
- The PubSubClient library provides a simple, robust interface to handle MQTT connections on the ESP32 platform.
- The client.loop() function must run continuously to prevent connection timeouts and handle message callbacks.
- Duplicate client IDs on the same broker will cause continuous network drops and reconnection loops.
- For production nodes, secure the connection using TLS/SSL and implement authentication instead of raw TCP on port 1883.
Questions engineers often ask
Can I use MQTT without an internet connection?
Yes. MQTT is a standard network protocol that works over any local network using TCP/IP. You can run a private MQTT broker like Mosquitto on a local server, a Raspberry Pi, or your development machine, allowing your ESP32 to communicate locally without internet access.
What happens if the Wi-Fi connection drops?
If the Wi-Fi connection drops, the MQTT client will lose its connection to the broker. The provided code handles this by checking the connection status in the loop() function and calling the reconnect() routine to re-establish both Wi-Fi and MQTT connectivity once the network recovers.
Why does my ESP32 disconnect from the MQTT broker every few seconds?
This is typically caused by client ID conflicts. MQTT brokers disconnect any client that connects with an ID already in use. Ensure your code generates a unique client ID, such as by appending a random value or using the ESP32's MAC address.
How do I secure my ESP32 MQTT connection?
To secure your connection, use WiFiClientSecure instead of WiFiClient to enable TLS/SSL encryption on port 8883. You must also configure your MQTT broker to require a username and password, which you pass within the client.connect() method.
Continue learning: esp32 mqtt tutorial for beginners
Explore more embedded systems tutorials and technical guides on Fried Engineers.