MQTT pub-sub messaging for IoT and real-time applications
Scope: MQTT protocol, pub-sub patterns, QoS levels, broker configuration, IoT integration Lines: ~400 Last Updated: 2025-10-27
Activate this skill when:
MQTT (Message Queuing Telemetry Transport): Lightweight pub-sub protocol designed for constrained devices and unreliable networks.
Key characteristics:
Architecture components:
Publisher → Broker → Subscriber(s)
↓
Topics + QoS
↓
Retained Messages
Last Will Testament
Client Broker
| |
|-- CONNECT (clientId, auth) ----->|
| |
|<-- CONNACK (session present) -----|
| |
|-- SUBSCRIBE (topic, QoS) -------->|
| |
|<-- SUBACK (granted QoS) ----------|
| |
|-- PUBLISH (topic, payload, QoS) ->|
| |
|<-- PUBACK (if QoS 1+) ------------|
| |
|-- DISCONNECT ---------------------->|
QoS 0 - At Most Once (Fire and forget):
QoS 1 - At Least Once (Acknowledged delivery):
QoS 2 - Exactly Once (Assured delivery):
# QoS comparison
qos_0 = client.publish("sensor/temp", "22.5", qos=0) # No ACK
qos_1 = client.publish("sensor/temp", "22.5", qos=1) # PUBACK required
qos_2 = client.publish("alarm/fire", "triggered", qos=2) # 4-way handshake
Topics are UTF-8 strings using / as separator:
# Good topic hierarchy
home/bedroom/temperature
home/bedroom/humidity
home/livingroom/temperature
factory/line1/machine5/status
factory/line1/machine5/telemetry
# Topic components
{domain}/{location}/{device}/{measurement}
Single-level wildcard (+):
home/+/temperature matches home/bedroom/temperature and home/kitchen/temperatureMulti-level wildcard (#):
home/# matches home/bedroom/temperature and home/bedroom/humidity# Wildcard subscriptions
client.subscribe("home/+/temperature") # All rooms' temperature
client.subscribe("home/bedroom/#") # All bedroom sensors
client.subscribe("#") # All topics (expensive!)
DO:
domain/location/device/metrichome/living_room/tempDON'T:
$ (reserved for broker)# wildcard (high load)sensor/temp/22.5 (use payload)import paho.mqtt.client as mqtt
import json
import time
class MQTTPublisher:
def __init__(self, broker, port=1883, client_id=None):
self.broker = broker
self.port = port
self.client = mqtt.Client(client_id=client_id)
# Callbacks
self.client.on_connect = self.on_connect
self.client.on_publish = self.on_publish
self.client.on_disconnect = self.on_disconnect
def on_connect(self, client, userdata, flags, rc):
if rc == 0:
print(f"Connected to {self.broker}:{self.port}")
else:
print(f"Connection failed: {rc}")
def on_publish(self, client, userdata, mid):
print(f"Message {mid} published")
def on_disconnect(self, client, userdata, rc):
if rc != 0:
print(f"Unexpected disconnect: {rc}")
def connect(self):
self.client.connect(self.broker, self.port, keepalive=60)
self.client.loop_start()
def publish(self, topic, payload, qos=0, retain=False):
"""Publish message to topic"""
if isinstance(payload, dict):
payload = json.dumps(payload)
result = self.client.publish(topic, payload, qos=qos, retain=retain)
return result.mid
def disconnect(self):
self.client.loop_stop()
self.client.disconnect()
# Usage
publisher = MQTTPublisher("mqtt.example.com")
publisher.connect()
# Publish sensor data
publisher.publish("home/bedroom/temperature", "22.5", qos=1)
publisher.publish("home/bedroom/humidity", "65", qos=1)
# Publish JSON
publisher.publish("sensor/data", {
"device_id": "sensor_01",
"temperature": 22.5,
"humidity": 65,
"timestamp": time.time()
}, qos=1)
time.sleep(2)
publisher.disconnect()
import paho.mqtt.client as mqtt
import json
class MQTTSubscriber:
def __init__(self, broker, port=1883, client_id=None):
self.broker = broker
self.port = port
self.client = mqtt.Client(client_id=client_id)
# Callbacks
self.client.on_connect = self.on_connect
self.client.on_message = self.on_message
self.client.on_subscribe = self.on_subscribe
self.client.on_disconnect = self.on_disconnect
# Message handlers
self.handlers = {}
def on_connect(self, client, userdata, flags, rc):
if rc == 0:
print(f"Connected to {self.broker}:{self.port}")
# Resubscribe on reconnect
for topic, qos in self.handlers.keys():
client.subscribe(topic, qos)
else:
print(f"Connection failed: {rc}")
def on_message(self, client, userdata, msg):
topic = msg.topic
payload = msg.payload.decode()
qos = msg.qos
print(f"Received: {topic} (QoS {qos}): {payload}")
# Call registered handler
for (handler_topic, handler_qos), handler_func in self.handlers.items():
if self.topic_matches(topic, handler_topic):
handler_func(topic, payload, qos)
def on_subscribe(self, client, userdata, mid, granted_qos):
print(f"Subscribed (QoS {granted_qos})")
def on_disconnect(self, client, userdata, rc):
if rc != 0:
print(f"Unexpected disconnect: {rc}")
def topic_matches(self, topic, pattern):
"""Check if topic matches pattern with wildcards"""
# Simplified matching (paho does this internally)
return mqtt.topic_matches_sub(pattern, topic)
def connect(self):
self.client.connect(self.broker, self.port, keepalive=60)
self.client.loop_start()
def subscribe(self, topic, qos=0, handler=None):
"""Subscribe to topic with optional handler"""
self.client.subscribe(topic, qos)
if handler:
self.handlers[(topic, qos)] = handler
def disconnect(self):
self.client.loop_stop()
self.client.disconnect()
# Usage
subscriber = MQTTSubscriber("mqtt.example.com")
# Register handler
def handle_temperature(topic, payload, qos):
temp = float(payload)
if temp > 25:
print(f"High temperature alert: {temp}°C")
subscriber.subscribe("home/+/temperature", qos=1, handler=handle_temperature)
subscriber.connect()
# Keep running
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
subscriber.disconnect()
Retained messages are stored by the broker and delivered to new subscribers immediately.
# Publish retained message (e.g., device status)
client.publish("device/status", "online", qos=1, retain=True)
# New subscribers immediately receive last retained message
# Use case: Device status, configuration, last known value
Use cases:
Clear retained message:
client.publish("device/status", "", qos=1, retain=True) # Empty payload clears
LWT is a message automatically sent by broker if client disconnects unexpectedly.
client = mqtt.Client()
# Set last will (sent if client disconnects without DISCONNECT)
client.will_set(
topic="device/status",
payload="offline",
qos=1,
retain=True
)
client.connect("mqtt.example.com")
# If client crashes or loses connection, broker publishes "offline"
Use cases:
Clean Session = True (default):
Clean Session = False (persistent):
# Persistent session
client = mqtt.Client(client_id="sensor_01", clean_session=False)
client.connect("mqtt.example.com")
# Subscribe with QoS 1
client.subscribe("commands/sensor_01", qos=1)
# Even if disconnected, messages are queued and delivered on reconnect
import ssl
client = mqtt.Client()
# TLS with server certificate verification
client.tls_set(
ca_certs="/path/to/ca.crt",
certfile="/path/to/client.crt", # Optional client cert
keyfile="/path/to/client.key", # Optional client key
tls_version=ssl.PROTOCOL_TLSv1_2
)
client.connect("mqtt.example.com", port=8883) # TLS port
client = mqtt.Client()
client.username_pw_set(username="sensor_01", password="secret")
client.connect("mqtt.example.com")
# AWS IoT Core uses mutual TLS (certificate-based)
client = mqtt.Client()
client.tls_set(
ca_certs="AmazonRootCA1.pem",
certfile="device.pem.crt",
keyfile="device.pem.key",
tls_version=ssl.PROTOCOL_TLSv1_2
)
client.connect("xxxxxx-ats.iot.us-east-1.amazonaws.com", port=8883)
Lightweight broker for development and small deployments.
# Install
sudo apt-get install mosquitto mosquitto-clients
# Start broker
mosquitto -c /etc/mosquitto/mosquitto.conf
# Test with CLI
mosquitto_sub -h localhost -t "test/topic"
mosquitto_pub -h localhost -t "test/topic" -m "Hello"
Configuration (/etc/mosquitto/mosquitto.conf):
# Basic config
listener 1883
allow_anonymous true
# TLS config
listener 8883
cafile /path/to/ca.crt
certfile /path/to/server.crt
keyfile /path/to/server.key
require_certificate false
# Authentication
password_file /etc/mosquitto/passwd
High-performance broker for large-scale IoT deployments.
# Docker deployment
docker run -d --name emqx \
-p 1883:1883 \
-p 8083:8083 \
-p 8084:8084 \
-p 8883:8883 \
-p 18083:18083 \
emqx/emqx:latest
# Dashboard: http://localhost:18083 (admin/public)
Features:
Production-grade broker with enterprise features.
Features:
MQTT is pub-sub, but request-response can be implemented:
# Client sends request with reply topic
request_payload = json.dumps({
"command": "get_status",
"reply_to": f"responses/{client_id}"
})
client.subscribe(f"responses/{client_id}", qos=1)
client.publish("commands/device_01", request_payload, qos=1)
# Device responds to reply_to topic
def on_message(client, userdata, msg):
request = json.loads(msg.payload)
reply_topic = request.get("reply_to")
response = {"status": "online", "uptime": 3600}
client.publish(reply_topic, json.dumps(response), qos=1)
# Device sends periodic telemetry
def send_telemetry():
while True:
telemetry = {
"device_id": "sensor_01",
"temperature": read_temperature(),
"humidity": read_humidity(),
"battery": get_battery_level(),
"timestamp": time.time()
}
client.publish(
f"devices/{device_id}/telemetry",
json.dumps(telemetry),
qos=1
)
time.sleep(60) # Every minute
# Backend sends commands to devices
client.publish(
"commands/sensor_01/update_config",
json.dumps({"interval": 30}),
qos=1
)
# Device subscribes to commands
client.subscribe("commands/sensor_01/#", qos=1)
def on_message(client, userdata, msg):
command = msg.topic.split("/")[-1]
payload = json.loads(msg.payload)
if command == "update_config":
update_config(payload)
elif command == "reboot":
reboot_device()
❌ Subscribing to # wildcard: Receives all messages (broker load) ✅ Use specific wildcards: home/+/temperature
❌ Using QoS 2 everywhere: Highest overhead (4-way handshake) ✅ Use QoS 0 for non-critical data, QoS 1 for most cases, QoS 2 only when required
❌ Large payloads (>1 MB): MQTT is for small messages ✅ Keep payloads small (<10 KB), use external storage for large data
❌ Embedding data in topics: sensor/temp/22.5 ✅ Use topics for routing, payloads for data: sensor/temp with payload 22.5
❌ No authentication: Open broker accessible to anyone ✅ Use TLS + username/password or client certificates
❌ Ignoring connection failures: No reconnection logic ✅ Implement exponential backoff reconnection
❌ Not using retained messages for status: Subscribers miss status ✅ Use retained messages for device status, config
This skill includes comprehensive Level 3 resources for deep MQTT implementation knowledge and practical tools.
Resources include:
Location: skills/protocols/mqtt-messaging/resources/REFERENCE.md
Comprehensive technical reference (3,200+ lines) covering:
Core Topics:
Key Sections:
Format: Markdown with extensive code examples in Python, Node.js, and shell scripts
Three production-ready executable scripts in resources/scripts/:
Purpose: Validate MQTT broker and topic configurations
Features:
Usage:
# Validate Mosquitto config
./validate_mqtt_config.py --config /etc/mosquitto/mosquitto.conf
# JSON output
./validate_mqtt_config.py --config /etc/mosquitto/mosquitto.conf --json
# Check topics
./validate_mqtt_config.py --check-topics --topics-file topics.txt
# Validate ACL
./validate_mqtt_config.py --config mosquitto.conf --check-acl
Checks:
Purpose: Test MQTT QoS levels and message delivery
Features:
Usage:
# Test all QoS levels
./test_mqtt_qos.py --broker mqtt.example.com --test-all
# Test QoS 1
./test_mqtt_qos.py --broker mqtt.example.com --qos 1 --count 100
# Test retained messages
./test_mqtt_qos.py --broker mqtt.example.com --test-retained
# Test LWT
./test_mqtt_qos.py --broker mqtt.example.com --test-lwt
# JSON output
./test_mqtt_qos.py --broker mqtt.example.com --test-all --json
Metrics: Delivery rate, message loss, duplicates, latency (min, avg, p95, max)
Purpose: Benchmark MQTT broker performance and scalability
Features:
Usage:
# Benchmark with 1000 connections
./benchmark_mqtt_broker.py --broker mqtt.example.com --connections 1000 --duration 60
# Test throughput
./benchmark_mqtt_broker.py --broker mqtt.example.com --test throughput --messages 10000
# Test latency
./benchmark_mqtt_broker.py --broker mqtt.example.com --test latency --count 100
# Ramp-up test
./benchmark_mqtt_broker.py --broker mqtt.example.com --connections 5000 --ramp-up 120 --duration 300
# JSON output
./benchmark_mqtt_broker.py --broker mqtt.example.com --connections 1000 --json
Metrics: Connections/sec, messages/sec, latency (p50, p95, p99), memory usage
Seven production-ready examples in resources/examples/:
Complete Python publisher implementation:
Complete Python subscriber implementation:
Production Mosquitto configuration:
EMQX deployment with Docker:
AWS IoT Core integration:
Node.js client implementation:
TLS certificate generation script:
1. Validate broker config:
cd skills/protocols/mqtt-messaging/resources/scripts
./validate_mqtt_config.py --config ../examples/mosquitto/mosquitto.conf --json
2. Test QoS levels:
./test_mqtt_qos.py --broker localhost --test-all
3. Run examples:
cd ../examples
# Start Mosquitto
mosquitto -c mosquitto/mosquitto.conf
# Run publisher (in another terminal)
python python/publisher.py --broker localhost --topic test/topic --message "Hello MQTT"
# Run subscriber (in another terminal)
python python/subscriber.py --broker localhost --topic test/#
4. Deploy with Docker:
cd emqx
docker-compose up -d
# Dashboard: http://localhost:18083 (admin/public)
5. Benchmark broker:
cd ../scripts
./benchmark_mqtt_broker.py --broker localhost --connections 100 --duration 30
skills/protocols/mqtt-messaging/
├── mqtt-messaging.md (this file)
└── resources/
├── REFERENCE.md (3,200+ lines)
├── scripts/
│ ├── validate_mqtt_config.py (550 lines) - Config validation
│ ├── test_mqtt_qos.py (600 lines) - QoS testing
│ └── benchmark_mqtt_broker.py (650 lines) - Broker benchmarking
└── examples/
├── python/
│ ├── publisher.py - Python publisher
│ └── subscriber.py - Python subscriber
├── mosquitto/
│ ├── mosquitto.conf - Mosquitto config
│ └── acl.conf - ACL rules
├── emqx/
│ └── docker-compose.yml - EMQX deployment
├── aws_iot/
│ └── iot_device.py - AWS IoT Core client
├── node/
│ └── mqtt_client.js - Node.js client
└── tls/
└── generate_certs.sh - TLS certificate generation
| Category | Item | Lines | Description | |----------|------|-------|-------------| | Reference | REFERENCE.md | 3,200+ | Complete technical reference | | Scripts | validate_mqtt_config.py | 550 | Config validation tool | | | test_mqtt_qos.py | 600 | QoS testing tool | | | benchmark_mqtt_broker.py | 650 | Broker benchmarking tool | | Examples | python/publisher.py | 180 | Python publisher | | | python/subscriber.py | 200 | Python subscriber | | | mosquitto.conf | 120 | Mosquitto configuration | | | docker-compose.yml | 100 | EMQX deployment | | | iot_device.py | 250 | AWS IoT Core client | | | mqtt_client.js | 150 | Node.js client | | | generate_certs.sh | 100 | TLS cert generation |
Total: 6,100+ lines of production-ready resources
protocols-grpc-implementation.md - gRPC for microservice communicationrealtime-websocket-implementation.md - WebSocket for bidirectional messagingpubsub-patterns.md - Pub-sub architecture patternsiot-device-management.md - IoT device lifecycle managementmessage-queue-patterns.md - Message queuing architecturesLast Updated: 2025-10-27 Format Version: 1.0 (Atomic)