Elegir un formato de payload MQTT es una de las primeras decisiones de diseño de cualquier proyecto IoT, y una de las más difíciles de cambiar después. MQTT trata el payload como bytes opacos, así que JSON, Protobuf, CBOR o un formato binario propio funcionan igual en la red. Esta comparativa muestra en qué se diferencian las tres codificaciones más habituales en tamaño, gestión de esquemas y herramientas, con el recuento de bytes de un mismo mensaje de sensor, para que puedas elegir la adecuada para tus dispositivos.
MQTT es agnóstico respecto al payload
Un paquete PUBLISH de MQTT contiene un topic, algunos flags, propiedades opcionales de MQTT 5 y un payload de hasta 256 MB según el límite del protocolo (en la práctica, los brokers aplican límites mucho más bajos). El broker MQTT nunca mira dentro del payload. Eso te da libertad, pero también significa que nada impide que dos equipos publiquen formatos incompatibles en el mismo topic.
MQTT 5 añade dos propiedades que ayudan:
- Payload Format Indicator: 0 significa bytes sin especificar y 1 significa texto UTF-8.
- Content Type: una cadena libre, normalmente un tipo MIME como
application/jsonoapplication/x-protobuf.
Establecerlas cuesta unos pocos bytes y permite que las herramientas genéricas, los loggers y los consumidores traten tus mensajes correctamente.
El mensaje de ejemplo
Para comparar de forma justa, codificamos la misma lectura de una bomba de una planta en Monterrey en cada formato:
{"deviceId":"pump-07","ts":1760600000,"temp":21.5,"pressure":2.31,"rpm":1480,"running":true}
Seis campos: un ID de tipo cadena, una marca de tiempo Unix, dos medidas decimales, un entero y un booleano. Es una forma realista para la telemetría industrial y de edificios.
Comparación de tamaño
Los recuentos de bytes siguientes se han calculado a partir de las reglas de codificación de cada formato para este mensaje exacto:
| Codificación | Bytes | Notas |
|---|---|---|
| JSON formateado (sangría de 2 espacios) | 117 | Cómodo para las personas, derrochador en la red |
| JSON compacto | 92 | La opción por defecto habitual |
| JSON compacto con claves cortas | 65 | {"d":"pump-07","t":1760600000,"tp":21.5,"p":2.31,"r":1480,"on":1} |
| CBOR, floats de 64 bits | 74 | Mismas claves que JSON, valores binarios |
| CBOR, floats de 32 bits | 64 | Se sacrifica precisión en pressure |
Protobuf, floats como float |
30 | Números de campo en lugar de nombres |
Algunas observaciones:
- La mayor parte del tamaño de JSON son los nombres de las claves y el texto de los números. Acortar las claves te lleva muy lejos antes siquiera de cambiar de formato.
- CBOR mantiene la estructura de mapa autodescriptiva, así que los nombres de las claves siguen viajando en cada mensaje. Su ahorro viene de los números y booleanos binarios.
- Protobuf es pequeño porque los nombres de los campos viven en un archivo de esquema, no en el mensaje. El receptor debe tener ese esquema.
Recuerda que la cabecera MQTT y el topic se suman al payload. Un topic como site/plant1/line1/pump-07/telemetry ocupa 35 bytes, más que todo el payload en Protobuf. Si el ancho de banda importa de verdad, revisa también la longitud de los topics y los topic aliases de MQTT 5, no solo la codificación. Para una visión más amplia de a dónde van los bytes, consulta el consumo de ancho de banda de MQTT.
JSON: la opción por defecto, con buenos motivos
JSON gana en todo excepto en tamaño:
- Se lee en cualquier cliente MQTT, archivo de log o terminal.
- Todos los lenguajes y plataformas tienen un parser, incluido ArduinoJson en microcontroladores.
- Node-RED, Telegraf, Home Assistant y la mayoría de los dashboards lo aceptan sin configuración.
- Añadir un campo rara vez rompe a los consumidores antiguos.
Sus puntos débiles: no impone esquemas, los tipos numéricos son ambiguos (¿1 es un booleano, un entero o un float?) y el coste de parseo en microcontroladores muy pequeños. Para la mayoría de los proyectos con dispositivos conectados a la red eléctrica y Wi-Fi o Ethernet, esos compromisos son aceptables.
Protobuf: el más pequeño, con esquema
Protocol Buffers codifica cada campo como un número de campo más un valor compacto. El esquema de nuestro mensaje:
syntax = "proto3";
message PumpReading {
string device_id = 1;
uint64 ts = 2;
float temp = 3;
float pressure = 4;
uint32 rpm = 5;
bool running = 6;
}
Puntos fuertes: los payloads más pequeños, tipado fuerte, código generado en muchos lenguajes y reglas bien definidas para evolucionar los esquemas (añadir campos con números nuevos, no reutilizar nunca los antiguos). Sparkplug B, muy usado en MQTT industrial, se basa en Protobuf justo por estos motivos; consulta MQTT Sparkplug B y el Unified Namespace.
Puntos débiles: ilegible sin el esquema, así que depurar requiere herramientas de decodificación; los archivos de esquema deben distribuirse y versionarse entre todos los productores y consumidores; y las integraciones genéricas no pueden usar los datos sin un paso de decodificación.
CBOR: JSON binario
CBOR (RFC 8949) mantiene el modelo de datos de JSON, mapas, arrays, cadenas y números, pero lo codifica en binario. No necesita esquema, así que cualquier decodificador CBOR puede convertir un mensaje de nuevo en una estructura similar a JSON.
Puntos fuertes: más pequeño que JSON, autodescriptivo, cadenas de bytes binarias nativas (sin base64) y popular en protocolos para dispositivos limitados como CoAP. Puntos débiles: el ahorro es moderado salvo que también acortes las claves, hay menos herramientas que para JSON y la codificación de floats varía entre librerías, lo que puede sorprenderte al comparar bytes.
Checklist para decidir
- ¿Van a leer personas estos mensajes con frecuencia? Prefiere JSON.
- ¿Están los dispositivos en redes celulares con tarifa por consumo, backhaul LoRa o satélite? Considera Protobuf, o JSON compacto con claves cortas como primer paso.
- ¿Muchos equipos y lenguajes consumen los mismos datos? Los esquemas de Protobuf compensan; si no, JSON con un esquema documentado.
- ¿Necesitas blobs binarios dentro de datos estructurados? CBOR los gestiona de forma nativa.
- ¿Integras con herramientas comerciales estándar? JSON, o planifica un paso de decodificación que vuelva a publicar en JSON.
- ¿Interoperabilidad industrial? Mira Sparkplug B antes de inventar tu propio esquema Protobuf.
Elijas lo que elijas, versiónalo. Un campo schema en el payload, una versión en el topic o una user property de MQTT 5 sirven; lo importante es que los consumidores puedan distinguir los formatos.
Publicar con content type en Python
Este ejemplo con paho-mqtt 2.x publica la lectura en JSON compacto con el payload format indicator y el content type de MQTT 5 establecidos, en el servidor MQTT público gratuito de ConnectMQ, pensado para pruebas y prototipos, no para datos de producción:
import json
import uuid
import paho.mqtt.client as mqtt
from paho.mqtt.properties import Properties
from paho.mqtt.packettypes import PacketTypes
reading = {"deviceId": "pump-07", "ts": 1760600000, "temp": 21.5,
"pressure": 2.31, "rpm": 1480, "running": True}
payload = json.dumps(reading, separators=(",", ":")).encode()
print("payload bytes:", len(payload)) # 92
props = Properties(PacketTypes.PUBLISH)
props.PayloadFormatIndicator = 1
props.ContentType = "application/json"
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id=f"fmt-{uuid.uuid4().hex[:8]}",
protocol=mqtt.MQTTv5)
client.connect("broker.connectmq.com", 1883, keepalive=30)
client.loop_start()
client.publish("connectmq/demo/f6n1/pump-07/telemetry", payload,
qos=1, properties=props).wait_for_publish()
client.loop_stop()
client.disconnect()
Fíjate en separators=(",", ":"): el json.dumps por defecto de Python añade un espacio tras cada coma y cada dos puntos, lo que haría el payload más grande que la cifra compacta de la tabla.
Errores habituales con el payload
- Mezclar formatos en un mismo topic. Los consumidores fallan de formas confusas. Un topic, un formato.
- Sorpresas con la coma flotante. Los floats de 32 bits no pueden representar 2.31 exactamente. Decide la precisión de forma deliberada o envía enteros escalados.
- Marcas de tiempo en hora local. Usa tiempo Unix en UTC o ISO 8601 con desfase horario.
- Superar los límites de tamaño. Agrupar muchas lecturas en un mensaje puede alcanzar el tamaño máximo de mensaje del broker; consulta el límite de tamaño de mensaje en MQTT.
Formatos de payload en ConnectMQ
ConnectMQ transporta cualquier formato de payload, ya que los payloads MQTT son bytes opacos. Además, ConnectMQ puede decodificar payloads Sparkplug B (Protobuf o JSON) y volver a publicarlos en topics ISA-95 legibles. Los límites de mensaje son de 256 KB en el plan Serverless (Free) y de 1 MB en el plan Serverless (Paid), más que suficiente para cualquiera de las codificaciones de una sola lectura anteriores. Si prefieres no operar la infraestructura, un broker MQTT gestionado te permite centrarte en el contrato del payload. El tutorial de MQTT cubre el resto del protocolo.
Empieza con ConnectMQ
Crea un broker gratuito en app.connectmq.com con el plan Serverless, sin tarjeta de crédito. Publica la misma lectura en JSON y en el formato binario que prefieras, y compáralas lado a lado en el cliente integrado en el navegador del portal de ConnectMQ.
Preguntas frecuentes
¿Cuál es el mejor formato de payload MQTT?
No hay uno mejor sin más. JSON es la opción por defecto más segura para la interoperabilidad y la depuración; Protobuf es lo mejor cuando el ancho de banda es escaso y los esquemas se gestionan bien.
¿Valida el broker el formato del payload?
No. Los brokers MQTT tratan los payloads como bytes opacos. La validación ocurre en tus consumidores.
¿Debería comprimir los payloads JSON?
Para lecturas individuales pequeñas, la compresión suele ahorrar poco y cuesta CPU. Ayuda más en mensajes grandes que agrupan muchas lecturas.
¿Cómo indico a los consumidores qué formato usa un mensaje?
En MQTT 5, establece la propiedad Content Type. En MQTT 3.1.1, usa una convención de topics o un campo de versión dentro del payload.



