ESP32: publish MQTT messages to a broker
Send sensor data over MQTT from an ESP32 to a broker, and subscribe from another device. The pub/sub pattern that runs most home automation.
MQTT is the protocol I default to for IoT. It is light, it is well-supported, and every home automation stack speaks it. This tutorial gets you publishing from an ESP32 and subscribing from a second device on the same network.
What you need
- Two ESP32s (one to publish, one to subscribe), or one ESP32 and the
mosquitto_subCLI on your laptop - A computer running an MQTT broker. I use Mosquitto because it is a one-line install on most systems and it does not get in the way.
- The two devices need to be on the same network.
Install the broker
On a Raspberry Pi, macOS, or Linux box:
sudo apt install mosquitto # Debian / Ubuntu
brew install mosquitto # macOS
Then start it:
mosquitto -v
The -v flag prints every message to the terminal, which is useful for
debugging. In production you would run it as a service without -v.
If you do not have a broker yet and just want to test, you can use a public broker like
test.mosquitto.org, but do not publish anything you would not want the whole internet to see. It is unauthenticated.
Install the library
In the Arduino IDE: Sketch >> Include Library >> Manage Libraries >>
search PubSubClient by Nick O’Leary. Install it.
The publisher
#include <WiFi.h>
#include <PubSubClient.h>
const char* ssid = "your-wifi-ssid";
const char* password = "your-wifi-password";
const char* mqttServer = "192.168.1.50"; // your broker IP
const int mqttPort = 1883;
const char* topic = "ctrlaltbrian/sensor/temperature";
WiFiClient wifiClient;
PubSubClient client(wifiClient);
void connectWifi() {
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
}
void connectMqtt() {
while (!client.connected()) {
Serial.print("Connecting to MQTT...");
if (client.connect("esp32-publisher")) {
Serial.println("connected");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" retrying in 5s");
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
connectWifi();
client.setServer(mqttServer, mqttPort);
connectMqtt();
}
unsigned long lastPublish = 0;
void loop() {
client.loop();
if (millis() - lastPublish > 10000) {
lastPublish = millis();
float temp = 22.5 + (random(-50, 50) / 10.0); // fake reading
char payload[32];
snprintf(payload, sizeof(payload), "{\"temp\":%.1f}", temp);
client.publish(topic, payload);
Serial.println(payload);
}
}
This publishes a JSON-ish string every 10 seconds.
The subscriber
#include <WiFi.h>
#include <PubSubClient.h>
const char* ssid = "your-wifi-ssid";
const char* password = "your-wifi-password";
const char* mqttServer = "192.168.1.50";
const int mqttPort = 1883;
const char* topic = "ctrlaltbrian/sensor/temperature";
WiFiClient wifiClient;
PubSubClient client(wifiClient);
void callback(char* t, byte* payload, unsigned int length) {
Serial.print("Message on [");
Serial.print(t);
Serial.print("]: ");
for (unsigned int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) delay(500);
client.setServer(mqttServer, mqttPort);
client.setCallback(callback);
client.connect("esp32-subscriber");
client.subscribe(topic);
}
void loop() {
client.loop();
}
Upload to a second ESP32. Watch the Serial Monitor on the subscriber. You should see the publisher’s messages arriving every 10 seconds.
Test from the command line
If you only have one ESP32, you can subscribe from a terminal:
mosquitto_sub -h 192.168.1.50 -t "ctrlaltbrian/#" -v
The -v prints the topic too. This is what I use for debugging more than
half the time.
Topics, wildcards, and how to organize them
MQTT topics are slash-separated strings. Two wildcards:
+matches one level.ctrlaltbrian/sensor/+matchesctrlaltbrian/sensor/temperatureandctrlaltbrian/sensor/humidity.#matches everything below.ctrlaltbrian/#matches anything underctrlaltbrian/.
The convention I use:
ctrlaltbrian/<room>/<device>/<measurement>
For example:
ctrlaltbrian/kitchen/esp32-1/temperaturectrlaltbrian/kitchen/esp32-1/humidityctrlaltbrian/garage/esp32-2/door
This lets you subscribe to all of one room (ctrlaltbrian/kitchen/#) or all
of one measurement type across rooms (ctrlaltbrian/+/+/temperature).
Quality of service
There are three QoS levels:
- 0: at most once. Fire and forget. Use for things you do not care about losing (e.g. status updates that refresh every 10 seconds anyway).
- 1: at least once. May get duplicates. Use for sensor data.
- 2: exactly once. Slowest. Use for commands where duplicates would be bad.
For sensor publishing, QoS 0 is usually fine. The next reading will arrive in 10 seconds. For commands (e.g. “turn off the light”), use QoS 1.
When to use MQTT vs. HTTP
- MQTT when the device is the source of truth and pushes data. Persistent connection. Low overhead per message.
- HTTP when something needs to request data on demand, or when you are already running a web service.
Most home automation uses both. The sensors publish over MQTT, the dashboard reads them over MQTT, but the user clicks “turn off the light” by hitting an HTTP endpoint that publishes an MQTT command under the hood.
What you just built
A two-device pub/sub system that runs over your local network, with no cloud in the loop. This is the foundation for everything in the home automation book. Once you can publish and subscribe, you can wire up dashboards (Node-RED, Home Assistant, or a custom web page), you can write automations (“if temperature > 28 and time is between 14:00 and 18:00, turn on the fan”), and you can build a sensor network that does not depend on any cloud provider staying in business.
What to build next
- The home sensor hub project ties MQTT into a full dashboard.
- The ntfy tutorial is the notification-side companion.
- The book IoT with ESP32 bundles the connectivity tutorials.