Back to IoT Blog
Protocols 20 min read β€’ Updated: March 2025

MQTT Protocol: Complete Guide for IoT

Master the MQTT protocol for IoT. Learn about topics, QoS levels, retained messages, wildcards, and best practices with practical examples.

1. Introduction to MQTT

MQTT (Message Queuing Telemetry Transport) is a lightweight, publish-subscribe messaging protocol designed for constrained devices and low-bandwidth, high-latency, or unreliable networks. It's the de facto standard for IoT communication.

πŸ“š History:

MQTT was invented in 1999 by Dr. Andy Stanford-Clark of IBM and Arlen Nipper of Eurotech for monitoring oil pipelines via satellite. It became an OASIS standard in 2013 and ISO standard in 2016.

Why MQTT for IoT?

MQTT vs HTTP for IoT

Feature MQTT HTTP
Protocol Type Publish-Subscribe Request-Response
Header Size 2 bytes minimum ~800 bytes
Power Consumption Low High
Network Usage Low High
Message Push Yes (server to client) No (client polling required)
Connection Persistent Per request

2. MQTT Architecture

MQTT uses a client-server architecture with three key components:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Publisher β”‚ β”‚ Broker β”‚ β”‚ Subscriber β”‚ β”‚ (Device) │────▢ β”‚ (Server) │────▢ β”‚ (Client) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ - Sensors β”‚ β”‚ - Routes β”‚ β”‚ - Dashboard β”‚ β”‚ - ESP32 β”‚ β”‚ Messages β”‚ β”‚ - Mobile β”‚ β”‚ - Arduino β”‚ β”‚ - Filters β”‚ β”‚ - Backend β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Multiple Clients β”‚ β”‚ Can Connect β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Components Explained

Publisher

Any device that sends data to the MQTT broker. Examples:

Broker

The central server that receives and routes messages. Popular brokers:

Subscriber

Clients that receive messages from the broker. Examples:

3. MQTT Topics Explained

Topics are UTF-8 strings that the broker uses to filter messages for each connected client. They represent the "channel" or "subject" of the message.

Topic Structure

home/livingroom/temperature
factory/line1/machine2/vibration
city/traffic/intersection5/cars
agricultural/field1/soil-moisture

Topic Best Practices

πŸ’‘ Topic Design Tips:
  • Use hierarchical structure (level/level/level)
  • Keep topics descriptive but concise
  • Avoid special characters (use hyphens or underscores)
  • Don't start with / (creates empty level)
  • Use consistent naming conventions

Topic Wildcards

Subscribers can use wildcards to match multiple topics:

Single-Level Wildcard (+)

# Subscribe to all rooms' temperature
home/+/temperature

# Matches:
βœ… home/livingroom/temperature
βœ… home/bedroom/temperature
βœ… home/kitchen/temperature

# Does NOT match:
❌ home/livingroom/humidity  (different last level)
❌ home/livingroom/sensor/temperature  (extra level)

Multi-Level Wildcard (#)

# Subscribe to everything under home
home/#

# Matches:
βœ… home/livingroom/temperature
βœ… home/livingroom/humidity
βœ… home/livingroom/sensor/battery
βœ… home/any/number/of/levels

# Must be at the end:
βœ… home/#
❌ home/#/temperature  (INVALID!)

Topic Examples by Use Case

Use Case Topic Structure Example
Smart Home home/{room}/{device}/{metric} home/kitchen/light1/state
Industrial factory/{line}/{machine}/{sensor} factory/line3/motor5/temp
Agriculture farm/{field}/{sensor}/{type} farm/north/soil1/moisture
Fleet Tracking fleet/{vehicle}/{data} fleet/truck42/gps

4. Quality of Service (QoS) Levels

MQTT defines three QoS levels that determine the delivery guarantee of messages. Choose based on your application's needs.

QoS 0: At Most Once (Fire and Forget)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚Publisher β”‚         β”‚ Subscriberβ”‚
β”‚          │──MSG──▢ β”‚          β”‚
β”‚          β”‚         β”‚          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
No acknowledgment

QoS 1: At Least Once (Acknowledged Delivery)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚Publisher β”‚         β”‚ Subscriberβ”‚
β”‚          │──MSG──▢ β”‚          β”‚
β”‚          │◀─PUBACK─│          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Acknowledged, may duplicate

QoS 2: Exactly Once (Assured Delivery)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚Publisher β”‚         β”‚ Subscriberβ”‚
β”‚          │──PUBLISH─▢│         β”‚
β”‚          │◀─PUBREC──│          β”‚
β”‚          │──PUBREL─▢│          β”‚
β”‚          │◀─PUBCOMP─│          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Guaranteed, no duplicates
⚠️ QoS Considerations:
  • Higher QoS = more bandwidth and battery usage
  • Both publisher and subscriber QoS matter
  • Effective QoS is the minimum of both
  • QoS 2 is rarely needed in IoT

5. Retained Messages

Retained messages are stored by the broker and sent to new subscribers immediately upon subscription. Perfect for "last known good" values.

How Retained Messages Work

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Publisher   β”‚      β”‚  Broker  β”‚      β”‚ Subscriber  β”‚
β”‚             β”‚      β”‚          β”‚      β”‚             β”‚
β”‚ Publish     β”‚      β”‚          β”‚      β”‚             β”‚
β”‚ retain=true │─────▢│ Store    β”‚      β”‚             β”‚
β”‚             β”‚      β”‚          β”‚      β”‚             β”‚
β”‚             β”‚      β”‚          β”‚      β”‚ Subscribe   β”‚
β”‚             β”‚      β”‚          │◀─────│             β”‚
β”‚             β”‚      β”‚          β”‚      β”‚             β”‚
β”‚             β”‚      β”‚ Send     │─────▢│ Receive     β”‚
β”‚             β”‚      β”‚ retained β”‚      β”‚ immediately β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Use Cases

Example: Device Status

// Publish device online status with retain
client.publish("home/sensor1/status", "online", { retain: true });

// When device goes offline
client.publish("home/sensor1/status", "offline", { retain: true });

// New subscriber immediately knows device status
subscriber.subscribe("home/sensor1/status");
// Receives: "offline" (instantly, without waiting for next publish)

Clearing Retained Messages

To remove a retained message, publish an empty payload with retain=true:

client.publish("home/sensor1/status", "", { retain: true });

6. Last Will and Testament

The Last Will and Testament (LWT) is a message that the broker publishes on behalf of a client if the client disconnects unexpectedly.

How LWT Works

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Client  β”‚      β”‚  Broker  β”‚      β”‚Subscriberβ”‚
β”‚          β”‚      β”‚          β”‚      β”‚          β”‚
β”‚ Connect  β”‚      β”‚          β”‚      β”‚          β”‚
β”‚ + LWT    │─────▢│ Store    β”‚      β”‚          β”‚
β”‚ config   β”‚      β”‚ LWT      β”‚      β”‚          β”‚
β”‚          β”‚      β”‚          β”‚      β”‚          β”‚
β”‚          β”‚      β”‚          β”‚      β”‚ Subscribeβ”‚
β”‚          β”‚      β”‚          │◀─────│ to LWT   β”‚
β”‚          β”‚      β”‚          β”‚      β”‚ topic    β”‚
β”‚ πŸ’₯ Crash β”‚      β”‚          β”‚      β”‚          β”‚
β”‚          β”‚      β”‚ Publish  β”‚      β”‚          β”‚
β”‚          β”‚      β”‚ LWT      │─────▢│ Receive  β”‚
β”‚          β”‚      β”‚ message  β”‚      β”‚ alert    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

LWT Configuration Example

// ESP32/Arduino with PubSubClient
const char* willTopic = "home/sensor1/status";
const char* willMessage = "offline";

// Connect with LWT
if (client.connect("sensor1", willTopic, 1, true, willMessage)) {
  // Publish online status
  client.publish("home/sensor1/status", "online", true);
}

// If client crashes or loses power:
// Broker automatically publishes "offline" to home/sensor1/status

Use Cases

7. Clean Session & Persistent Sessions

Clean Session (MQTT 3.1)

Session Expiry (MQTT 5.0)

MQTT 5.0 introduces session expiry for better control:

// MQTT 5.0 example
connectOptions.setSessionExpiryInterval(3600); // 1 hour

When to Use Each

Scenario Recommended Setting
Mobile app (intermittent) Clean Session = true
Always-on sensor Clean Session = false
Critical monitoring Clean Session = false + QoS 1
Temporary diagnostics Clean Session = true

8. MQTT Security

Authentication Methods

Username/Password

// Basic authentication
const char* username = "myUser";
const char* password = "mySecurePassword123";

client.connect("clientID", username, password);

Client Certificates (TLS/SSL)

// Secure connection with certificates
WiFiClientSecure net;
net.setCACert(caCert);
net.setCertificate(clientCert);
net.setPrivateKey(privateKey);

PubSubClient client(net);
client.setServer("mqtt.example.com", 8883);

Encryption (TLS/SSL)

πŸ”’ Security Best Practices:
  • Always use TLS in production
  • Never hardcode credentials in firmware
  • Use unique credentials per device
  • Implement access control lists (ACL)
  • Regularly rotate passwords/certificates
  • Monitor for unusual activity

9. Practical Examples

Example 1: ESP32 Temperature Sensor

#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>

#define DHTPIN 4
#define DHTTYPE DHT22

const char* ssid = "YOUR_WIFI";
const char* password = "YOUR_PASSWORD";
const char* mqtt_server = "broker.hivemq.com";

DHT dht(DHTPIN, DHTTYPE);
WiFiClient espClient;
PubSubClient client(espClient);

void reconnect() {
  while (!client.connected()) {
    String clientId = "ESP32Client-" + String(random(0xffff), HEX);
    if (client.connect(clientId.c_str())) {
      client.publish("home/livingroom/status", "online", true);
    } else {
      delay(5000);
    }
  }
}

void setup() {
  Serial.begin(115200);
  dht.begin();
  WiFi.begin(ssid, password);
  client.setServer(mqtt_server, 1883);
}

void loop() {
  if (!client.connected()) reconnect();
  client.loop();

  float temp = dht.readTemperature();
  float hum = dht.readHumidity();
  
  char tempStr[8];
  char humStr[8];
  dtostrf(temp, 1, 2, tempStr);
  dtostrf(hum, 1, 2, humStr);
  
  client.publish("home/livingroom/temperature", tempStr);
  client.publish("home/livingroom/humidity", humStr);
  
  delay(10000);
}

Example 2: Node-RED Subscriber

// Node-RED flow (JSON)
[
  {
    "id": "mqtt-in",
    "type": "mqtt in",
    "name": "Temperature",
    "topic": "home/+/temperature",
    "qos": "1",
    "broker": "mqtt-broker"
  },
  {
    "id": "debug",
    "type": "debug",
    "name": "Log Temperature"
  }
]

Example 3: Python Backend Subscriber

import paho.mqtt.client as mqtt

def on_connect(client, userdata, flags, rc):
    print(f"Connected with result code {rc}")
    client.subscribe("home/#")

def on_message(client, userdata, msg):
    print(f"{msg.topic}: {msg.payload.decode()}")

client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message

client.connect("broker.hivemq.com", 1883, 60)
client.loop_forever()

10. Best Practices

Topic Design

Payload Format

Connection Management

Resource Optimization

11. Troubleshooting

Problem: Client Won't Connect

Problem: Messages Not Received

Problem: High Battery Drain

Problem: Connection Drops

Debug Tools

Next Steps

Now that you understand MQTT, try these projects:

  • Build a temperature monitoring system with ESP32
  • Set up your own Mosquitto broker
  • Create a Node-RED dashboard
  • Integrate with Home Assistant
  • Explore MQTT 5.0 features