Asesoría Agenda una llamada de descubrimiento técnico hoy mismo »

· Eduardo Vieira · Industrial Protocols  · 11 min de lectura

Diseño de Payloads MQTT IIoT: JSON vs Sparkplug B

Arquitectar el formato de tus mensajes es tan importante como el protocolo. Guía profunda sobre eficiencia binaria, estados y estandarización con Sparkplug B.

Arquitectar el formato de tus mensajes es tan importante como el protocolo. Guía profunda sobre eficiencia binaria, estados y estandarización con Sparkplug B.

Un tópico MQTT responde dónde se enruta un mensaje; el payload debe responder qué significan sus bytes, cuándo se observó la medición y si un consumidor puede utilizarla. MQTT 5.0 deja deliberadamente abierto ese contrato de aplicación. Ofrece metadatos de PUBLISH como Payload Format Indicator, Content Type y User Properties, pero no define un objeto JSON para IIoT, un sistema de unidades ni un modelo de calidad [C-A-S-003] [C-A-P-002]. Por eso, un diseño durable empieza por la semántica antes de elegir una serialización.

Este artículo delimita la capa de payload: sobres, valores escalares y estructurados, marcas de tiempo, calidad, unidades, validación y control de cambios. Compara JSON con un vector acotado de Sparkplug/Protobuf; no afirma un resultado universal sobre bytes, rendimiento, seguridad o interoperabilidad. El espacio de nombres, los certificados de nacimiento y muerte, el estado retenido y las reglas de mensajes spBv1.0 corresponden al artículo sobre el ciclo de vida de Sparkplug. Consúltelo cuando el sistema requiera ese protocolo de aplicación y no solamente un contrato JSON definido.

Empezar con un sobre que el receptor pueda interpretar

Un sobre separa el contexto de transporte de la medición. Debe permitir que el receptor identifique el esquema, identifique la fuente productora, determine el instante de la medición y decida si el valor es utilizable. La forma mínima depende del sistema, pero cada campo necesita una definición inequívoca. Por ejemplo:

{
  "schema": "com.example.iiot.telemetry/1",
  "source": "plant-a/line-2/press-07",
  "observedAt": "2023-11-14T22:13:20.000Z",
  "sequence": 7,
  "metrics": {
    "temperature": { "value": 24.5, "type": "number", "unit": "Cel", "quality": "good" }
  }
}

schema es un identificador de aplicación, no una función de MQTT. source identifica el contrato productor y no debe considerarse confiable solo por aparecer en el cuerpo: la autorización del despliegue debe vincular la identidad del publicador con las combinaciones permitidas de tópico y fuente. observedAt es el momento en que la fuente observó el valor; la hora de recepción en el broker es un hecho operativo distinto. sequence puede ayudar a detectar huecos o duplicados dentro de una época de un productor, pero no es un identificador de evento ordenado globalmente.

Los campos deben ser ortogonales. No sobrecargue timestamp para muestreo y carga, ni status para comunicación, validez y alarma. Si una pasarela necesita ambos tiempos, publique observedAt e ingestedAt con reloj y precisión documentados. Un ISO 8601 UTC es legible; un epoch puede ser compacto, pero debe declarar unidad y rango. Ambos requieren política de reloj y datos fuera de orden.

Conservar tipo, unidad y calidad como datos

Un valor de telemetría escalar es una medición con un valor: temperatura, posición de válvula o contador. Un valor estructurado representa un objeto coherente: un espectro de vibración, aceleración en tres ejes, un resultado de lote o una instantánea de configuración. No aplane telemetría estructurada en nombres inventados como axis_1_42 cuando la forma importa para los consumidores. A la inversa, no envuelva cada escalar en un objeto grande si un contrato escalar documentado es suficiente.

JSON solo tiene los tipos number, string, boolean, null, array y object. Un número JSON no distingue un entero de 32 bits de un contador de 64 bits, una cantidad decimal de una aproximación de coma flotante o un código que parece numérico. Un consumidor JavaScript puede perder precisión por encima de su rango de entero seguro; otro decodificador puede conservar un entero más amplio. Para identificadores, secuencias con rango definido, valores equivalentes a dinero y cantidades que exigen decimales exactos, indique explícitamente la representación y pruebe las bibliotecas reales de productores y consumidores. Un decimal entre comillas puede transferirse con más seguridad cuando se necesita exactitud, pero entonces los consumidores requieren reglas de validación y conversión.

El campo type del ejemplo hace visible el significado pretendido, aunque un esquema también puede hacerlo implícito. Elija un enfoque y documéntelo. Un valor numérico de ingeniería necesita una unidad, como Cel, kPa, rpm o un código gobernado localmente. Una clave llamada temperature no basta: distintos componentes pueden usar legítimamente Celsius, Fahrenheit o un conteo bruto de sensor. Adopte una convención de nombres que describa la magnitud y no la etiqueta visual, publique la unidad junto al valor o en un esquema inmutable y defina quién es responsable de convertir. No convierta silenciosamente en varios saltos.

La calidad responde una pregunta distinta del tipo. good, uncertain y bad solo son útiles si se documentan sus significados y transiciones. Una calidad puede señalar fallo de sensor, dato sustituido, timeout de comunicación o una comprobación fuera de rango, pero esas situaciones no son intercambiables. Incluya un código de motivo opcional y acotado cuando operaciones necesite diagnóstico; no exponga trazas de pila, credenciales, datos personales ni errores de dispositivos sin restricción en un payload de telemetría. Los consumidores deben decidir conscientemente si un valor cuya calidad no es buena actualiza una pantalla, un historiador, un cálculo o un flujo cercano al control.

Distinguir ausencia, null y datos obsoletos

Una métrica ausente, null y un valor obsoleto son tres señales diferentes. Una métrica faltante puede significar que esa versión del productor no la admite, que esta actualización no la modifica o que el productor la omitió de forma incorrecta. null puede significar «conocida pero no disponible», pero únicamente si el esquema reserva ese significado de manera explícita. Un dato obsoleto suele indicar que el último valor era válido al observarse, pero ya superó el presupuesto de frescura de la aplicación receptora. Debe derivarse de observedAt, del tiempo de recepción y de un timeout documentado, no de un cero o una cadena vacía arbitrarios.

En un tópico de actualizaciones parciales, la omisión puede significar correctamente «sin cambios». En un tópico de instantáneas, puede significar «no está presente en esta instantánea». Ambos contratos no pueden compartir una única regla de decodificación. Nombre el modo del mensaje o el esquema de acuerdo con ello y mantenga el estado operativo retenido separado de una secuencia de eventos. Una instantánea retenida puede ayudar a un suscriptor tardío a conocer la última representación publicada; no demuestra que la medición siga siendo fresca. Un evento registra algo que ocurrió una vez y debe incluir identificador de evento, tiempo de ocurrencia y una política de retención apropiada. Un mensaje de estado representa la condición conocida más reciente y necesita vencimiento o tratamiento de frescura.

Esta distinción evita un fallo común: tratar un documento JSON retenido y entregado por el broker como verdad viva del proceso. El receptor debe aplicar su propia regla de frescura incluso si QoS entregó el mensaje correctamente. QoS MQTT gobierna una interacción de entrega entre cliente y broker; no certifica la validez del sensor ni crea procesamiento de negocio exactamente una vez. Diseñe el tratamiento de duplicados y de datos obsoletos en el límite de la aplicación.

Evolucionar esquemas sin obligar a adivinar a consumidores antiguos

Versione el contrato desde el primer mensaje. Una versión mayor en schema, como com.example.iiot.telemetry/1, ofrece a los consumidores un límite de compatibilidad claro. Los campos opcionales aditivos pueden ser compatibles solo si los consumidores antiguos ignoran campos desconocidos y los nuevos tienen valores predeterminados para los ausentes. Renombrar una clave, cambiar su unidad, cambiar un escalar por un objeto, cambiar un número por una cadena o dar un significado nuevo a null es un cambio semántico aunque el JSON continúe analizando. Publique un esquema o tópico mayor nuevo y sostenga una ventana de migración deliberada.

La canonicalización es igualmente importante cuando un payload se firma, se hashea, se deduplica o se compara en pruebas. El orden de miembros de un objeto JSON no es semántico, los espacios en blanco no son una identidad confiable y el formato numérico puede variar entre codificadores. Defina el algoritmo exacto de canonicalización antes de usar una secuencia de bytes JSON como identificador. De otro modo, hashee una representación estable de la aplicación en vez de pretender que bytes JSON arbitrarios son equivalentes. El comprobante determinista de más abajo usa claves ordenadas y separadores compactos para este único fixture; no establece una norma de canonicalización para producción.

Mantenga un registro de esquemas o un repositorio versionado donde productores y consumidores puedan inspeccionar campos requeridos, tipos permitidos, rangos, reglas de unidad, nulabilidad, ejemplos y fechas de deprecación. Un documento de esquema legible por personas es útil, pero un validador aplicado por máquina en el límite de confianza es el que rechaza entradas malformadas antes de que entren en un historiador, tablero o flujo cercano al control.

Valide por etapas. Primero rechace un mensaje cuyo tópico MQTT, publicador autenticado, Content Type o esquema declarado no esté permitido para esa entrada. Después aplique límites económicos de bytes y profundidad antes de analizar JSON, valide la estructura analizada contra el esquema seleccionado y finalmente ejecute controles semánticos como unidades permitidas, rangos plausibles, contadores monotónicos y frescura específica de la fuente. Los fallos de validación deben producir un código de motivo y una métrica acotados, no un volcado de excepción que contenga el payload. Un mensaje rechazado no debe sobrescribir el último estado bueno; ponerlo en cuarentena, descartarlo o reintentarlo es una política operativa explícita.

Medir un vector; no generalizarlo

JSON repite nombres y puntuación, mientras que Protobuf codifica campos con etiquetas numéricas y valores de cable tipados. Esa diferencia puede importar en enlaces restringidos, pero el resultado depende de nombres de campos, valores, metadatos opcionales, agrupamiento, compresión y del codificador real. El siguiente comprobante autocontenido compara exactamente un objeto JSON compacto con un vector construido manualmente a partir del esquema Eclipse Tahu fijado. Es un cálculo de bytes fuera de línea; no es una prueba de broker ni de dispositivo [C-A-P-001] [C-A-P-003].

Entorno: Python 3.14.6; fecha: 2026-07-22; comprobante: V-A-P-001 [V-A-P-001].

python3 - <<'PY'
import hashlib,json,struct
def v(n):
 b=bytearray()
 while n>127:b.append((n&127)|128);n>>=7
 b.append(n);return bytes(b)
def out(n,b,h=False):print(f'{n} bytes={len(b)}'+(f' hex={b.hex()}' if h else '')+f' sha256={hashlib.sha256(b).hexdigest()}')
j=json.dumps({'metrics':[{'name':'temp_c','type':'Double','value':24.5}],'seq':7,'timestamp':1700000000000},sort_keys=True,separators=(',',':')).encode()
m=b'\x0a'+v(6)+b'temp_c'+b'\x20'+v(10)+b'\x69'+struct.pack('<d',24.5)
s=b'\x08'+v(1700000000000)+b'\x12'+v(len(m))+m+b'\x18'+v(7)
out('json',j);out('sparkplug',s,True)
PY

Salida esperada y observada, en dos ejecuciones idénticas:

json bytes=94 sha256=b8e0e19bcc6ba98c5eb6c7b1a2b20b50f101e824f6fffedbd9caf0409a24462b
sparkplug bytes=30 hex=0880d095ffbc3112130a0674656d705f63200a6900000000008038401807 sha256=c06d68e146bab6ab68b7fc2bd2d7669f7b0cc20e4ab1d33f32496858ef97f036

El SHA-256 de estas dos líneas de stdout sin su salto de línea final es fe1690d6f555caf6b6c7ad886bfb97ea7fb2ef236943c06be94930d88074246f. El comprobante solo demuestra su entrada y lógica de codificación declaradas. Agregar unidad, calidad, identificadores, métricas repetidas, otra escritura JSON o una biblioteca Sparkplug generada cambia los bytes. La compresión puede reducir datos repetidos en algunos recorridos de transporte, pero implica compensaciones de CPU, latencia, observabilidad y compatibilidad; mida el sistema negociado en vez de superponer compresión por defecto.

Agrupar deliberadamente y conservar los límites del mensaje

Agrupar mediciones amortiza tópico y framing, pero aumenta la unidad de pérdida, memoria y latencia. Un lote debe declarar tiempos, orden, duplicados y si admite subconjuntos válidos. Limite elementos, bytes, anidamiento, cadenas y tiempo antes de analizar datos no confiables. Rechace o ponga en cuarentena entradas malformadas con diagnósticos acotados; no registre payloads completos con información sensible. La revisión de privacidad debe definir clasificación, retención y acceso.

La compresión es independiente de la serialización. Puede ayudar con datos grandes y repetitivos; mensajes pequeños quizá no compensen cabeceras y complejidad. Dificulta la inspección y puede agotar recursos sin límites. Imponga techos comprimidos y descomprimidos, autentique y autorice publicadores, use TLS y revise permisos. La validación no reemplaza la autorización, ni a la inversa.

Elegir un protocolo y mantener el límite observable

JSON suele ser adecuado cuando los equipos necesitan mensajes inspeccionables, herramientas ampliamente disponibles y un contrato que puedan validar con claridad. Un protocolo binario estructurado puede ser apropiado cuando su esquema, implementaciones generadas y semántica de ciclo de vida coinciden con el sistema participante. Sparkplug añade su propio espacio de nombres y modelo de ciclo de vida acordados; este artículo no repite esas reglas. La decisión útil no es «JSON frente a binario» en abstracto, sino un contrato documentado cuyos consumidores puedan rechazar con seguridad, evolucionar de forma previsible y diagnosticar en producción.

En cada receptor, registre metadatos acotados: versión de esquema, identidad de fuente, tamaño de payload, resultado de validación, decisión de frescura y un valor de correlación o secuencia cuando esté permitido. Evite usar registros de payload bruto como almacén de diagnóstico predeterminado. Cree adaptadores de migración que lean el esquema antiguo, lo validen, emitan el esquema nuevo y expongan un conteo de registros rechazados o ambiguos. Ejecute ambas rutas contra fixtures representativos antes de retirar la anterior; no cambie la etiqueta de un tópico ni modifique una unidad en el lugar.

Referencias y verificación

  • [C-A-S-003] OASIS, MQTT Version 5.0, publicado 2019-03-07.
  • [C-A-P-001] Eclipse Tahu, sparkplug_b.proto fijado, commit 5736e404889d4b95910613040a99ba79589ffb13.
  • [C-A-P-002] El límite de sobre de payload se deriva del contrato PUBLISH de MQTT 5.0 anterior; los nombres JSON, unidades, reglas de calidad y evolución siguen siendo contratos de aplicación.
  • [C-A-P-003] V-A-P-001, vector local determinista únicamente; no concluye sobre rendimiento, compatibilidad, seguridad, broker ni tamaño universal.
  • [V-A-P-001] Comprobante local determinista exacto anterior; Python 3.14.6; ejecutado dos veces el 2026-07-22.
  • [C-A-S-001] Eclipse Sparkplug, Specification 3.0, votación de ratificación concluida 2022-10-21.

Recibos de investigación de fuentes primarias: solicitud Tavily sobre el límite de payload 773f3b12-f6bf-48fc-a33e-07571c34369c y solicitud sobre el límite de Sparkplug acb84e15-ec4a-40f7-9486-000e54a1b296, consultadas 2026-07-22. Los intentos de Context7 para Eclipse Paho MQTT Python y Eclipse Tahu devolvieron Monthly quota exceeded. Create a free API key at https://context7.com/dashboard for more requests. En su lugar se utilizan las fuentes oficiales de OASIS, Eclipse Sparkplug y Eclipse Tahu fijadas.

Última verificación: 2026-07-22.

Volver al blog

Related Posts

View All Posts »
MQTT Sparkplug B: Hablando la Lingua Franca del IIoT

MQTT Sparkplug B: Hablando la Lingua Franca del IIoT

Para sistemas industriales donde las aplicaciones participantes necesitan un espacio de nombres y un modelo de ciclo de vida compartidos, conozca cómo Sparkplug B puede aportar una capa de interoperabilidad respaldada por evidencia en 2026.