MQTT Client Setup
MQTT Client Setup
Overview
Section titled “Overview”This section covers setting up an MQTT client on the ESP32 using the PubSubClient library. By the end of this section, you will be able to:
- Install and configure the PubSubClient library for ESP32
- Connect the ESP32 to an MQTT broker
- Implement connection retry logic with Wi-Fi dependency management
- Configure MQTT server address, port, and authentication
Prerequisites
Section titled “Prerequisites”- ESP32 with working Wi-Fi connection (01-09)
- A running MQTT broker (Mosquitto on the same network, default port 1883)
- PubSubClient library installed (via Library Manager or lib_deps)
Key Concepts
Section titled “Key Concepts”PubSubClient Library
Section titled “PubSubClient Library”The PubSubClient library by Nick O’Leary (creator of Node-RED) is the de facto MQTT client for Arduino-compatible boards. It handles the MQTT protocol details while providing a simple API.
Key Characteristics:
- Lightweight (~15 KB flash, ~2 KB RAM)
- Supports QoS 0 and QoS 1
- Supports MQTT 3.1.1
- No built-in TLS support (requires WiFiClientSecure for TLS — see 01-16)
- Single-threaded, must be polled in
loop()
Core Functions:
| Function | Description |
|---|---|
PubSubClient(wifiClient) | Constructor with Wi-Fi client |
setServer(broker, port) | Set MQTT broker address and port |
setCallback(callback) | Register message handler |
connect(clientId) | Connect to broker |
connect(clientId, user, pass) | Connect with authentication |
publish(topic, payload) | Publish a message |
subscribe(topic) | Subscribe to a topic |
loop() | Maintain connection, process messages |
connected() | Check MQTT connection status |
MQTT Client Identifier
Section titled “MQTT Client Identifier”Each MQTT client must have a unique client identifier (client ID). If two clients connect with the same ID, the broker will disconnect the first one. The convention is to use a device-specific name:
const char* clientId = "esp32-sensor-01";// Or generate dynamically:String clientId = "esp32-" + String(WiFi.macAddress());MQTT Server Configuration
Section titled “MQTT Server Configuration”The server configuration requires:
- Broker IP/Hostname: e.g.,
"192.168.1.100"or"mqtt.local"(mDNS) - Port: 1883 (unencrypted) or 8883 (TLS)
- Credentials (optional): Username and password
Implementation Steps
Section titled “Implementation Steps”Step 1: Basic MQTT Connection
Section titled “Step 1: Basic MQTT Connection”This example establishes an MQTT connection after Wi-Fi is connected:
#include <WiFi.h>#include <PubSubClient.h>
// Wi-Fi credentialsconst char* ssid = "YourWiFiSSID";const char* password = "YourWiFiPassword";
// MQTT broker configurationconst char* mqttServer = "192.168.1.100"; // Your broker IPconst int mqttPort = 1883;const char* mqttUser = ""; // Leave empty if no authconst char* mqttPassword = "";const char* clientId = "esp32-client";
WiFiClient wifiClient;PubSubClient client(wifiClient);
void setup() { Serial.begin(115200); delay(1000);
// Connect to Wi-Fi connectToWiFi();
// Configure MQTT client.setServer(mqttServer, mqttPort); client.setCallback(mqttCallback);
// Connect to MQTT broker connectToMQTT();}
void loop() { // Maintain MQTT connection (must be called regularly) if (!client.connected()) { connectToMQTT(); } client.loop();
// Your other code here delay(100);}
void connectToWiFi() { Serial.print("Connecting to Wi-Fi"); WiFi.begin(ssid, password);
int attempts = 0; while (WiFi.status() != WL_CONNECTED && attempts < 20) { delay(500); Serial.print("."); attempts++; }
if (WiFi.status() == WL_CONNECTED) { Serial.println("\nWi-Fi connected!"); Serial.print("IP: "); Serial.println(WiFi.localIP()); } else { Serial.println("\nWi-Fi failed! Restarting..."); ESP.restart(); }}
void connectToMQTT() { // Loop until connected while (!client.connected()) { Serial.print("Attempting MQTT connection...");
if (client.connect(clientId, mqttUser, mqttPassword)) { Serial.println("connected!");
// Subscribe to topics after successful connection client.subscribe("esp32/commands");
} else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" retrying in 5 seconds"); delay(5000); } }}
void mqttCallback(char* topic, byte* payload, unsigned int length) { Serial.print("Message arrived on topic: "); Serial.println(topic);
String message; for (unsigned int i = 0; i < length; i++) { message += (char)payload[i]; } Serial.print("Message: "); Serial.println(message);}Step 2: MQTT Connection with Last Will and Testament (LWT)
Section titled “Step 2: MQTT Connection with Last Will and Testament (LWT)”LWT is a feature that sends a predefined message when a client unexpectedly disconnects:
// LWT configurationconst char* willTopic = "esp32/status";const char* willMessage = "offline";int willQos = 1;bool willRetain = true;
void connectToMQTT() { while (!client.connected()) { Serial.print("Attempting MQTT connection...");
// Connect with LWT: (clientId, user, pass, willTopic, willQos, willRetain, willMessage) if (client.connect(clientId, mqttUser, mqttPassword, willTopic, willQos, willRetain, willMessage)) { Serial.println("connected!");
// Publish online status client.publish("esp32/status", "online", true);
// Subscribe client.subscribe("esp32/commands"); } else { Serial.print("failed, rc="); Serial.println(client.state()); delay(5000); } }}Step 3: MQTT Connection with Authentication
Section titled “Step 3: MQTT Connection with Authentication”If your broker requires authentication, pass the username and password:
const char* mqttUser = "iot-device";const char* mqttPassword = "secure-password-123";
void connectToMQTT() { while (!client.connected()) { if (client.connect(clientId, mqttUser, mqttPassword)) { Serial.println("MQTT connected with authentication"); client.subscribe("esp32/commands"); } else { Serial.print("MQTT auth failed, state: "); Serial.println(client.state()); delay(5000); } }}Step 4: Checking Connection Return Codes
Section titled “Step 4: Checking Connection Return Codes”The client.state() function returns the connection state. Common values:
| Code | Constant | Meaning |
|---|---|---|
| -4 | MQTT_CONNECTION_TIMEOUT | Connection timed out |
| -3 | MQTT_CONNECTION_LOST | Connection lost |
| -2 | MQTT_CONNECT_FAILED | Connection failed |
| -1 | MQTT_DISCONNECTED | Client disconnected |
| 0 | MQTT_CONNECTED | Successfully connected |
| 1 | MQTT_CONNECT_BAD_PROTOCOL | Wrong protocol version |
| 2 | MQTT_CONNECT_BAD_CLIENT_ID | Client ID rejected |
| 3 | MQTT_CONNECT_UNAVAILABLE | Server unavailable |
| 4 | MQTT_CONNECT_BAD_CREDENTIALS | Username/password wrong |
| 5 | MQTT_CONNECT_UNAUTHORIZED | Not authorized |
Verification
Section titled “Verification”- ESP32 connects to the MQTT broker after Wi-Fi connection
- Serial Monitor shows “MQTT connected” message
- Subscribe to
esp32/statusfrom another client shows “online” message - Disconnecting ESP32 sends “offline” via LWT
- MQTT connection with authentication succeeds (if applicable)
Troubleshooting
Section titled “Troubleshooting”MQTT connection fails repeatedly
Section titled “MQTT connection fails repeatedly”Common return codes and solutions:
- rc=4: Bad credentials — check MQTT username and password
- rc=5: Not authorized — check broker ACL settings
- rc=-2: Network unreachable — verify broker IP and Wi-Fi connectivity
- rc=-4: Timeout — broker may be on different network or firewall blocking port 1883
General solutions:
- Verify broker is running:
mosquitto_sub -h 192.168.1.100 -t "test"on a computer - Check firewall: Port 1883 must be open between ESP32 and broker
- Ping broker from ESP32 (use
pingcommand if available) - Check client ID uniqueness: No other device should use the same
clientId
”WiFiClient not connected” error in MQTT
Section titled “”WiFiClient not connected” error in MQTT”Cause: Wi-Fi disconnects before MQTT reconnection is attempted.
Solution: Always verify Wi-Fi status before MQTT operations:
void loop() { if (WiFi.status() == WL_CONNECTED) { if (!client.connected()) { connectToMQTT(); } client.loop(); } else { // Reconnect Wi-Fi first connectToWiFi(); }}MQTT messages not being received
Section titled “MQTT messages not being received”Causes:
client.loop()not called frequently enough- Not subscribed to the correct topic
- QoS mismatch between publisher and subscriber
Solutions:
- Ensure
client.loop()is called at least every 100ms - Verify subscription topic exactly matches publisher topic
- Try with QoS 0 for both publisher and subscriber
Best Practices
Section titled “Best Practices”- Combine Wi-Fi and MQTT reconnection: Always check Wi-Fi before attempting MQTT connection
- Use unique client IDs: Append MAC address to client ID to avoid conflicts
- Enable LWT: The Last Will message helps the broker (and Node-RED) detect offline devices
- Keep
client.loop()frequent: Call it in everyloop()iteration, not inside long delays - Log connection state: Using
client.state()helps diagnose connection issues - Set a shorter keep-alive: Default is 15 seconds; for unstable networks, reduce to 10 seconds via
client.setKeepAlive(10)
Summary
Section titled “Summary”- PubSubClient is the standard MQTT library for ESP32, configured via
setServer()andsetCallback() - MQTT connection requires an active Wi-Fi connection — always check Wi-Fi status first
- Every client needs a unique ID; duplicates cause disconnections
- LWT (Last Will and Testament) enables offline detection
client.loop()must be called frequently to maintain connection and process messages- Connection return codes help diagnose authentication and network issues