MQTT Protocol: A Practical Guide with Code Examples, Libraries, and Real-World Use Cases
MQTT (Message Queuing Telemetry Transport) is a lightweight publish-subscribe messaging protocol designed for resource-constrained devices and unreliable networks. Created in 1999 for monitoring oil pipelines, it has become the de facto standard for IoT communication.
This guide provides practical knowledge: how MQTT works, when to use it, code examples in multiple languages, and production-ready use cases.
What is MQTT?
MQTT is a client-server protocol where:
- Publishers send messages to topics
- Subscribers receive messages from topics
- A broker manages routing and delivery
Unlike HTTP request-response, MQTT uses publish-subscribe patterns, decoupling senders from receivers.
Key Features:
✅ Lightweight: Minimal packet overhead (2-byte fixed header)
✅ Low bandwidth: Ideal for cellular/satellite networks
✅ Quality of Service (QoS): Three levels of delivery guarantee
✅ Persistent sessions: Clients can resume after disconnect
✅ Last Will and Testament: Automatic notification when clients disconnect unexpectedly
✅ Retained messages: New subscribers immediately get the last message
MQTT Architecture
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Publisher │ │ Broker │ │ Subscriber │
│ (Client) │────────▶│ (Server) │────────▶│ (Client) │
└─────────────┘ └─────────────┘ └─────────────┘
│
│ Topic: "home/temperature"
▼
┌─────────────┐
│ Subscriber │
│ (Client) │
└─────────────┘
Components:
- MQTT Broker: Central server (Mosquitto, HiveMQ, EMQX, AWS IoT Core)
- MQTT Clients: Publishers and subscribers (sensors, gateways, applications)
- Topics: Hierarchical strings like
home/bedroom/temperature - Messages: Payload (binary or text) + metadata (QoS, retain flag)
Topics and Wildcards
Topics use / as a separator:
home/livingroom/temperature
home/livingroom/humidity
home/bedroom/temperature
factory/line1/robot/status
factory/line1/robot/error
Wildcard Subscriptions:
+(single-level): Matches one levelhome/+/temperature→ matcheshome/livingroom/temperature,home/bedroom/temperature#(multi-level): Matches all remaining levelsfactory/line1/#→ matchesfactory/line1/robot/status,factory/line1/robot/error,factory/line1/sensor/data
Quality of Service (QoS) Levels
| QoS | Name | Delivery Guarantee | Use Case |
|---|---|---|---|
| 0 | At most once | Fire and forget, no acknowledgment | Non-critical sensor data, high-frequency telemetry |
| 1 | At least once | Acknowledged, possible duplicates | Most IoT applications, smart home |
| 2 | Exactly once | Guaranteed once, no duplicates | Billing, critical commands, industrial control |
Example Scenarios:
- QoS 0: Room temperature updates every 10 seconds (losing one reading is acceptable)
- QoS 1: Door lock status (important, but handling duplicates is easy)
- QoS 2: Payment transactions or industrial valve control (must be exactly once)
Retained Messages and Last Will
Retained Messages:
When you publish with the retain flag, the broker stores the message. New subscribers immediately get it.
Use case: Device status
Topic: device/sensor123/status
Payload: "online"
Retain: true
New subscribers instantly know the device is online without waiting for the next update.
Last Will and Testament (LWT):
Set a message that the broker publishes if your client disconnects unexpectedly.
Use case: Offline detection
LWT Topic: device/sensor123/status
LWT Payload: "offline"
If the device crashes or loses network, the broker publishes "offline" automatically.
MQTT Brokers
Popular Brokers:
| Broker | Type | Best For |
|---|---|---|
| Mosquitto | Open-source | Development, small deployments, Raspberry Pi |
| EMQX | Open-source, enterprise | High scalability, millions of connections |
| HiveMQ | Commercial | Enterprise IoT, compliance, support |
| AWS IoT Core | Cloud | AWS ecosystem, serverless integration |
| Azure IoT Hub | Cloud | Azure ecosystem, device twins |
| Google Cloud IoT Core | Cloud (deprecated) | Legacy projects |
Quick Mosquitto Setup (Ubuntu/Debian):
sudo apt update
sudo apt install mosquitto mosquitto-clients
# Start broker
sudo systemctl start mosquitto
sudo systemctl enable mosquitto
# Test with command-line tools
mosquitto_sub -h localhost -t "test/topic" &
mosquitto_pub -h localhost -t "test/topic" -m "Hello MQTT"
MQTT Libraries by Language
C / C++
1. Eclipse Paho MQTT C
Standard library for embedded Linux and microcontrollers.
#include "MQTTClient.h"
#define ADDRESS "tcp://localhost:1883"
#define CLIENTID "ExampleClient"
#define TOPIC "home/temperature"
#define QOS 1
int main() {
MQTTClient client;
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
MQTTClient_message pubmsg = MQTTClient_message_initializer;
MQTTClient_deliveryToken token;
MQTTClient_create(&client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL);
conn_opts.keepAliveInterval = 20;
conn_opts.cleansession = 1;
MQTTClient_connect(client, &conn_opts);
pubmsg.payload = "22.5";
pubmsg.payloadlen = 4;
pubmsg.qos = QOS;
pubmsg.retained = 0;
MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token);
MQTTClient_waitForCompletion(client, token, 10000);
MQTTClient_disconnect(client, 10000);
MQTTClient_destroy(&client);
return 0;
}
Install: sudo apt install libpaho-mqtt-dev
Link: gcc mqtt_example.c -o mqtt_example -lpaho-mqtt3c
2. Mosquitto C Library
#include <mosquitto.h>
void on_connect(struct mosquitto *mosq, void *obj, int rc) {
printf("Connected with code %d\n", rc);
mosquitto_subscribe(mosq, NULL, "home/#", 0);
}
void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg) {
printf("Topic: %s, Payload: %s\n", msg->topic, (char *)msg->payload);
}
int main() {
mosquitto_lib_init();
struct mosquitto *mosq = mosquitto_new("subscriber", true, NULL);
mosquitto_connect_callback_set(mosq, on_connect);
mosquitto_message_callback_set(mosq, on_message);
mosquitto_connect(mosq, "localhost", 1883, 60);
mosquitto_loop_forever(mosq, -1, 1);
mosquitto_destroy(mosq);
mosquitto_lib_cleanup();
return 0;
}
Install: sudo apt install libmosquitto-dev
Link: gcc mqtt_sub.c -o mqtt_sub -lmosquitto
Python
Paho MQTT Python
import paho.mqtt.client as mqtt
import time
# Callbacks
def on_connect(client, userdata, flags, rc):
print(f"Connected with result code {rc}")
client.subscribe("home/+/temperature")
def on_message(client, userdata, msg):
print(f"Topic: {msg.topic}, Payload: {msg.payload.decode()}")
# Publisher
def publish_example():
client = mqtt.Client()
client.connect("localhost", 1883, 60)
client.publish("home/livingroom/temperature", "23.5", qos=1, retain=True)
client.disconnect()
# Subscriber
def subscribe_example():
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("localhost", 1883, 60)
client.loop_forever()
if __name__ == "__main__":
# publish_example()
subscribe_example()
Install: pip install paho-mqtt
JavaScript / Node.js
MQTT.js
const mqtt = require('mqtt');
// Publisher
const client = mqtt.connect('mqtt://localhost:1883');
client.on('connect', () => {
console.log('Connected');
// Publish
client.publish('home/livingroom/temperature', '24.1', { qos: 1, retain: true });
// Subscribe
client.subscribe('home/+/temperature', (err) => {
if (!err) console.log('Subscribed');
});
});
client.on('message', (topic, message) => {
console.log(`Topic: ${topic}, Message: ${message.toString()}`);
});
Install: npm install mqtt
Go
Paho MQTT Go
package main
import (
"fmt"
"time"
mqtt "github.com/eclipse/paho.mqtt.golang"
)
func main() {
opts := mqtt.NewClientOptions().AddBroker("tcp://localhost:1883")
opts.SetClientID("go-client")
client := mqtt.NewClient(opts)
if token := client.Connect(); token.Wait() && token.Error() != nil {
panic(token.Error())
}
// Subscribe
client.Subscribe("home/+/temperature", 0, func(client mqtt.Client, msg mqtt.Message) {
fmt.Printf("Topic: %s, Payload: %s\n", msg.Topic(), msg.Payload())
})
// Publish
token := client.Publish("home/bedroom/temperature", 0, true, "21.3")
token.Wait()
time.Sleep(10 * time.Second)
client.Disconnect(250)
}
Install: go get github.com/eclipse/paho.mqtt.golang
Rust
rumqtt
use rumqttc::{Client, MqttOptions, QoS};
use std::time::Duration;
fn main() {
let mut mqttoptions = MqttOptions::new("rust-client", "localhost", 1883);
mqttoptions.set_keep_alive(Duration::from_secs(5));
let (mut client, mut connection) = Client::new(mqttoptions, 10);
client.subscribe("home/+/temperature", QoS::AtMostOnce).unwrap();
client.publish("home/office/temperature", QoS::AtLeastOnce, true, "22.0").unwrap();
for notification in connection.iter() {
println!("Notification = {:?}", notification);
}
}
Install: Add to Cargo.toml: rumqttc = "0.23"
Real-World Use Cases
1. Smart Home Automation
Scenario: Control lights, thermostats, and sensors.
Architecture:
- Home Assistant publishes to
home/livingroom/light/set - ESP32 devices subscribe and toggle GPIO
- Sensors publish to
home/bedroom/temperature
Example:
# Turn on living room light
mosquitto_pub -t "home/livingroom/light/set" -m "ON"
# Subscribe to all temperature sensors
mosquitto_sub -t "home/+/temperature"
2. Industrial IoT Monitoring
Scenario: Monitor factory machines and send alerts.
Topics:
factory/line1/machine1/temperature
factory/line1/machine1/vibration
factory/line1/machine1/status
factory/line1/machine1/errors
QoS Strategy:
- Temperature/vibration: QoS 0 (high frequency, non-critical)
- Status changes: QoS 1 (important)
- Emergency stop commands: QoS 2 (critical)
3. Fleet Tracking
Scenario: Track delivery vehicles with GPS.
MQTT Flow:
- Vehicle publishes GPS coordinates every 30 seconds to
fleet/vehicle123/gps - Backend subscribes to
fleet/+/gps - Dashboard updates in real-time
- LWT topic
fleet/vehicle123/statusset to “offline” for connectivity loss
Benefits over HTTP polling:
- Lower bandwidth (persistent connection)
- Real-time updates (no polling delay)
- Automatic offline detection (LWT)
4. Agricultural Monitoring
Scenario: Monitor soil moisture, temperature, and irrigation.
Devices:
- Soil sensors (ESP32, battery-powered)
- Weather station (Raspberry Pi)
- Irrigation controller (PLC)
Topics:
farm/field1/soil/moisture → QoS 0, retain=true
farm/field1/irrigation/valve → QoS 2, retain=true
farm/weather/temperature → QoS 0
Power Optimization:
- ESP32 deep sleep, wake every 15 minutes
- Publish with QoS 0 (saves battery)
- Persistent session with clean_session=0
5. Home Energy Monitoring
Scenario: Monitor electricity usage and solar production.
Architecture:
- Smart meter publishes to
home/power/consumption - Solar inverter publishes to
home/solar/production - Python script calculates net usage and stores in InfluxDB
- Grafana dashboard visualizes data
Python Example:
import paho.mqtt.client as mqtt
from influxdb import InfluxDBClient
influx = InfluxDBClient(host='localhost', port=8086, database='energy')
def on_message(client, userdata, msg):
value = float(msg.payload.decode())
json_body = [{
"measurement": msg.topic.replace("/", "_"),
"fields": {"value": value}
}]
influx.write_points(json_body)
client = mqtt.Client()
client.on_message = on_message
client.connect("localhost", 1883)
client.subscribe("home/#")
client.loop_forever()
MQTT vs Other Protocols
| Protocol | Pattern | Overhead | Use Case |
|---|---|---|---|
| MQTT | Pub/Sub | Very low | IoT, real-time telemetry |
| HTTP/REST | Request/Response | High | Web APIs, CRUD operations |
| WebSocket | Bidirectional | Medium | Web dashboards, chat |
| CoAP | Request/Response | Very low | Constrained devices (UDP) |
| AMQP | Pub/Sub + Queue | High | Enterprise messaging |
When to choose MQTT:
- Unreliable networks (cellular, satellite)
- Battery-powered devices
- Many-to-many communication
- Real-time updates needed
When NOT to use MQTT:
- Simple HTTP GET/POST is sufficient
- No broker infrastructure available
- Strict regulatory compliance for message routing
Security Best Practices
1. Use TLS/SSL (MQTTS)
client = mqtt.Client()
client.tls_set(ca_certs="/path/to/ca.crt")
client.connect("broker.example.com", 8883) # Port 8883 for MQTTS
2. Username/Password Authentication
# Mosquitto: create password file
mosquitto_passwd -c /etc/mosquitto/passwd username
# Configure mosquitto.conf
allow_anonymous false
password_file /etc/mosquitto/passwd
3. Access Control Lists (ACL)
# /etc/mosquitto/acl
user sensor1
topic read home/livingroom/#
user controller
topic readwrite home/livingroom/light/set
4. Network Segmentation
- IoT devices on separate VLAN
- Broker not directly exposed to internet
- Use VPN or reverse proxy for remote access
Common Pitfalls
❌ Using QoS 2 Everywhere
Problem: Highest latency and broker load
Solution: Use QoS 0 for telemetry, QoS 1 for most cases, QoS 2 only when critical
❌ Not Setting LWT
Problem: No way to detect unexpected disconnections
Solution: Always set Last Will and Testament for status topics
❌ Deep Topic Hierarchies
Problem: building/floor/wing/room/device/sensor/type/unit (hard to manage)
Solution: Keep 3-5 levels max: site/device/metric
❌ Large Payloads
Problem: Sending 1MB JSON in MQTT message
Solution: MQTT is for small messages; use HTTP/FTP for large files, send URL via MQTT
Debugging MQTT
Command-Line Tools:
# Subscribe to all topics (# wildcard)
mosquitto_sub -h localhost -t "#" -v
# Publish with retain
mosquitto_pub -h localhost -t "test/topic" -m "Hello" -r
# Publish with QoS 2
mosquitto_pub -h localhost -t "test/topic" -m "Critical" -q 2
# Subscribe with authentication
mosquitto_sub -h broker.example.com -p 8883 -u user -P pass -t "data/#" --cafile ca.crt
GUI Tools:
- MQTT Explorer: Desktop app for browsing topics and debugging
- HiveMQ Webclient: Browser-based client
- MQTT.fx: Java-based MQTT client
Conclusion
MQTT is the ideal protocol for IoT and embedded systems when you need:
- Lightweight, low-bandwidth communication
- Publish-subscribe decoupling
- Reliable delivery with QoS levels
- Real-time data distribution
Start with Mosquitto broker for development, use Paho libraries for clients, and design your topic hierarchy before scaling. MQTT’s simplicity and efficiency make it the backbone of modern IoT infrastructure.
Further Reading
Are you using MQTT in production? What challenges have you faced? Share your experience in the comments.