Why MQTT for ESP32?
When a DHT22 reads 23.4°C, you have two options: send an HTTP POST (adds ~300 bytes of headers to your 8 bytes of data) or publish an MQTT message (8 bytes, no headers). For devices publishing every 5 seconds, that bandwidth difference compounds fast. MQTT also gives you a central broker that routes messages to any number of subscribers — dashboards, databases, automation rules — without the sensor needing to know they exist.
Architecture Overview
- Broker — the central hub (Mosquitto, HiveMQ, AWS IoT Core). Routes messages between publishers and subscribers.
- Publisher — your ESP32, sending sensor readings to topics like
home/bedroom/temperature. - Subscriber — a Node-RED flow, Grafana panel, or another ESP32 receiving the messages.
- Topic — a UTF-8 string path like
home/bedroom/temperature. Use/as a separator,+as a single-level wildcard,#as a multi-level wildcard.
Install PubSubClient
Open Arduino IDE → Sketch → Include Library → Manage Libraries. Search for PubSubClient (by Nick O’Leary) and install the latest version.
Connect and Publish
#include <WiFi.h>
#include <PubSubClient.h>
const char* ssid = "YourSSID";
const char* password = "YourPassword";
const char* mqtt_server = "broker.hivemq.com";
const int mqtt_port = 1883;
const char* client_id = "ESP32Sensor001"; // must be unique per broker
WiFiClient wifiClient;
PubSubClient mqtt(wifiClient);
void connectMQTT() {
while (!mqtt.connected()) {
Serial.print("Connecting to MQTT... ");
if (mqtt.connect(client_id)) {
Serial.println("connected");
} else {
Serial.printf("failed (rc=%d) — retry in 5 s\n", mqtt.state());
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
Serial.println("\nWi-Fi connected");
mqtt.setServer(mqtt_server, mqtt_port);
mqtt.setKeepAlive(60);
connectMQTT();
}
void loop() {
if (!mqtt.connected()) connectMQTT();
mqtt.loop(); // processes incoming messages and keepalives
// Publish a fake temperature reading every 10 s
static unsigned long last = 0;
if (millis() - last >= 10000) {
last = millis();
float temp = 22.5 + (random(-10, 10) / 10.0);
char buf[16];
dtostrf(temp, 4, 1, buf);
bool ok = mqtt.publish("home/bedroom/temperature", buf, true); // retained
Serial.printf("Published: %s (%s)\n", buf, ok ? "OK" : "FAIL");
}
}
Subscribing to Topics
void callback(char* topic, byte* payload, unsigned int length) {
String msg;
for (unsigned int i = 0; i < length; i++) msg += (char)payload[i];
Serial.printf("[%s] %s\n", topic, msg.c_str());
// Control LED via command topic
if (String(topic) == "home/bedroom/led/set") {
digitalWrite(2, msg == "ON" ? HIGH : LOW);
}
}
// In setup():
mqtt.setCallback(callback);
// In connectMQTT(), after mqtt.connect():
mqtt.subscribe("home/bedroom/led/set");
mqtt.subscribe("home/+/command"); // wildcard — all rooms
Last Will and Testament (LWT)
LWT lets the broker announce on your behalf when your device goes offline unexpectedly:
// In setup(), before connectMQTT():
mqtt.setWill(
"home/bedroom/status", // topic
"offline", // message
true, // retained
1 // QoS 1
);
// In connectMQTT(), after mqtt.connect():
mqtt.publish("home/bedroom/status", "online", true);
Publishing JSON with ArduinoJson
#include <ArduinoJson.h>
void publishSensorData(float temp, float hum) {
JsonDocument doc;
doc["device"] = client_id;
doc["temperature"] = serialized(String(temp, 1));
doc["humidity"] = serialized(String(hum, 1));
doc["uptime"] = millis() / 1000;
char buf[256];
serializeJson(doc, buf, sizeof(buf));
mqtt.publish("home/bedroom/sensors", buf, true);
}
MQTT over TLS (Port 8883)
#include <WiFiClientSecure.h>
const char* root_ca = R"(-----BEGIN CERTIFICATE-----
... broker root CA certificate here ...
-----END CERTIFICATE-----
)";
WiFiClientSecure secureClient;
PubSubClient mqtt(secureClient);
// In setup():
secureClient.setCACert(root_ca);
mqtt.setServer("your-broker.com", 8883);
Reconnect Pattern
The connection can drop. Subscriptions are lost when it does. Always re-subscribe inside the reconnect function:
void connectMQTT() {
int retries = 0;
while (!mqtt.connected() && retries < 5) {
if (mqtt.connect(client_id, "username", "password")) {
Serial.println("MQTT connected");
// Re-subscribe every reconnect
mqtt.subscribe("home/bedroom/led/set");
mqtt.publish("home/bedroom/status", "online", true);
return;
}
retries++;
delay(5000);
}
if (!mqtt.connected()) ESP.restart(); // hard reset after 5 fails
}
Testing with MQTT Explorer
Install MQTT Explorer (free, cross-platform) to visualise topics, publish test messages, and inspect payloads while developing. Connect to your broker, subscribe to # (all topics), and watch your ESP32 messages arrive in real time.