MQTT
Tiny publish/subscribe messages for sensors and small devices.
CONNECT
How a client says hello. Notice the length-prefixed strings and the packed flags byte.
Whole frame
10 10 00 04 4D 51 54 54 04 02 00 3C 00 04 64 65 6D 6F: the shortest useful CONNECT. Two header bytes plus a ten-byte variable header and a six-byte payload.
| Field | Offset | Example value | Meaning |
|---|---|---|---|
| Packet type | Byte 0, bits 7–4 | 1 = CONNECT | The top nibble says what this packet is: 1 CONNECT, 2 CONNACK, 3 PUBLISH, 4 PUBACK, 8 SUBSCRIBE, 9 SUBACK, 12 PINGREQ, 14 DISCONNECT. |
| Flags | Byte 0, bits 3–0 | 0 | The low nibble is type-specific. CONNECT requires 0000. |
| Remaining length | Byte 1 | 16 bytes follow | Bytes after this field: 16. It is a variable-length integer: 7 bits per byte, top bit = 'more bytes follow', so values up to 127 take one byte and a 256 MB packet takes four. |
| Protocol name length | Byte 2–3 | 4 | MQTT strings are a 2-byte length followed by UTF-8 bytes. |
| Protocol name | Byte 4–7 | MQTT | The literal text 'MQTT'. A broker checks it to reject non-MQTT traffic early. |
| Protocol level | Byte 8 | 4 = v3.1.1 | 4 = MQTT 3.1.1, 5 = MQTT 5.0. |
| Connect flags | Byte 9 | 0x02 = Clean Session | Which optional parts the payload contains and how the session behaves. Here only Clean Session is set. |
| Keep alive | Byte 10–11 | 60 s | Seconds the client may stay silent. If the broker hears nothing for 1.5× this, it drops the connection. The client sends PINGREQ to stay within it. |
| Client ID length | Byte 12–13 | 4 | Length of the client identifier string. |
| Client ID | Byte 14–17 | demo | A name the broker uses to identify this client. It must be unique: connecting twice with the same ID kicks the first one out. |
Overview
MQTT is a lightweight publish/subscribe protocol on top of TCP. Devices connect to a broker; they publish messages to named topics and subscribe to topic patterns, and the broker routes between them. A packet has a 2-byte fixed header and often just a handful more bytes.
Three quality-of-service levels trade bandwidth for certainty: 0 fire and forget, 1 at least once, 2 exactly once.
Key facts
- Transport
- TCP 1883 (8883 with TLS)
- Model
- publish / subscribe via broker
- Fixed header
- 2–5 bytes
- QoS
- 0, 1, 2
- Keep alive
- PINGREQ / PINGRESP
- Standard
- OASIS MQTT 3.1.1 / 5.0
PUBLISH
A sensor reading to a topic: 15 bytes of overhead around a 4-byte payload.
Whole frame
32 11 00 09 68 6F 6D 65 2F 74 65 6D 70 00 0A 32 31 2E 35: a four-byte reading sent to topic home/temp. The overhead is only 15 bytes, which is why MQTT suits tiny sensors on slow links.
| Field | Offset | Example value | Meaning |
|---|---|---|---|
| Packet type | Byte 0, bits 7–4 | 3 = PUBLISH | 3 = PUBLISH: carry a message to subscribers of a topic. |
| Flags | Byte 0, bits 3–0 | 0010 = QoS 1 | For PUBLISH the flags carry delivery options. Here QoS = 1 ('at least once'). |
| Remaining length | Byte 1 | 17 | 17 bytes follow: 2 (topic length) + 9 (topic) + 2 (packet ID) + 4 (payload). |
| Topic length | Byte 2–3 | 9 | Length of the topic name. |
| Topic | Byte 4–12 | home/temp | A hierarchical name with '/' separators. Subscribers can use wildcards such as home/+ or home/#. Messages are routed by topic, not by address. |
| Packet identifier | Byte 13–14 | 10 | Present only when QoS > 0. The PUBACK echoes it so the sender knows which message was received. |
| Payload | Byte 15–18 | 21.5 | The message itself. MQTT does not care about its format: here it is the text '21.5'. It could be JSON or raw binary. |
Publish with QoS 1
From subscribing to delivery and acknowledgement, with the broker in the middle.
Whole exchange
Publishers and subscribers never talk to each other: the broker routes by topic. The QoS level decides how many acknowledgements guard each message.
1The app subscribes
The subscriber tells the broker which topics it wants. home/# is a wildcard for everything under home/.
2Broker confirms
The broker confirms the subscription and the QoS it will use.
3The sensor publishes
The sensor does not know who is listening. It just sends the reading to a topic, with QoS 1 and packet ID 10.
4Broker fans it out
The broker forwards the message to every subscriber whose pattern matches the topic.
5App acknowledges
The subscriber's PUBACK tells the broker the delivery is done. Without it the broker would retry.
6Sensor gets its receipt
The broker acknowledges the sensor's packet ID 10: the message was accepted. If this never arrives, the sensor resends with DUP set. That is why QoS 1 means 'at least once'.
Where you meet it
- IoT sensors and smart-home devices (Home Assistant, Zigbee2MQTT)
- Telemetry from vehicles and industrial equipment on flaky links
- Facebook Messenger used it for mobile push in its early days
Watch out for
- Two clients with the same client ID kick each other off in a loop. Give each device a unique ID.
- Retained messages stay on the broker until you publish an empty retained payload to the topic.
- Topics are case-sensitive and a leading '/' creates a different, empty first level.
Standards
- OASIS MQTT Version 3.1.1
- OASIS MQTT Version 5.0