Learning Goals 5 min
Time to publish from a real ESP to a real broker. PubSubClient is the classic Arduino MQTT library — small, robust, works on every WiFi-capable board. By the end of this lesson you will be able to:
- Install PubSubClient and connect an ESP to a public broker (
test.mosquitto.org). - Publish a sensor reading to a custom topic with retain=true.
- Verify the message arrived using MQTT Explorer on your laptop.
Warm-Up 10 min
Hardware: ESP (8266 or 32) + a sensor on A0 (pot or LDR). MQTT Explorer running on your laptop, connected to test.mosquitto.org.
Install PubSubClient
Library Manager → search "PubSubClient" (by Nick O'Leary). Install.
Pick a unique topic prefix
Public brokers are crowded. Use a prefix nobody else will collide with:
advaslearning/aliya/sensor1/value
The middle aliya = your name or something unique. Avoid generic names like test/foo.
New Concept · The five PubSubClient calls 25 min
Setup boilerplate
#if defined(ESP8266)
#include <ESP8266WiFi.h>
#elif defined(ESP32)
#include <WiFi.h>
#endif
#include <PubSubClient.h>
const char* SSID = "YourNetwork";
const char* PASSWORD = "YourPassword";
const char* MQTT_SERVER = "test.mosquitto.org";
const int MQTT_PORT = 1883;
const char* CLIENT_ID = "advaslearning-aliya-esp1"; // unique per device
WiFiClient netClient;
PubSubClient mqtt(netClient);connect()
bool connectMQTT() {
while (!mqtt.connected()) {
Serial.print("# MQTT connecting...");
if (mqtt.connect(CLIENT_ID)) {
Serial.println(" OK");
return true;
}
Serial.print(" failed rc=");
Serial.println(mqtt.state());
delay(2000);
}
return true;
}Return codes (from mqtt.state()):
-4connection timeout-3connection lost-2connect failed (network issue)-1disconnected0connected1..5protocol errors (bad client ID, unauthorised, etc.)
publish()
mqtt.publish("advaslearning/aliya/sensor1/value", "22.5", true);
// ^ topic ^ payload ^ retainThe retain flag (last arg) is optional; defaults to false. For a sensor reading you almost always want true.
loop()
void loop() {
if (!mqtt.connected()) connectMQTT();
mqtt.loop(); // CRITICAL: services incoming messages, keep-alive pings
static unsigned long lastPub = 0;
if (millis() - lastPub >= 5000) {
lastPub = millis();
int raw = analogRead(A0);
char buf[16];
snprintf(buf, sizeof(buf), "%d", raw);
mqtt.publish("advaslearning/aliya/sensor1/raw", buf, true);
}
}mqtt.loop() is the equivalent of server.handleClient() in WebServer. Must run every iteration.
Last Will and Testament setup
if (mqtt.connect(CLIENT_ID,
"advaslearning/aliya/sensor1/status", // LWT topic
0, // LWT QoS
true, // LWT retain
"offline")) { // LWT payload
mqtt.publish("advaslearning/aliya/sensor1/status", "online", true);
}One call sets up LWT AND connects. Then immediately publish "online" (retained) so subscribers see the current state.
Worked Example · ESP publishes pot reading 25 min
Step 1 — wire
Pot wiper on A0. Power.
Step 2 — sketch
// L04-14 · ESP → MQTT publish
#if defined(ESP8266)
#include <ESP8266WiFi.h>
#elif defined(ESP32)
#include <WiFi.h>
#endif
#include <PubSubClient.h>
const char* SSID = "YourNetwork";
const char* PASSWORD = "YourPassword";
const char* MQTT_SERVER = "test.mosquitto.org";
const int MQTT_PORT = 1883;
const char* CLIENT_ID = "advaslearning-aliya-pot1";
const char* TOPIC_VALUE = "advaslearning/aliya/pot1/value";
const char* TOPIC_STATUS = "advaslearning/aliya/pot1/status";
WiFiClient netClient;
PubSubClient mqtt(netClient);
void connectWiFi() {
WiFi.mode(WIFI_STA);
WiFi.begin(SSID, PASSWORD);
Serial.print("# WiFi");
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
Serial.println();
}
bool connectMQTT() {
if (mqtt.connected()) return true;
Serial.print("# MQTT connecting...");
// (clientId, willTopic, willQos, willRetain, willMessage)
if (mqtt.connect(CLIENT_ID, TOPIC_STATUS, 0, true, "offline")) {
Serial.println(" OK");
mqtt.publish(TOPIC_STATUS, "online", true);
return true;
}
Serial.print(" failed rc="); Serial.println(mqtt.state());
return false;
}
void setup() {
Serial.begin(115200);
connectWiFi();
mqtt.setServer(MQTT_SERVER, MQTT_PORT);
}
void loop() {
if (!mqtt.connected()) {
if (!connectMQTT()) { delay(2000); return; }
}
mqtt.loop();
static unsigned long lastPub = 0;
if (millis() - lastPub >= 5000) {
lastPub = millis();
int raw = analogRead(A0);
char buf[16];
snprintf(buf, sizeof(buf), "%d", raw);
if (mqtt.publish(TOPIC_VALUE, buf, true)) {
Serial.print("# pub "); Serial.println(buf);
}
}
}Step 3 — upload and watch
Open MQTT Explorer (connected to test.mosquitto.org). Subscribe to advaslearning/aliya/# (or your prefix). After a few seconds you should see status: online and value updating every 5 s.
Step 4 — test the LWT
Unplug your ESP (or kill its WiFi). After ~30 s, MQTT Explorer should show status: offline. The broker auto-published it on the ESP's behalf.
Plug back in → online reappears.
Step 5 — let two ESPs publish to the same broker
Change the second board's CLIENT_ID + topic prefix. Both publish to test.mosquitto.org. MQTT Explorer shows both data streams side by side. The broker handles fan-out trivially.
Try It Yourself 15 min
Goal: Publish JSON instead of plain text: {"value": 512, "uptime": 60}. Use ArduinoJson to build the payload.
Goal: Publish a second topic .../sensor2/value with a TMP36 (or DHT) temperature. Update at a different rate (every 30 s vs 5 s for the pot).
Goal: Install Mosquitto locally on your laptop (free, 1-minute install). Change MQTT_SERVER to your laptop's IP. The ESP now publishes to your private broker — no internet required.
Hint
Mosquitto install on Mac: brew install mosquitto. Linux: sudo apt install mosquitto. Windows: download from mosquitto.org. Then run mosquitto -v in a terminal. ESP connects to 192.168.x.x (your laptop's LAN IP) on port 1883.
Mini-Challenge · Verify your topic structure 10 min
- In MQTT Explorer, subscribe to
advaslearning/aliya/#. - Confirm you see:
status(online/offline),value(live numbers),sensor2/value(if you did the medium task). - Note the "retained" flag indicator on retained topics.
- Screenshot the explorer tree.
Recap 5 min
PubSubClient = ~10 lines of setup + 1 line per publish. Connect, set LWT, publish often, call mqtt.loop() every iteration. Public brokers are great for learning; local Mosquitto is great for production. Tomorrow we add the other half — subscribing to commands.
- PubSubClient
- The classic Arduino MQTT library. Small, robust, works on UNO + ESP + Mega. Install via Library Manager.
- setServer / connect / publish / subscribe / loop
- The five core methods.
setServeronce in setup;connectwhen disconnected;publish/subscribeas needed;loopevery iteration. - Client ID
- Unique identifier per connection. Two clients with same ID = the second one kicks the first off.
- Topic prefix
- The first one or two levels of a topic, used to namespace your project. Essential on public brokers.
- JSON payload
- Structured message body. Most smart-home integrations expect JSON.
- Local broker
- Mosquitto running on your laptop or Raspberry Pi. No internet needed; lower latency; private.
- Retain flag
- Optional 3rd arg to publish(). When true, broker keeps last message for new subscribers.
- LWT (Last Will)
- Configured as the 2nd–5th args to connect(). Broker publishes on ungraceful disconnect.
Homework 5 min
- Get your ESP publishing to test.mosquitto.org under your unique prefix.
- Screenshot MQTT Explorer showing the topic tree + retained values.
- Read ahead to ARD-L04-15 (Subscribing to Topics). Bring an LED on the ESP for tomorrow.