mqtt
stableEncode and decode MQTT 3.1.1 protocol packets for publish/subscribe messaging, including authentication, will messages, and QoS acknowledgement flows.
use plugin mqtt Plugin snippets can be edited in the Playground, but native capabilities are executed locally with the Zolo CLI.
Functions (21)
- encode_connect Encode a CONNECT packet
- encode_connect_with_auth Encode CONNECT with username and password
- encode_connect_with_will Encode CONNECT with a last-will message
- encode_publish Encode a PUBLISH packet
- encode_publish_with_options Encode PUBLISH with retain, dup, and packet ID
- encode_subscribe Encode a SUBSCRIBE packet for one topic
- encode_subscribe_multiple Encode SUBSCRIBE for multiple topics
- encode_unsubscribe Encode an UNSUBSCRIBE packet
- encode_disconnect Encode a DISCONNECT packet
- encode_pingreq Encode a PINGREQ packet
- encode_puback Encode a PUBACK acknowledgement
- encode_pubrec Encode a PUBREC (QoS 2 received) packet
- encode_pubrel Encode a PUBREL (QoS 2 release) packet
- encode_pubcomp Encode a PUBCOMP (QoS 2 complete) packet
- decode_connack Decode a CONNACK response packet
- decode_publish Decode a PUBLISH packet
- decode_suback Decode a SUBACK response packet
- decode_packet_type Decode the first byte of any MQTT packet
- packet_type_name Convert packet type integer to name string
- remaining_length_encode Encode an integer as MQTT variable-length field
- remaining_length_decode Decode an MQTT variable-length field from bytes
From import to result
- 01Import only what you use
- 02Copy the smallest example
- 03Run it with the Zolo CLI
Overview¶
mqtt is a dependency-free codec for the MQTT 3.1.1 wire protocol. It does not open sockets or manage a connection — instead, each encode_* function builds the raw bytes of one control packet, and each decode_* function parses incoming bytes back into a table. You drive the actual transport yourself (typically over the net plugin or a raw TCP stream), feeding encoded packets out and handing received bytes to the decoders. This keeps the plugin small and lets you implement exactly the publish/subscribe flow your broker needs.
The mental model is a request/response handshake expressed as byte buffers: send a CONNECT, read a CONNACK; send a SUBSCRIBE, read a SUBACK; send or receive PUBLISH frames, and for QoS 1/2 exchange the matching PUBACK/PUBREC/PUBREL/PUBCOMP acknowledgements. Use decode_packet_type to peek at any inbound frame's first byte and dispatch it to the right decoder. Use it whenever you need an embedded, allocation-light MQTT client without pulling in a full broker library.
Common patterns¶
Open a session: encode a CONNECT, send it, then decode the broker's CONNACK reply.
use plugin mqtt::{encode_connect, decode_connack}
let connect = encode_connect("device-01", true)
// ... send `connect` over your transport, then read the reply bytes ...
let reply = [0x20, 0x02, 0x00, 0x00]
let ack = decode_connack(reply)
print("accepted={ack["return_code"] == 0}")Round-trip a message: encode a PUBLISH and decode it back into its fields.
use plugin mqtt::{encode_publish, decode_publish}
let frame = encode_publish("sensors/temp", "23.5", 1)
let msg = decode_publish(frame)
print("{msg["topic"]} = {msg["payload"]} (QoS {msg["qos"]})")Dispatch any inbound frame by peeking at its type byte before decoding.
use plugin mqtt::{decode_packet_type, decode_publish, decode_suback}
fn handle(bytes) {
let info = decode_packet_type(bytes)
if info["type_name"] == "PUBLISH" {
let m = decode_publish(bytes)
print("message on {m["topic"]}")
} else if info["type_name"] == "SUBACK" {
let s = decode_suback(bytes)
print("subscribed, packet {s["packet_id"]}")
}
}Encode a CONNECT packet
Encodes a minimal MQTT 3.1.1 CONNECT packet with the given client ID and clean-session flag. Uses a 60-second keep-alive with no username, password, or will.
use plugin mqtt::{encode_connect, decode_connack}
let pkt = encode_connect("my-client-1", true)
print("CONNECT bytes: {pkt}")Encode CONNECT with username and password
Encodes a CONNECT packet with username and password credentials. Sets the username and password flags in the connect flags byte.
use plugin mqtt::{encode_connect_with_auth}
let pkt = encode_connect_with_auth("device-01", true, 30, "admin", "secret")
print(pkt)Encode CONNECT with a last-will message
Encodes a CONNECT packet with a last-will-and-testament message, which the broker publishes if the client disconnects unexpectedly.
use plugin mqtt::{encode_connect_with_will}
let pkt = encode_connect_with_will(
"sensor-01", true, 60,
"sensors/sensor-01/status", "offline", 1, true
)Encode a PUBLISH packet
Encodes a PUBLISH packet with a topic, string payload, and QoS level (0, 1, or 2). For QoS > 0, uses packet ID 1.
use plugin mqtt::{encode_publish, decode_publish}
let pkt = encode_publish("sensors/temperature", "23.5", 0)
let decoded = decode_publish(pkt)
print(decoded["topic"])
print(decoded["payload"])Encode PUBLISH with retain, dup, and packet ID
Encodes a PUBLISH packet with full control over all MQTT header flags: retain bit, duplicate delivery flag, and explicit packet ID for QoS > 0.
use plugin mqtt::{encode_publish_with_options}
let pkt = encode_publish_with_options("home/lights", "on", 1, true, false, 42)Set dup to true when re-sending an unacknowledged QoS 1 message with the same packet ID, so the broker knows it may be a duplicate:
use plugin mqtt::{encode_publish_with_options, decode_publish}
let resend = encode_publish_with_options("home/lights", "on", 1, false, true, 42)
let msg = decode_publish(resend)
print("dup={msg["dup"]} packet_id={msg["packet_id"]}")Encode a SUBSCRIBE packet for one topic
Encodes a SUBSCRIBE packet for a single topic filter with the requested QoS level.
use plugin mqtt::{encode_subscribe, decode_suback}
let pkt = encode_subscribe("sensors/#", 1, 1)Encode SUBSCRIBE for multiple topics
Encodes a SUBSCRIBE packet for multiple topic filters in a single packet. Each entry in topics is a table with topic and qos fields.
use plugin mqtt::{encode_subscribe_multiple}
let topics = [
#{"topic": "sensors/temp", "qos": 1},
#{"topic": "sensors/humidity", "qos": 0},
#{"topic": "alerts/#", "qos": 2}
]
let pkt = encode_subscribe_multiple(topics, 5)Encode an UNSUBSCRIBE packet
Encodes an UNSUBSCRIBE packet for one or more topic filters.
use plugin mqtt::{encode_unsubscribe}
let pkt = encode_unsubscribe(["sensors/temp", "sensors/humidity"], 6)Encode a DISCONNECT packet
Returns the 2-byte DISCONNECT packet (0xE0 0x00). Send this before closing the TCP connection for a clean disconnect.
use plugin mqtt::{encode_disconnect}
let pkt = encode_disconnect()Encode a PINGREQ packet
Returns the 2-byte PINGREQ packet used to keep the connection alive. The broker responds with PINGRESP.
use plugin mqtt::{encode_pingreq}
let pkt = encode_pingreq()Encode a PUBACK acknowledgement
Encodes a PUBACK acknowledgement for a QoS 1 PUBLISH. Send this after receiving and processing a QoS 1 message.
use plugin mqtt::{encode_puback}
let ack = encode_puback(42)Encode a PUBREC (QoS 2 received) packet
Encodes a PUBREC packet, the first step of the QoS 2 acknowledgement flow (received).
use plugin mqtt::{encode_pubrec, encode_pubrel, encode_pubcomp}
let rec = encode_pubrec(10)
let rel = encode_pubrel(10)
let comp = encode_pubcomp(10)Encode a PUBREL (QoS 2 release) packet
Encodes a PUBREL packet, the second step of the QoS 2 flow (release).
use plugin mqtt::{encode_pubrel}
let pkt = encode_pubrel(10)Encode a PUBCOMP (QoS 2 complete) packet
Encodes a PUBCOMP packet, the final step of the QoS 2 flow (complete).
use plugin mqtt::{encode_pubcomp}
let pkt = encode_pubcomp(10)Decode a CONNACK response packet
Decodes a CONNACK response into {session_present, return_code}. A return_code of 0 means the connection was accepted.
use plugin mqtt::{decode_connack}
let connack_bytes = [0x20, 0x02, 0x00, 0x00]
let result = decode_connack(connack_bytes)
print("accepted={result["return_code"] == 0}")
print("session_present={result["session_present"]}")Decode a PUBLISH packet
Decodes a PUBLISH packet into {topic, payload, qos, retain, dup, packet_id}.
use plugin mqtt::{encode_publish, decode_publish}
let pkt = encode_publish("home/temp", "22.1", 1)
let msg = decode_publish(pkt)
print("{msg["topic"]}: {msg["payload"]} (QoS {msg["qos"]})")Decode a SUBACK response packet
Decodes a SUBACK response into {packet_id, return_codes} where each return code is the granted QoS level (0, 1, 2) or 0x80 for failure.
use plugin mqtt::{decode_suback}
let suback_bytes = [0x90, 0x03, 0x00, 0x01, 0x01]
let result = decode_suback(suback_bytes)
print("packet_id={result["packet_id"]}")Pair it with encode_subscribe_multiple to confirm the broker granted a QoS for every topic you asked for:
use plugin mqtt::{encode_subscribe_multiple, decode_suback}
let topics = [
#{"topic": "a/#", "qos": 1},
#{"topic": "b/#", "qos": 2}
]
let sub = encode_subscribe_multiple(topics, 11)
// ... send `sub`, then read the SUBACK reply ...
let granted = decode_suback([0x90, 0x04, 0x00, 0x0B, 0x01, 0x02])
print("granted codes for packet {granted["packet_id"]}")Decode the first byte of any MQTT packet
Peeks at the first byte of any MQTT packet and returns {type_id, type_name, dup, qos, retain}. Use to dispatch incoming bytes to the correct decoder.
use plugin mqtt::{encode_publish, decode_packet_type}
let pkt = encode_publish("test", "hello", 0)
let info = decode_packet_type(pkt)
print(info["type_name"])Because it also surfaces the header flags, you can read QoS and retain straight from the type byte without fully decoding the packet:
use plugin mqtt::{encode_publish_with_options, decode_packet_type}
let pkt = encode_publish_with_options("status", "up", 2, true, false, 7)
let info = decode_packet_type(pkt)
print("{info["type_name"]} qos={info["qos"]} retain={info["retain"]}")Convert packet type integer to name string
Converts a packet type integer (1–14) to its name string such as "CONNECT", "PUBLISH", "SUBACK", etc.
use plugin mqtt::{packet_type_name}
print(packet_type_name(3))
print(packet_type_name(9))Encode an integer as MQTT variable-length field
Encodes an integer as an MQTT variable-length field (1–4 bytes). Useful when manually constructing raw MQTT frames.
use plugin mqtt::{remaining_length_encode, remaining_length_decode}
let encoded = remaining_length_encode(300)
let decoded = remaining_length_decode(encoded)
print(decoded["length"])
print(decoded["bytes_used"])Decode an MQTT variable-length field from bytes
Decodes an MQTT variable-length field from a byte sequence, returning {length, bytes_used}.
use plugin mqtt::{remaining_length_decode}
let result = remaining_length_decode([0xC1, 0x02])
print(result["length"])