MQTT is the language that millions of smart home devices, factory machines and sensors use to talk to each other. Devices don't send messages to each other directly: they send them to a broker, a server that passes each message on to everyone who has asked for it. In this lesson the ESP32 publishes its readings to a free public broker, and an app on your phone turns them into a dashboard with gauges. The phone can also publish a message back, to change the colour of an RGB LED.
What you'll learn
- How the MQTT broker, topics, publish and subscribe work
- How to send readings from the ESP32 with the PubSubClient library
- How to react to messages that arrive
- How to build a dashboard in an MQTT phone app
What you need
- ESP32 board, expansion board and USB cable (ESP32 IoT Starter Kit)
- DHT11 temperature and humidity module (45-in-1 Sensor Kit)
- Photoresistor module (45-in-1 Sensor Kit)
- RGB LED module (45-in-1 Sensor Kit)
- 3 resistors, 220 Ω (ESP32 IoT Starter Kit)
- A phone with a free MQTT dashboard app, such as IoT MQTT Panel
- A 2.4 GHz Wi-Fi network
- The printed parts from the 3D printing box below (two module stands), or the cardboard version described there
- Breadboard and jumper wires (ESP32 IoT Starter Kit)
Wiring

| From | To |
|---|---|
| DHT11 module S | ESP32 GPIO 27 |
| Photoresistor module S | ESP32 GPIO 34 |
| Both modules + and − | 3V3 and GND |
| RGB module R | 220 Ω resistor, then GPIO 25 |
| RGB module G | 220 Ω resistor, then GPIO 26 |
| RGB module B | 220 Ω resistor, then GPIO 33 |
| RGB module − | GND |
Sensor modules from different makers can label and order their pins differently. Always follow the labels printed on your module.
Choose your topics
Everyone in the world shares the free public broker at broker.hivemq.com, so your topics need a name nobody else uses. The sketch builds them from BASE, for example eementor/maya-7q2:
| Topic | Who publishes | Message |
|---|---|---|
BASE/temperature |
ESP32, every 10 s | Temperature in °C, for example 21.4
|
BASE/humidity |
ESP32, every 10 s | Humidity in % |
BASE/light |
ESP32, every 10 s | Light level, 0 to 100 |
BASE/led |
Your phone |
red, green, blue, white or off
|
Important: a public broker is like a notice board in a park. Anyone who knows your topic can read your messages and send their own, so only use it for harmless data like room temperature, never for anything private.
The sketch
Replace YOUR_WIFI_NAME and YOUR_WIFI_PASSWORD with your own network details (2.4 GHz only).
In the Arduino IDE, open Library Manager and install PubSubClient (by Nick O'Leary) and the DHT sensor library by Adafruit.
Change BASE to your own name plus a few random letters.
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
const char* WIFI_NAME = "YOUR_WIFI_NAME";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* BROKER = "broker.hivemq.com";
const String BASE = "eementor/YOUR_NAME_123"; // must be unique
const int LIGHT_PIN = 34;
const int RED_PIN = 25, GREEN_PIN = 26, BLUE_PIN = 33;
WiFiClient net;
PubSubClient mqtt(net);
DHT dht(27, DHT11);
unsigned long lastPublish = 0;
void colour(int r, int g, int b) {
analogWrite(RED_PIN, r);
analogWrite(GREEN_PIN, g);
analogWrite(BLUE_PIN, b);
}
// Runs every time a message arrives on a topic we subscribed to
void onMessage(char* topic, byte* payload, unsigned int length) {
String text;
for (unsigned int i = 0; i < length; i++) text += (char)payload[i];
text.trim();
text.toLowerCase();
Serial.println(String("Message on ") + topic + ": " + text);
if (text == "red") colour(255, 0, 0);
else if (text == "green") colour(0, 255, 0);
else if (text == "blue") colour(0, 0, 255);
else if (text == "white") colour(255, 255, 255);
else if (text == "off") colour(0, 0, 0);
}
void connectMqtt() {
while (!mqtt.connected()) {
String id = "eementor-" + String((uint32_t)ESP.getEfuseMac(), HEX);
Serial.print("Connecting to the broker... ");
if (mqtt.connect(id.c_str())) {
Serial.println("connected");
mqtt.subscribe((BASE + "/led").c_str());
} else {
Serial.printf("failed (%d), trying again in 5 s\n", mqtt.state());
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
pinMode(RED_PIN, OUTPUT);
pinMode(GREEN_PIN, OUTPUT);
pinMode(BLUE_PIN, OUTPUT);
dht.begin();
WiFi.begin(WIFI_NAME, WIFI_PASSWORD);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(" connected");
mqtt.setServer(BROKER, 1883);
mqtt.setCallback(onMessage);
}
void loop() {
if (!mqtt.connected()) connectMqtt();
mqtt.loop(); // receive messages and keep the connection alive
if (millis() - lastPublish > 10000) {
lastPublish = millis();
float t = dht.readTemperature();
float h = dht.readHumidity();
int light = analogRead(LIGHT_PIN) * 100 / 4095;
if (!isnan(t)) mqtt.publish((BASE + "/temperature").c_str(), String(t, 1).c_str());
if (!isnan(h)) mqtt.publish((BASE + "/humidity").c_str(), String(h, 0).c_str());
mqtt.publish((BASE + "/light").c_str(), String(light).c_str());
Serial.printf("Published %.1f C, %.0f %%, light %d\n", t, h, light);
}
}
Build the phone dashboard
- Install IoT MQTT Panel (or another MQTT dashboard app) and add a connection: broker
broker.hivemq.com, port1883, protocol TCP, any client ID. - Add a dashboard, then add a Gauge panel with the topic
eementor/YOUR_NAME_123/temperature, range 0 to 40. Add two more gauges for humidity and light. - Add a Button or Combo box panel that publishes
red,green,blue,whiteandoffto…/led. - Watch the gauges update every 10 seconds, and switch the LED colour. It works on mobile data too.
No phone? Open the HiveMQ WebSocket client in a browser (search for "HiveMQ MQTT websocket client"), connect, subscribe to eementor/YOUR_NAME_123/# and publish to the led topic from there.
How it works
The ESP32 opens one connection to the broker and keeps it open. mqtt.publish() sends a message with a topic; the broker passes it to every client that subscribed to that topic, such as your phone. When your phone publishes to …/led, the broker passes it to the ESP32, because it subscribed to that topic, and onMessage() runs. mqtt.loop() must run often: it receives those messages and sends small "I'm still here" packets so the broker doesn't close the connection.
The # in …/# is a wildcard that means "every topic below this one". Topics are arranged like folders, which is why it helps to start them all with the same BASE.
Try this next
- Publish an alarm message on
…/alertwhen it gets warmer than 28 °C, and add a notification in the app. - Add a slider in the app that publishes 0 to 255 and sets the LED brightness.
- Read the same topics on a second ESP32 and show them on its LCD: two devices talking through the cloud.
Troubleshooting
- "failed (-2)": the ESP32 couldn't reach the broker. Some school and office networks block port 1883; try a home network or a phone hotspot.
- The gauges stay empty: the topic in the app must match the sketch exactly, including capital letters.
-
Strange messages appear: someone else uses the same
BASE. Make it longer and more random. - The light value is always 0 or 100: the photoresistor must be on GPIO 34; pins 25 to 27 can't read analog values while Wi-Fi is on.
- It never connects to Wi-Fi: check the network name and password (both are case-sensitive) and make sure the network is 2.4 GHz.
- A printed part is too tight or too loose: printers differ by a few tenths of a millimetre. Sand or file a tight hole, and fix a loose part with a drop of hot glue or a strip of tape.