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
---
TipInteractive Simulator: MQTT Publisher (ESP32 + DHT22)

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:

%%{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

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();
}
TipInteractive Simulator: MQTT Subscriber (Light Control)

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