%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#E67E22'}}}%%
flowchart LR
subgraph "Motion Sensor ESP32"
PIR[PIR Sensor]
ESP1[ESP32 #1]
end
subgraph "MQTT Broker"
B[Broker]
end
subgraph "Light Controller ESP32"
ESP2[ESP32 #2]
LED[LED/Relay]
end
PIR --> ESP1
ESP1 -->|"home/motion"| B
B -->|"home/light/cmd"| ESP2
ESP2 --> LED
style B fill:#E67E22,stroke:#2C3E50,color:#fff
style ESP1 fill:#2C3E50,stroke:#16A085,color:#fff
style ESP2 fill:#2C3E50,stroke:#16A085,color:#fff
1192 MQTT Hands-On Labs and Exercises
1192.1 MQTT Hands-On Labs
This chapter provides hands-on labs, interactive simulators, and exercises for mastering MQTT implementation. Each lab includes complete code, expected output, and learning objectives.
1192.2 Learning Objectives
By the end of these labs, you will be able to:
- Build ESP32 MQTT Publishers: Create temperature/humidity sensors publishing to MQTT
- Create MQTT Dashboards: Build real-time data visualization with Python
- Implement Home Automation: Design multi-device MQTT communication systems
- Debug MQTT Issues: Troubleshoot common connection and message delivery problems
1192.3 Lab 1: ESP32 DHT22 MQTT Publisher with QoS Levels
Objective: Build a temperature/humidity sensor that publishes to MQTT with different QoS levels.
Materials: - ESP32 development board - DHT22 temperature/humidity sensor - 10k ohm pull-up resistor - Breadboard and jumper wires - Wi-Fi connection
Circuit Diagram:
DHT22 ESP32
----- -----
VCC ------> 3.3V
DATA ------> GPIO 4 (with 10k ohm pull-up to 3.3V)
GND ------> GND
Complete Code:
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
// Wi-Fi credentials
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
// MQTT Broker settings
const char* mqtt_server = "test.mosquitto.org";
const int mqtt_port = 1883;
const char* mqtt_client_id = "ESP32_DHT22_001";
// DHT22 sensor
#define DHTPIN 4
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// MQTT topics
const char* topic_temp = "iotclass/lab1/temperature";
const char* topic_humidity = "iotclass/lab1/humidity";
const char* topic_status = "iotclass/lab1/status";
WiFiClient espClient;
PubSubClient client(espClient);
unsigned long lastPublish = 0;
const long publishInterval = 5000; // 5 seconds
void setup_wifi() {
Serial.println("\nConnecting to Wi-Fi...");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWi-Fi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void reconnect_mqtt() {
while (!client.connected()) {
Serial.print("Connecting to MQTT broker...");
if (client.connect(mqtt_client_id)) {
Serial.println(" Connected");
// Publish online status as retained message
client.publish(topic_status, "online", true);
// Subscribe to commands (optional)
client.subscribe("iotclass/lab1/command");
} else {
Serial.print(" Failed, rc=");
Serial.println(client.state());
delay(5000);
}
}
}
void mqtt_callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message received on ");
Serial.print(topic);
Serial.print(": ");
String message = "";
for (int i = 0; i < length; i++) {
message += (char)payload[i];
}
Serial.println(message);
}
void setup() {
Serial.begin(115200);
dht.begin();
setup_wifi();
client.setServer(mqtt_server, mqtt_port);
client.setCallback(mqtt_callback);
}
void loop() {
if (!client.connected()) {
reconnect_mqtt();
}
client.loop();
unsigned long now = millis();
if (now - lastPublish >= publishInterval) {
lastPublish = now;
// Read sensor
float humidity = dht.readHumidity();
float temperature = dht.readTemperature();
if (isnan(humidity) || isnan(temperature)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
// Publish temperature with QoS 0
char tempStr[8];
dtostrf(temperature, 6, 2, tempStr);
bool temp_success = client.publish(topic_temp, tempStr, false);
Serial.print("Temperature: ");
Serial.print(tempStr);
Serial.print("C ");
Serial.println(temp_success ? "OK" : "FAIL");
// Publish humidity
char humStr[8];
dtostrf(humidity, 6, 2, humStr);
bool hum_success = client.publish(topic_humidity, humStr, false);
Serial.print("Humidity: ");
Serial.print(humStr);
Serial.print("% ");
Serial.println(hum_success ? "OK" : "FAIL");
Serial.println("---");
}
}Expected Output (Serial Monitor):
Connecting to Wi-Fi...
Wi-Fi connected
IP address: 192.168.1.100
Connecting to MQTT broker... Connected
Temperature: 22.50C OK
Humidity: 45.30% OK
---
Temperature: 22.48C OK
Humidity: 45.35% OK
---
Try it yourself! See a complete IoT system publishing sensor data to an MQTT broker.
Learning Points: - PubSubClient Library: Arduino MQTT client for ESP32 - client.connect(): Establishes connection to broker with unique client ID - client.publish(): Sends message to topic (returns true/false) - Topic Hierarchy: iotclass/lab1/temperature uses / separators - QoS 0 (At Most Once): Fire-and-forget, fastest but no guarantee
Challenges: 1. Modify to publish only when temperature changes by +/-0.5C (reduce traffic) 2. Add battery voltage monitoring and publish with QoS 2 3. Implement Last Will and Testament to detect unexpected disconnections 4. Add JSON payload with multiple sensor readings
1192.4 Lab 2: Python MQTT Dashboard with Multiple Sensors
Objective: Create a real-time dashboard that subscribes to multiple sensor topics and displays data.
Materials: - Python 3.7+ - paho-mqtt library - (Optional) Running ESP32 from Lab 1
Complete Code:
import paho.mqtt.client as mqtt
from datetime import datetime
import json
# MQTT Settings
BROKER = "test.mosquitto.org"
PORT = 1883
TOPICS = [
("iotclass/lab1/temperature", 0),
("iotclass/lab1/humidity", 0),
("iotclass/lab1/status", 0)
]
# Data storage
sensor_data = {}
message_count = 0
def on_connect(client, userdata, flags, reason_code, properties):
print("=" * 60)
print(" IoT MQTT Dashboard - Real-Time Sensor Data")
print("=" * 60)
print(f"\nConnected with reason code {reason_code}")
# Subscribe to all topics
client.subscribe(TOPICS)
print(f"Subscribed to {len(TOPICS)} topics")
print("-" * 60)
def on_message(client, userdata, msg):
global message_count
message_count += 1
topic = msg.topic
payload = msg.payload.decode()
timestamp = datetime.now().strftime("%H:%M:%S")
# Extract sensor name from topic
parts = topic.split("/")
sensor_name = parts[-1].upper()
# Store data
sensor_data[sensor_name] = {
"value": payload,
"time": timestamp,
"qos": msg.qos
}
# Display update
print(f"\n[{timestamp}] Update from {sensor_name}:")
print(f" Value: {payload}")
# Check for alerts
if sensor_name == "TEMPERATURE":
try:
temp = float(payload)
if temp > 30:
print(" β WARNING: High temperature!")
elif temp < 10:
print(" β WARNING: Low temperature!")
except ValueError:
pass
# Show dashboard summary
print(f"\n--- Dashboard Summary ({message_count} total messages) ---")
for name, data in sensor_data.items():
print(f" {name}: {data['value']} (updated: {data['time']})")
def main():
# Create client (paho-mqtt 2.0+)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="Dashboard_001")
client.on_connect = on_connect
client.on_message = on_message
# Connect
print(f"Connecting to {BROKER}:{PORT}...")
client.connect(BROKER, PORT)
# Run forever
try:
client.loop_forever()
except KeyboardInterrupt:
print("\nDisconnecting...")
client.disconnect()
if __name__ == "__main__":
main()Expected Output:
Connecting to test.mosquitto.org:1883...
============================================================
IoT MQTT Dashboard - Real-Time Sensor Data
============================================================
Connected with reason code Success
Subscribed to 3 topics
------------------------------------------------------------
[14:32:15] Update from TEMPERATURE:
Value: 22.50
--- Dashboard Summary (1 total messages) ---
TEMPERATURE: 22.50 (updated: 14:32:15)
[14:32:15] Update from HUMIDITY:
Value: 45.30
--- Dashboard Summary (2 total messages) ---
TEMPERATURE: 22.50 (updated: 14:32:15)
HUMIDITY: 45.30 (updated: 14:32:15)
1192.5 Lab 3: MQTT Home Automation - Lights and Motion
Objective: Build a complete home automation system with motion detection and automated lighting control.
Materials: - 2x ESP32 boards (one for motion sensor, one for light control) - PIR motion sensor (HC-SR501) - LED (or relay module for real lights) - 220 ohm resistor - Breadboard and wires
Architecture:
Code for Motion Sensor (ESP32 #1):
#include <WiFi.h>
#include <PubSubClient.h>
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
const char* mqtt_server = "test.mosquitto.org";
WiFiClient espClient;
PubSubClient client(espClient);
#define PIR_PIN 13
#define ROOM_ID "living_room"
char motion_topic[50];
char light_command_topic[50];
bool last_motion_state = false;
unsigned long motion_start_time = 0;
const unsigned long AUTO_OFF_DELAY = 30000; // 30 seconds
void setup() {
Serial.begin(115200);
pinMode(PIR_PIN, INPUT);
sprintf(motion_topic, "home/%s/motion", ROOM_ID);
sprintf(light_command_topic, "home/%s/light/command", ROOM_ID);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWi-Fi connected");
client.setServer(mqtt_server, 1883);
reconnect();
}
void reconnect() {
while (!client.connected()) {
Serial.print("Connecting to MQTT...");
if (client.connect("ESP32_MotionSensor")) {
Serial.println(" Connected");
} else {
delay(5000);
}
}
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
bool motion_detected = digitalRead(PIR_PIN) == HIGH;
// Motion started
if (motion_detected && !last_motion_state) {
Serial.println("Motion detected!");
client.publish(motion_topic, "true", true);
// Turn on light
client.publish(light_command_topic, "ON");
motion_start_time = millis();
last_motion_state = true;
}
// Motion stopped
if (!motion_detected && last_motion_state) {
Serial.println("Motion cleared");
client.publish(motion_topic, "false", true);
last_motion_state = false;
}
// Auto turn off light after delay
if (!motion_detected && (millis() - motion_start_time > AUTO_OFF_DELAY)) {
client.publish(light_command_topic, "OFF");
Serial.println("Auto turning off light");
motion_start_time = millis() + 1000000; // Prevent repeated commands
}
delay(200);
}Code for Light Control (ESP32 #2):
#include <WiFi.h>
#include <PubSubClient.h>
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
const char* mqtt_server = "test.mosquitto.org";
WiFiClient espClient;
PubSubClient client(espClient);
#define LED_PIN 2
#define ROOM_ID "living_room"
char light_command_topic[50];
char light_state_topic[50];
void mqtt_callback(char* topic, byte* payload, unsigned int length) {
String message = "";
for (int i = 0; i < length; i++) {
message += (char)payload[i];
}
Serial.print("Received command: ");
Serial.println(message);
if (message == "ON") {
digitalWrite(LED_PIN, HIGH);
client.publish(light_state_topic, "ON", true);
Serial.println("Light turned ON");
} else if (message == "OFF") {
digitalWrite(LED_PIN, LOW);
client.publish(light_state_topic, "OFF", true);
Serial.println("Light turned OFF");
}
}
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
sprintf(light_command_topic, "home/%s/light/command", ROOM_ID);
sprintf(light_state_topic, "home/%s/light/state", ROOM_ID);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWi-Fi connected");
client.setServer(mqtt_server, 1883);
client.setCallback(mqtt_callback);
reconnect();
}
void reconnect() {
while (!client.connected()) {
Serial.print("Connecting to MQTT...");
if (client.connect("ESP32_LightControl")) {
Serial.println(" Connected");
// Subscribe to light commands
client.subscribe(light_command_topic);
Serial.print("Subscribed to: ");
Serial.println(light_command_topic);
// Publish initial state
client.publish(light_state_topic, "OFF", true);
} else {
delay(5000);
}
}
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
}What This Simulates: ESP32 subscribing to MQTT commands and controlling an LED based on received messages.
Learning Points: - mqtt_callback() executes when messages arrive - client.subscribe() registers interest in topics - LED state changes based on βONβ/βOFFβ payload - Retained messages store current state for new subscribers
1192.6 MQTT Broker Interactive Game
Master MQTT concepts through an interactive game where you act as an MQTT broker, routing messages from publishers to the correct subscribers.
1192.7 Practice Exercises
1192.7.1 Exercise 1: Temperature Monitor with Alerts
Objective: Build a complete IoT system with hardware publishing sensor data via MQTT.
Tasks: 1. Set up ESP32 with DHT22 temperature/humidity sensor 2. Publish temperature to home/livingroom/temperature every 30 seconds 3. Create Python subscriber to log all readings to CSV file 4. Add alert when temperature exceeds 30C
1192.7.2 Exercise 2: Secure MQTT with TLS/SSL
Objective: Implement production-grade MQTT security with encryption.
Tasks: 1. Generate self-signed certificate for local Mosquitto broker 2. Configure Mosquitto for TLS on port 8883 3. Create Python MQTT client with TLS 4. Test that unencrypted connections are rejected
1192.7.3 Exercise 3: MQTT-to-Database Pipeline
Objective: Build a complete data ingestion pipeline from MQTT to persistent storage.
Tasks: 1. Set up SQLite database for time-series sensor data 2. Create MQTT subscriber that writes to database 3. Simulate 5 different sensors publishing various metrics 4. Query database for average temperature over last hour
1192.8 Whatβs Next
Continue to MQTT Advanced Topics for production deployment guidance including broker clustering, high availability, and troubleshooting techniques.
1192.9 See Also
- MQTT Introduction: Fundamentals and terminology
- MQTT Security: TLS and authentication
- Simulations Hub: More interactive simulators