Fundamentos de MQTT

Formatos de payload MQTT: JSON vs Protobuf vs CBOR

A MQTT no le importa lo que hay dentro de un payload, así que la elección es tuya. Compara JSON, Protobuf y CBOR en tamaño, esquemas, depuración y herramientas, con el recuento de bytes de una misma lectura de sensor.

La misma lectura de sensor codificada en JSON, Protobuf y CBOR, comparando tamaños de formato de payload MQTT
Pruébalo en tu propio broker MQTTConsigue un broker MQTT privado y gestionado con el plan Free y empieza con ejemplos de código listos para usar.
Pruébalo gratis Ejemplos de código Sin tarjeta

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/json o application/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

  1. ¿Van a leer personas estos mensajes con frecuencia? Prefiere JSON.
  2. ¿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.
  3. ¿Muchos equipos y lenguajes consumen los mismos datos? Los esquemas de Protobuf compensan; si no, JSON con un esquema documentado.
  4. ¿Necesitas blobs binarios dentro de datos estructurados? CBOR los gestiona de forma nativa.
  5. ¿Integras con herramientas comerciales estándar? JSON, o planifica un paso de decodificación que vuelva a publicar en JSON.
  6. ¿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.

Artículos relacionados

¿Quién inventó MQTT? La historia, de los oleoductos al IoT

MQTT lo inventaron en 1999 Andy Stanford-Clark, de IBM, y Arlen Nipper, de Arcom, para monitorizar oleoductos vía satélite. Sigue su camino hacia el open source, OASIS, ISO y los brokers MQTT gestionados en la nube de hoy.

8 min de lectura

Por qué tu cliente MQTT se desconecta constantemente (y cómo solucionarlo)

Los bucles de reconexión son uno de los problemas más habituales de MQTT. Conoce las cinco causas típicas, desde los client ID duplicados hasta los timeouts de keep alive y los paquetes demasiado grandes, y sigue un flujo de diagnóstico que localiza la tuya.

9 min de lectura

Todos los artículos →

Tu broker MQTT privado, a unos pocos clics

Crea una cuenta, obtén las credenciales de tu broker y conecta tu primer dispositivo, sin servidores que instalar, parchear ni escalar.