MQTT 5 es la versión actual del estándar MQTT y resuelve muchos de los problemas con los que se topan los equipos al operar MQTT 3.1.1 en producción. En esta guía verás qué características de MQTT 5 cambian de verdad el trabajo de ingeniería del día a día, las compararás con 3.1.1 y las probarás tú mismo contra el servidor MQTT público gratuito broker.connectmq.com (pensado para pruebas y prototipos, nunca para datos de producción) con las herramientas CLI de Mosquitto y con Python.
Si el protocolo es nuevo para ti, empieza por qué es un broker MQTT y vuelve aquí después.
Por qué existe MQTT 5
MQTT 3.1.1 es pequeño, estable y está en todas partes. Pero deja sin respuesta preguntas importantes. ¿Por qué se rechazó mi conexión? ¿Por qué falló mi suscripción? ¿Cómo reparto los mensajes de un topic entre varios workers? ¿Cuánto tiempo debería guardar el broker mi sesión o un comando antiguo?
En 3.1.1 las respuestas solían ser «el broker cerró el socket» o «escríbelo tú mismo en el payload». MQTT 5, estandarizado por OASIS en 2019, añade al protocolo información explícita, metadatos y límites, manteniendo el mismo modelo de publicación/suscripción. Tus topics, niveles de QoS y retained messages funcionan exactamente igual que antes. La especificación completa está disponible en OASIS.
MQTT 3.1.1 frente a MQTT 5 de un vistazo
| Capacidad | MQTT 3.1.1 | MQTT 5 |
|---|---|---|
| Información de errores | Unos pocos códigos de retorno en CONNACK, flag de fallo en SUBACK | Reason codes en casi todas las confirmaciones, más reason strings opcionales |
| Duración de la sesión | Clean session activada o desactivada | Session Expiry Interval en segundos |
| Duración del mensaje | Ilimitada hasta su entrega | Message Expiry Interval por mensaje |
| Balanceo de carga | No está en el estándar | Suscripciones compartidas $share/{group}/{filter} |
| Ahorro de ancho de banda | Topic completo en cada PUBLISH | Topic aliases (un entero en lugar de la cadena del topic) |
| Metadatos personalizados | Solo dentro del payload | User properties (pares clave/valor) |
| Petición/respuesta | Convenciones ad hoc | Response Topic y Correlation Data |
| Control de flujo | Depende de la implementación | Receive Maximum negociado en CONNECT/CONNACK |
| Límites del servidor | El cliente tiene que adivinarlos | Anunciados en el CONNACK (Maximum QoS, Retain Available y más) |
Reason codes: saber por qué algo ha fallado
El cambio más útil de MQTT 5 para la operación son los reason codes. CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK y DISCONNECT llevan un código de un byte, y el servidor puede enviar junto a él una reason string legible.
| Código | Nombre | Significado habitual |
|---|---|---|
| 0x00 | Success | Operación aceptada |
| 0x10 | No matching subscribers | Publicación QoS 1/2 aceptada, pero no hay nadie suscrito |
| 0x86 | Bad user name or password | Credenciales incorrectas |
| 0x87 | Not authorized | El cliente no tiene permiso para realizar la acción |
| 0x89 | Server busy | El broker se está protegiendo; reintenta más tarde |
| 0x95 | Packet too large | El mensaje supera el tamaño máximo de paquete |
| 0x97 | Quota exceeded | Se ha alcanzado un límite de tasa o de uso |
Los códigos por debajo de 0x80 indican éxito; los de 0x80 en adelante son fallos. Para los dispositivos desplegados esto es muy importante: el firmware puede reaccionar de forma distinta ante «contraseña incorrecta» (detenerse y alertar) que ante «servidor ocupado» (esperar y reintentar). ConnectMQ devuelve reason codes como 0x97 Quota exceeded y 0x89 Server busy, de modo que tus clientes pueden distinguir un problema de cuota de una sobrecarga temporal.
Expiración de sesión y expiración de mensaje
Session Expiry Interval
En 3.1.1, una sesión persistente (clean session = false) dura hasta que el cliente vuelve, posiblemente para siempre. MQTT 5 lo sustituye por dos ajustes: Clean Start (empezar de cero o reanudar) y Session Expiry Interval (cuántos segundos conserva el broker la sesión tras la desconexión). Un sensor que duerme diez minutos puede pedir una sesión de 15 minutos y seguir recibiendo sus mensajes QoS 1 encolados al despertar, mientras que las sesiones abandonadas desaparecen por sí solas.
En ConnectMQ, la expiración de sesión tiene un tope por plan; en el servidor MQTT público gratuito, el máximo es de 30 minutos.
Message Expiry Interval
Un publicador puede asignar a cada mensaje una vida útil en segundos. Si el mensaje sigue esperando en una cola o almacenado como retenido cuando se agota ese intervalo, el broker lo descarta. Es exactamente lo que quieres para comandos como «abrir la válvula», que resultan peligrosos si se ejecutan con una hora de retraso.
mosquitto_pub -h broker.connectmq.com -p 1883 -V mqttv5 -i demo-a1b2-pub \
-t "connectmq/demo/a1b2/commands/valve1" -m "open" -q 1 \
-D publish message-expiry-interval 60
Suscripciones compartidas: balanceo de carga integrado
Con una suscripción normal, cada suscriptor recibe todos los mensajes. Eso es un problema cuando una sola instancia del backend no da abasto con miles de dispositivos. Las suscripciones compartidas de MQTT 5 permiten que varios clientes se unan a un grupo; el broker entrega cada mensaje a un solo miembro del grupo.
La sintaxis es $share/{group}/{filter}. Los publicadores no cambian nada: siguen publicando en el topic normal.
Pruébalo: dos workers compartiendo un topic
Abre dos terminales e inicia dos consumidores en el mismo grupo workers. Sustituye a1b2 por tu propio ID aleatorio, porque cualquiera puede leer el área pública.
# Terminal 1
mosquitto_sub -h broker.connectmq.com -p 1883 -V mqttv5 -i demo-a1b2-w1 \
-t '$share/workers/connectmq/demo/a1b2/jobs' -v
# Terminal 2
mosquitto_sub -h broker.connectmq.com -p 1883 -V mqttv5 -i demo-a1b2-w2 \
-t '$share/workers/connectmq/demo/a1b2/jobs' -v
Ahora publica seis trabajos desde una tercera terminal:
for i in 1 2 3 4 5 6; do
mosquitto_pub -h broker.connectmq.com -p 1883 -V mqttv5 -i demo-a1b2-pub \
-t "connectmq/demo/a1b2/jobs" -m "job-$i" -q 1
done
Cuando lo ejecutamos contra broker.connectmq.com, el worker 1 recibió job-1, job-3 y job-5, y el worker 2 recibió job-2, job-4 y job-6. Cada trabajo se procesó una sola vez, y añadir un tercer worker reparte aún más la carga sin tocar los publicadores. En bash, las comillas simples alrededor del topic son importantes para que la shell no expanda $share.
Los usos típicos son servicios de ingesta escalados horizontalmente, procesadores de comandos y cualquier consumidor que ejecutes como varias réplicas de contenedor.
Topic aliases: paquetes más pequeños en enlaces limitados
Los topics industriales pueden ser largos, por ejemplo acme/izmir/machining/line1/cnc1/telemetry. Enviar esa cadena en cada PUBLISH desperdicia ancho de banda en enlaces móviles o por satélite, como los de una mina remota en los Andes o una plataforma de oil & gas. Con un topic alias, el cliente envía una vez el topic completo junto con un alias numérico y, a partir de ahí, solo envía el número del alias. Cada parte anuncia cuántos alias acepta (Topic Alias Maximum) en CONNECT y CONNACK. La mayoría de las librerías cliente gestionan los alias por ti una vez que los activas.
User properties: metadatos sin tocar el payload
Las user properties son pares clave/valor UTF-8 arbitrarios asociados a un paquete. Úsalas para indicar el tipo de contenido, versiones de esquema, IDs de trazas o la versión de firmware del emisor, mientras el payload sigue siendo una lectura de sensor limpia.
mosquitto_pub -h broker.connectmq.com -p 1883 -V mqttv5 -i demo-a1b2-pub \
-t "connectmq/demo/a1b2/sensors/t1" -m '{"temperature":22.4}' \
-D publish user-property schema v2 -D publish user-property fw 1.4.0
Un suscriptor puede mostrarlas con mosquitto_sub -V mqttv5 -F "%t %p %P".
Petición/respuesta con Response Topic y Correlation Data
Muchos flujos IoT son en realidad llamadas RPC: «lee tu configuración», «reinicia», «dame los 10 últimos valores». MQTT 5 lo estandariza:
- El solicitante se suscribe a su propio topic de respuesta.
- Publica la petición con una propiedad
Response Topicque apunta a ese topic de respuesta y unaCorrelation Dataque identifica la petición. - El que responde contesta en el response topic y devuelve una copia de la correlation data.
Como el response topic se transmite a los suscriptores, quien responde no necesita ningún conocimiento de enrutamiento fijado en el código. Puedes ver llegar las propiedades con:
mosquitto_sub -h broker.connectmq.com -p 1883 -V mqttv5 -i demo-a1b2-s \
-t "connectmq/demo/a1b2/#" -F "%t %p | response=%R props=%P"
Control de flujo y límites anunciados por el servidor
Receive Maximum
Receive Maximum limita cuántos mensajes QoS 1 y QoS 2 pueden estar en tránsito (enviados pero aún sin confirmar) a la vez. El cliente comunica su límite al servidor en el CONNECT y el servidor comunica el suyo al cliente en el CONNACK. Un microcontrolador pequeño puede fijar un valor bajo para no verse nunca desbordado, y el broker puede protegerse de un cliente que publica más rápido de lo que es capaz de confirmar. Para saber más sobre las confirmaciones, consulta Niveles de QoS en MQTT explicados.
Límites en el CONNACK
En 3.1.1, un cliente descubría los límites fallando. En MQTT 5, el servidor los anuncia en las propiedades del CONNACK, entre ellas Maximum QoS, Retain Available, Wildcard Subscription Available, Shared Subscription Available, Receive Maximum y Topic Alias Maximum. Un cliente bien escrito los lee una vez y se adapta, por ejemplo bajando a QoS 1 si el plan actual no ofrece QoS 2.
MQTT 5 en Python con Paho 2.x
El siguiente script se conecta con MQTT 5, muestra los límites anunciados en el CONNACK y ejecuta un ciclo completo de petición/respuesta usando expiración de mensaje, user properties, response topic y correlation data.
import time
import uuid
import paho.mqtt.client as mqtt
from paho.mqtt.packettypes import PacketTypes
from paho.mqtt.properties import Properties
BROKER = "broker.connectmq.com"
PREFIX = "connectmq/demo/a1b2" # usa tu propio ID aleatorio
REQ_TOPIC = f"{PREFIX}/rpc/request"
RESP_TOPIC = f"{PREFIX}/rpc/response"
def on_connect(client, userdata, flags, reason_code, properties):
print("CONNACK:", reason_code)
for name in ("MaximumQoS", "RetainAvailable", "ReceiveMaximum",
"TopicAliasMaximum", "SharedSubscriptionAvailable"):
if hasattr(properties, name):
print(f" {name} = {getattr(properties, name)}")
client.subscribe(REQ_TOPIC, qos=1)
client.subscribe(RESP_TOPIC, qos=1)
def on_message(client, userdata, msg):
props = msg.properties
if msg.topic == REQ_TOPIC:
# Lado que responde: contestar en el topic solicitado y devolver la correlation data
reply = Properties(PacketTypes.PUBLISH)
reply.CorrelationData = props.CorrelationData
client.publish(props.ResponseTopic, b'{"uptime": 3600}', qos=1,
properties=reply)
elif msg.topic == RESP_TOPIC:
print("Response:", msg.payload.decode(),
"correlation:", props.CorrelationData.decode())
connect_props = Properties(PacketTypes.CONNECT)
connect_props.SessionExpiryInterval = 600 # conservar la sesión 10 minutos
connect_props.ReceiveMaximum = 10 # máx. 10 mensajes entrantes sin confirmar
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id="demo-a1b2-py", protocol=mqtt.MQTTv5)
client.on_connect = on_connect
client.on_message = on_message
client.connect(BROKER, 1883, clean_start=True, properties=connect_props)
client.loop_start()
time.sleep(2)
request = Properties(PacketTypes.PUBLISH)
request.ResponseTopic = RESP_TOPIC
request.CorrelationData = uuid.uuid4().hex.encode()
request.MessageExpiryInterval = 30
request.UserProperty = [("schema", "v1"), ("app", "blog-demo")]
client.publish(REQ_TOPIC, b'{"cmd": "status"}', qos=1, properties=request)
time.sleep(3)
client.loop_stop()
client.disconnect()
En un sistema real, el solicitante y el que responde son clientes distintos, pero el código es el mismo. En producción, conéctate a tu propio broker con YOUR_USERNAME, YOUR_PASSWORD y YOUR_CLIENT_ID del portal, idealmente por TLS en el puerto 8883 (consulta las buenas prácticas de seguridad en MQTT).
Soporte de MQTT 5 en ConnectMQ
Los brokers de ConnectMQ hablan tanto MQTT 3.1.1 como MQTT 5.0 sobre TCP, TLS y WebSocket, así que dispositivos antiguos y nuevos pueden compartir el mismo broker. Las funciones de MQTT 5 implementadas actualmente son:
| Función | Soporte en ConnectMQ |
|---|---|
| Reason codes | Sí, por ejemplo 0x97 Quota exceeded y 0x89 Server busy |
| Expiración de sesión | Sí, con tope por plan (30 minutos en el servidor MQTT público gratuito) |
| Message expiry interval | Sí |
| Suscripciones compartidas | Sí, $share/{group}/{filter}; en el servidor público y en el plan Serverless (Paid) y superiores |
| Topic alias | Sí |
| Response topic (petición/respuesta) | Sí, se transmite a los suscriptores |
| Límites en el CONNACK | Maximum QoS, Retain Available, Wildcard y Shared Subscription Available, Receive Maximum |
| Retained messages | Sí |
La extensión de ConnectMQ para VS Code muestra directamente los reason codes de CONNACK, SUBACK y PUBACK, algo muy útil cuando estás depurando por qué se ha rechazado una suscripción.
Empieza con ConnectMQ
Puedes probar todos los ejemplos de este artículo en broker.connectmq.com sin cuenta. Cuando estés listo para tener tu propio broker aislado con credenciales, regístrate en app.connectmq.com: el plan Serverless es gratuito, no requiere tarjeta de crédito y te da un broker en minutos. Compara las opciones en nuestra guía sobre qué es un broker MQTT gestionado.
Preguntas frecuentes
¿Es MQTT 5 compatible con MQTT 3.1.1?
El formato de los paquetes es distinto, así que un cliente elige una versión al conectarse. Un broker que admite ambas, como ConnectMQ, permite que clientes MQTT 3.1.1 y MQTT 5 intercambien mensajes en los mismos topics.
¿Necesito MQTT 5 para usar suscripciones compartidas?
Las suscripciones compartidas están definidas en el estándar MQTT 5. Conéctate con MQTT 5 (por ejemplo, -V mqttv5 en las herramientas de Mosquitto) y suscríbete a $share/{group}/{filter} para obtener una entrega con balanceo de carga.
¿Qué diferencia hay entre la expiración de sesión y la expiración de mensaje?
La expiración de sesión controla cuánto tiempo conserva el broker la sesión y los mensajes encolados de un cliente desconectado. La expiración de mensaje controla cuánto tiempo sigue siendo válido un mensaje concreto antes de que el broker lo descarte.
¿Qué característica de MQTT 5 debería adoptar primero?
Empieza por los reason codes, porque hacen visibles los fallos sin coste alguno. Después añade la expiración de sesión para los dispositivos que duermen y las suscripciones compartidas para los servicios backend que necesitan escalar.



