1187  MQTT Practice and Exercises

1187.1 Learning Objectives

By the end of this chapter, you will be able to:

  • Apply MQTT Concepts: Complete hands-on exercises building real MQTT systems
  • Avoid Common Pitfalls: Recognize and fix frequent MQTT implementation mistakes
  • Debug MQTT Issues: Troubleshoot connection, subscription, and delivery problems
  • Access Resources: Find libraries, tools, and documentation for continued learning

1187.2 Prerequisites

Before diving into this chapter, you should be familiar with:

1187.3 Practice Exercises

1187.3.1 Exercise 1: Basic MQTT Pub/Sub

Objective: Set up a basic MQTT publisher and subscriber.

Tasks:

  1. Install Mosquitto broker or use test.mosquitto.org
  2. Create a publisher that sends temperature readings to home/bedroom/temperature every 5 seconds
  3. Create a subscriber that listens and prints received messages
  4. Run both simultaneously and observe the message flow

Expected Outcome:

  • Subscriber receives temperature readings in real-time
  • You understand the decoupling between publisher and subscriber

Sample Code:

# Requires paho-mqtt 2.0+
import paho.mqtt.client as mqtt
import time

# Publisher
def publish_temperature():
    client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
    client.connect("test.mosquitto.org", 1883)

    for temp in [22.0, 22.5, 23.0, 23.5]:
        client.publish("home/bedroom/temperature", str(temp))
        print(f"Published: {temp}")
        time.sleep(5)

    client.disconnect()

# Subscriber
def on_message(client, userdata, message):
    print(f"Received: {message.payload.decode()} from {message.topic}")

def subscribe_temperature():
    client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
    client.on_message = on_message
    client.connect("test.mosquitto.org", 1883)
    client.subscribe("home/bedroom/temperature")
    client.loop_forever()

Hints:

  • Use client.loop_forever() in subscriber to keep listening
  • Start subscriber before publisher to ensure it doesn’t miss messages

1187.3.2 Exercise 2: Topic Wildcards

Objective: Master topic design and wildcard subscriptions.

Tasks:

  1. Design a topic hierarchy for a smart home with:
    • 3 rooms (bedroom, kitchen, living_room)
    • 3 sensor types per room (temperature, humidity, motion)
  2. Create publishers for all 9 sensors
  3. Create subscribers that:
    • Subscribe to all bedroom sensors (home/bedroom/#)
    • Subscribe to all temperatures (home/+/temperature)
    • Subscribe to everything (home/#)
  4. Verify wildcards match correctly

Expected Outcome:

  • home/bedroom/# receives bedroom temperature, humidity, motion
  • home/+/temperature receives temperature from all 3 rooms
  • home/# receives all 9 sensor streams

Topic Structure:

home/
  bedroom/
    temperature
    humidity
    motion
  kitchen/
    temperature
    humidity
    motion
  living_room/
    temperature
    humidity
    motion

1187.3.3 Exercise 3: Retained Messages and LWT

Objective: Implement device status monitoring using retained messages and Last Will Testament.

Tasks:

  1. Create a simulated sensor that:
    • Publishes “online” (retained) on startup to device/sensor1/status
    • Sets LWT to publish “offline” (retained) to same topic
    • Publishes temperature readings every 10 seconds
  2. Create a monitoring dashboard subscriber
  3. Test LWT by forcefully killing the sensor process (Ctrl+C or kill -9)
  4. Verify dashboard receives “offline” status automatically

Expected Outcome:

  • Dashboard shows “online” immediately upon subscribing
  • When sensor crashes, dashboard receives “offline” automatically

Sample LWT Configuration:

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
client.will_set("device/sensor1/status", payload="offline", qos=1, retain=True)
client.connect("broker.local", 1883)
client.publish("device/sensor1/status", "online", qos=1, retain=True)

Testing Checklist:

1187.3.4 Exercise 4: QoS Comparison

Objective: Compare QoS 0, 1, and 2 behavior under different conditions.

Tasks:

  1. Set up local Mosquitto broker
  2. Create a publisher that sends 100 messages with each QoS level
  3. Use Wireshark or tcpdump to capture MQTT traffic
  4. Analyze packet captures:
    • QoS 0: Single PUBLISH packet
    • QoS 1: PUBLISH + PUBACK
    • QoS 2: PUBLISH + PUBREC + PUBREL + PUBCOMP
  5. (Optional) Simulate 20% packet loss and observe retransmissions

Measurement Template:

QoS Level Packets Sent Packets Received ACKs Latency (ms)
0 100 ? 0 ?
1 100 ? ? ?
2 100 ? ? ?

Hints:

  • Use mosquitto -v to run broker in verbose mode
  • Filter Wireshark by mqtt protocol
  • Simulate packet loss: tc qdisc add dev lo root netem loss 20%

1187.4 Common Pitfalls

WarningPitfall: Wrong MQTT Wildcard Usage

The Mistake: Using wildcards incorrectly leads to missing or unintended messages.

Wrong approach:

# INVALID: # wildcard not at end
client.subscribe("sensors/#/temperature")

# INVALID: Mixing wildcards incorrectly
client.subscribe("sensors/+/#/data")

Correct approach:

# Correct: Use + for single-level wildcard
client.subscribe("sensors/+/temperature")

# Correct: Use # at the end only
client.subscribe("sensors/room1/#")

# Correct: Multiple + wildcards
client.subscribe("sensors/+/+/temperature")
WarningPitfall: Non-Unique Client ID

The Mistake: Multiple clients using the same client_id causes disconnection loops.

Why It Happens: MQTT brokers only allow one connection per client_id. When a second client connects with the same ID, the first is disconnected.

Wrong approach:

# All devices using same client ID - CAUSES DISCONNECTIONS
client.connect(broker, client_id="sensor")

Correct approach:

# Unique client ID based on device identity
device_id = get_device_serial()
client.connect(broker, client_id=f"sensor-{device_id}")

# Or use MAC address
mac = get_mac_address()
client.connect(broker, client_id=f"device-{mac}")
WarningPitfall: Using QoS 2 Everywhere

The Mistake: Setting QoS 2 for all messages thinking “higher is better.”

The Impact:

  • 4x message overhead
  • 3-4x battery drain
  • 2-3 RTT additional latency
  • Unnecessary broker load

The Fix:

# Temperature reading every 30 seconds - QoS 0 is fine
client.publish("sensors/temp", "24.5", qos=0)

# Motion alert - QoS 1 ensures delivery
client.publish("alerts/motion", "detected", qos=1)

# Door unlock command - QoS 2 prevents duplicates
client.publish("actuators/door/unlock", "1", qos=2)
WarningPitfall: Retained Message Accumulation

The Mistake: Publishing large payloads with retain=true causes broker memory exhaustion.

The Problem: Retained messages persist indefinitely until cleared. A 1MB firmware binary retained on 100 topics = 100MB broker memory.

Wrong approach:

# WRONG: Large retained payload
firmware = open("firmware.bin", "rb").read()  # 2MB
client.publish("device/firmware", firmware, retain=True)  # Memory leak!

Correct approach:

# CORRECT: Retain only a reference
metadata = json.dumps({
    "version": "2.1.0",
    "url": "https://firmware.example.com/v2.1/sensor.bin",
    "sha256": "a1b2c3d4..."
})
client.publish("device/firmware/available", metadata, retain=True)  # ~200 bytes
WarningPitfall: Not Handling Disconnections

The Mistake: Assuming connections stay up forever without implementing reconnection logic.

The Fix: Implement exponential backoff reconnection:

def on_disconnect(client, userdata, rc):
    if rc != 0:  # Unexpected disconnect
        backoff = 1
        max_backoff = 300

        while not connected:
            try:
                client.reconnect()
                break
            except:
                time.sleep(backoff)
                backoff = min(backoff * 2, max_backoff)
WarningPitfall: Ignoring Clean Session

The Mistake: Using clean_session=True causes missed messages after reconnection.

The Impact: When device reconnects after network loss:

  • Must re-subscribe to all topics
  • Messages published during disconnection are lost

The Fix:

# BAD: Clean session loses state
client.connect(broker, clean_session=True)

# GOOD: Persistent session retains subscriptions
client.connect(broker, client_id="device-ABC123", clean_session=False)

1187.5 Knowledge Check

What is the main difference between MQTT and HTTP communication?

Options:

    1. MQTT uses peer-to-peer while HTTP uses client-server
    1. MQTT uses publish-subscribe with a broker while HTTP uses request-response
    1. MQTT requires persistent connections while HTTP cannot use them
    1. MQTT only works over UDP while HTTP requires TCP

Correct: B)

MQTT decouples publishers and subscribers via a broker and topic-based routing. HTTP is direct client-to-server request/response.

A door lock command must be delivered exactly once. Which QoS level?

Options:

    1. QoS 0
    1. QoS 1
    1. QoS 2
    1. QoS 3

Correct: C) QoS 2

QoS 2 provides exactly-once delivery via 4-way handshake. QoS 0 has no guarantee, QoS 1 may duplicate. QoS 3 doesn’t exist.

Which pattern receives temperature from ALL rooms on floor 3?

Options:

    1. building/3/+/temperature
    1. building/3/#
    1. building/+/temperature
    1. building/3/*/temperature

Correct: A)

+ matches exactly one level (the room). # would match all sensor types. * is not a valid MQTT wildcard.

1187.6 Resources

1187.6.1 Client Libraries

Language Library Link
Python paho-mqtt pypi.org/project/paho-mqtt
JavaScript mqtt.js github.com/mqttjs/MQTT.js
C/C++ Eclipse Paho eclipse.org/paho
Arduino/ESP PubSubClient pubsubclient.knolleary.net
Go paho.mqtt.golang github.com/eclipse/paho.mqtt.golang

1187.6.2 Brokers

Broker Type Best For
Mosquitto Open source Learning, small deployments
EMQX Open source Large scale, clustering
HiveMQ Commercial Enterprise, managed cloud
AWS IoT Core Cloud AWS integration
Azure IoT Hub Cloud Azure integration

1187.6.3 Testing Tools

Tool Purpose Link
MQTT Explorer GUI client mqtt-explorer.com
mosquitto_pub/sub CLI tools mosquitto.org/man
test.mosquitto.org Public broker test.mosquitto.org
MQTTX Cross-platform GUI mqttx.app

1187.6.4 Standards and Documentation

1187.7 Summary

Practice exercises covered:

  1. Basic pub/sub - Understanding message flow
  2. Wildcards - Efficient topic subscriptions
  3. Retained messages and LWT - Device status monitoring
  4. QoS comparison - Reliability vs overhead trade-offs

Common pitfalls to avoid:

  • Wrong wildcard syntax
  • Non-unique client IDs
  • Overusing QoS 2
  • Retained message accumulation
  • Missing reconnection logic
  • Ignoring clean session implications

1187.8 What’s Next

You’ve completed the MQTT Fundamentals series! Continue your learning: