Skip to main content
โšก Calmops

MQTT Protocol: Lightweight IoT Messaging 2026

Introduction

MQTT (Message Queuing Telemetry Transport) is a lightweight, publish-subscribe network protocol designed for constrained devices and low-bandwidth, high-latency networks. Originally developed by IBM in 1999 for connecting oil pipeline sensors via satellite, MQTT has become the de facto standard for IoT communications in 2026, powering millions of connected devices worldwide.

This comprehensive guide covers MQTT protocol mechanics, quality of service levels, topic structures, security implementations, and practical IoT applications. Understanding MQTT is essential for developers building IoT solutions and messaging architectures.

What is MQTT?

MQTT is a publish-subscribe messaging protocol that operates over TCP/IP. Unlike request-response protocols, MQTT enables asynchronous communication where publishers send messages to topics without knowing who subscribes.

Key Characteristics

Lightweight: Minimal overhead (2-byte header minimum).

Publish-Subscribe: Decoupled producers and consumers.

Quality of Service: Three delivery guarantees.

Persistent Sessions: Offline message delivery.

Last Will and Testament: Notification on unexpected disconnection.

Use Cases

  • IoT sensor networks
  • Smart home systems
  • Industrial automation
  • Vehicle telematics
  • Healthcare monitoring
  • Energy grid management

Protocol Mechanics

Connection Flow

Client                    Broker
  |                         |
  |------- CONNECT ------->|
  |    (client ID, keepalive, credentials)
  |                         |
  |<------ CONNACK --------|
  |    (session present, return code)
  |                         |
  |------- SUBSCRIBE ----->|
  |    (topics, QoS requests)
  |                         |
  |<------ SUBACK ---------|
  |    (message IDs, QoS granted)
  |                         |
  |========= Ready =========|

MQTT Packet Structure

Fixed Header (2-5 bytes)
+--------+----------+----------+
| byte 1 |  byte 2  | bytes 3-5|
+--------+----------+----------+
| Type   | Flags   | Remaining|
| (4bit) | (4bit)  | Length   |
+--------+----------+----------+

Variable Header (optional)
- Packet Identifier
- Protocol Name
- Protocol Level
- Connect Flags
- Keep Alive
- Properties

Payload (optional)
- Client Identifier
- Will Topic/Message
- Username
- Password

Packet Types

Type Direction Description
CONNECT Cโ†’B Client connection request
CONNACK Bโ†’C Connection acknowledgment
PUBLISH Cโ†”B Publish message
PUBACK Cโ†”B Publish acknowledgment
PUBREC Cโ†”B Publish received
PUBREL Cโ†”B Publish release
PUBCOMP Cโ†”B Publish complete
SUBSCRIBE Cโ†’B Subscribe request
SUBACK Bโ†’C Subscribe acknowledgment
UNSUBSCRIBE Cโ†’B Unsubscribe request
UNSUBACK Bโ†’C Unsubscribe acknowledgment
PINGREQ Cโ†’B Ping request
PINGRESP Bโ†’C Ping response
DISCONNECT Cโ†’B Disconnect notification

Quality of Service

MQTT provides three levels of delivery guarantee.

QoS 0 - At Most Once

Publisher                    Broker                     Subscriber
    |                          |                          |
    |-- PUBLISH (QoS 0) ----->|                          |
    |                          |-- PUBLISH (QoS 0) ----->|
    |                          |                          |
    |                    (No acknowledgment)            |
  • Fire and forget
  • No acknowledgment
  • May be lost
  • Minimum overhead

QoS 1 - At Least Once

Publisher                    Broker                     Subscriber
    |                          |                          |
    |-- PUBLISH (QoS 1) ----->|                          |
    |                          |-- PUBLISH (QoS 1) ----->|
    |                          |                          |
    |                          |<-- PUBACK --------------|
    |<-- PUBACK --------------|                          |
    |                          |                          |
  • Acknowledged delivery
  • May duplicate
  • Guaranteed delivery

QoS 2 - Exactly Once

Publisher                    Broker                     Subscriber
    |                          |                          |
    |-- PUBLISH (QoS 2) ----->|                          |
    |                          |-- PUBLISH (QoS 2) ----->|
    |                          |<-- PUBREC --------------|
    |<-- PUBREC ---------------|                          |
    |-- PUBREL (ID) --------->|                          |
    |                          |-- PUBREL (ID) --------->|
    |                          |<-- PUBCOMP -------------|
    |<-- PUBCOMP --------------|                          |
    |                          |<-- PUBCOMP -------------|
    |                          |                          |
  • Four-part handshake
  • No loss, no duplicate
  • Highest overhead

Topics and Wildcards

Topic Structure

sensor/building1/floor2/temperature
sensor/building1/floor2/humidity
sensor/building1/floor3/temperature
home/livingroom/temperature
home/kitchen/temperature

Single-Level Wildcard (+)

sensor/building1/+/temperature

Matches:
- sensor/building1/floor2/temperature
- sensor/building1/floor3/temperature
- sensor/building1/roof/temperature

Does not match:
- sensor/building1/floor2/humidity

Multi-Level Wildcard (#)

sensor/building1/#

Matches:
- sensor/building1/floor2/temperature
- sensor/building1/floor2/humidity
- sensor/building1/floor3/temperature
- sensor/building1/floor3/pressure

Must be last character in subscription

Implementation

Python Client (paho-mqtt)

import paho.mqtt.client as mqtt
import json

class IoTClient:
    def __init__(self, client_id, broker, port=1883):
        self.client = mqtt.Client(client_id)
        self.broker = broker
        self.port = port
        
        self.client.on_connect = self.on_connect
        self.client.on_message = self.on_message
        self.client.on_disconnect = self.on_disconnect
    
    def on_connect(self, client, userdata, flags, rc):
        if rc == 0:
            print("Connected to MQTT broker")
            # Subscribe to topics
            self.client.subscribe("home/+/temperature", qos=1)
            self.client.subscribe("sensors/#", qos=0)
        else:
            print(f"Connection failed with code {rc}")
    
    def on_message(self, client, userdata, msg):
        topic = msg.topic
        payload = msg.payload.decode()
        qos = msg.qos
        print(f"[{topic}] {payload} (QoS {qos})")
    
    def on_disconnect(self, client, userdata, rc):
        print(f"Disconnected with code {rc}")
    
    def connect(self, username=None, password=None):
        if username and password:
            self.client.username_pw_set(username, password)
        
        self.client.connect(self.broker, self.port, keepalive=60)
        self.client.loop_start()
    
    def publish(self, topic, payload, qos=0, retain=False):
        if isinstance(payload, dict):
            payload = json.dumps(payload)
        
        result = self.client.publish(topic, payload, qos=qos, retain=retain)
        return result.rc == mqtt.MQTT_ERR_SUCCESS
    
    def subscribe(self, topic, qos=0):
        result = self.client.subscribe(topic, qos=qos)
        return result[0] == mqtt.MQTT_ERR_SUCCESS

# Usage
client = IoTClient("sensor-node-1", "broker.example.com", 1883)
client.connect(username="sensor", password="secret")
client.publish("sensor/building1/room101/temperature", "23.5", qos=1)

JavaScript Client

const mqtt = require('mqtt');

class IoTClient {
    constructor(clientId, brokerUrl) {
        this.client = mqtt.connect(brokerUrl, {
            clientId: clientId,
            clean: true,
            connectTimeout: 4000,
            reconnectPeriod: 1000,
        });
        
        this.client.on('connect', () => {
            console.log('Connected to MQTT broker');
            this.subscribe(['home/+/temperature', 'sensors/#']);
        });
        
        this.client.on('message', (topic, message, packet) => {
            console.log(`[${topic}] ${message.toString()}`);
        });
        
        this.client.on('error', (err) => {
            console.error('MQTT Error:', err);
        });
    }
    
    subscribe(topics) {
        topics.forEach(topic => {
            this.client.subscribe(topic, { qos: 1 });
        });
    }
    
    publish(topic, payload, options = { qos: 0, retain: false }) {
        if (typeof payload === 'object') {
            payload = JSON.stringify(payload);
        }
        this.client.publish(topic, payload, options);
    }
    
    disconnect() {
        this.client.end();
    }
}

const client = new IoTClient('device-001', 'mqtt://broker.example.com');
client.publish('sensor/device001/temperature', { value: 23.5, unit: 'C' });

Session Management

Clean Session

# Clean session = false (persistent session)
client = mqtt.Client(client_id="device-001", clean_session=False)

# This means:
# - Subscriptions persist across disconnections
# - Offline messages are queued
# - Client ID must be unique and stable

Offline Message Queueing

client = mqtt.Client(client_id="sensor-001")
client.max_queued_messages = 1000  # Default 100
client.max_queued_bytes = 1024 * 1024  # Default 0 (unlimited)

# Messages published while offline
# will be delivered on reconnect

Message Retaining

# Publisher: Retain last message on topic
client.publish("home/livingroom/temperature", "22.5", retain=True)

# Subscriber: Receives last retained message immediately on connect
# Useful for state discovery

Last Will and Testament

Configuring LWT

# Set last will message
client.will_set(
    topic="devices/status",
    payload="offline",
    qos=1,
    retain=True
)

# When client disconnects unexpectedly:
# - Broker publishes LWT message
# - Not published on intentional disconnect

Use Cases

  • Device online/offline status
  • Heartbeat monitoring
  • Unexpected shutdown alerts

Security

TLS/SSL Encryption

# MQTT over TLS (port 8883)
client.tls_set(
    ca_certs="/path/to/ca.crt",
    certfile="/path/to/client.crt",
    keyfile="/path/to/client.key",
    cert_reqs=ssl.CERT_REQUIRED
)

client.tls_insecure_set(False)

Authentication

# Username/Password
client.username_pw_set("username", "password")

# Or use certificates
client.tls_set(
    ca_certs="/path/to/ca.crt",
    certfile="/path/to/cert.crt",
    keyfile="/path/to/key.key"
)

ACL (Access Control List)

# allow.anonymous=false
# password_file /etc/mosquitto/passwd
# acl_file /etc/mosquitto/acl

# /etc/mosquitto/acl
user sensor
topic read sensors/#
topic write sensors/+/temperature

user actuator
topic read sensors/#
topic write home/+/+/command

MQTT 5.0 Features

User Properties

# MQTT 5.0: Custom properties
properties = {
    'user_property':('key', 'value')]
}

client [.publish(
    "sensors/data",
    payload,
    properties=properties
)

Topic Aliases

# Reduce bandwidth by using topic alias
# Broker assigns numeric alias to topic
# Subsequent messages use alias instead of full topic name

Payload Format Indicator

# Indicate payload format
properties = {
    'payload_format_indicator': 0,  # 0 = bytes, 1 = UTF-8
    'content_type': 'application/json'
}

Flow Control

# Receive maximum at once
client.max_inflight_messages_set(20)

# Flow control in MQTT 5.0
# Client can signal "pause" to broker

Brokers

Mosquitto (Eclipse)

# Installation
apt install mosquitto mosquitto-clients

# Configuration /etc/mosquitto/mosquitto.conf
listener 1883
allow_anonymous false
password_file /etc/mosquitto/passwd

listener 8883
tls_version tlsv1.2
certfile /etc/mosquitto/certs/server.crt
keyfile /etc/mosquitto/certs/server.key

# Run
mosquitto -c /etc/mosquitto/mosquitto.conf

EMQX

# Docker Compose
version: '3'
services:
  emqx:
    image: emqx/emqx:latest
    ports:
      - "1883:1883"
      - "8883:8883"
      - "8083:8083"
      - "18083:18083"
    environment:
      - EMQX_NAME=emqx
      - EMQX_HOST=127.0.0.1

HiveMQ

# Download HiveMQ Community Edition
wget https://www.hivemq.com/releases/hivemq-ce-latest.zip
unzip hivemq-ce-latest.zip
cd hivemq-ce-*

# Run
./bin/run.sh

Best Practices

Topic Design

  • Use hierarchical structure
  • Avoid deep nesting (max 5 levels)
  • Use consistent naming convention
  • Include device ID in topic path

QoS Selection

  • QoS 0: Non-critical sensor data
  • QoS 1: Important telemetry (default)
  • QoS 2: Critical commands, billing data

Performance

  • Keep client ID short
  • Use persistent sessions for unreliable networks
  • Set appropriate keepalive (60-300 seconds)
  • Monitor connection state

Security

  • Always use TLS in production
  • Implement authentication
  • Use ACLs for authorization
  • Rotate credentials regularly

Conclusion

MQTT remains the dominant protocol for IoT communications in 2026 due to its simplicity, efficiency, and proven reliability. Its publish-subscribe model perfectly suits the asynchronous nature of IoT applications, while the three QoS levels provide flexibility for different reliability requirements. Understanding MQTT implementation and best practices enables developers to build scalable, reliable IoT systems.

Resources

Comments