Las plantillas Sparkplug (templates) son la forma que tiene Sparkplug B de definir tipos de equipo reutilizables, el equivalente en MQTT de un tipo definido por el usuario (UDT) en un PLC o un sistema SCADA. En esta guía aprenderás cómo funcionan las definiciones e instancias de template, cómo encajan los parámetros, el anidamiento y las versiones, cómo construir una plantilla de bomba en Python y cómo mantener el mismo modelo coherente en muchos edge nodes.
Por qué existen las plantillas
Sin plantillas, una bomba no es más que un conjunto de métricas vagamente relacionadas: Pump1/Speed, Pump1/Running, Pump1/Pressure. Una segunda bomba repite los mismos nombres con otro prefijo, y nada le indica a la aplicación host que ambas son bombas. Si un edge node llama a la métrica Speed y otro la llama RPM, cada consumidor tiene que conocer esa diferencia.
Las plantillas lo resuelven separando el tipo de la instancia. Defines una Pump una sola vez con sus métricas miembro, tipos de datos y valores por defecto. Cada bomba física es entonces una instancia que hace referencia a la definición. Una aplicación host que entiende las plantillas puede crear automáticamente los objetos correspondientes, aplicar las mismas pantallas y alarmas a todas las bombas y detectar cuándo una instancia no coincide con su tipo.
Definiciones e instancias
Tanto las definiciones como las instancias son métricas con tipo de datos 19 (Template) cuyo valor es un mensaje Template:
| Campo del template | Finalidad | En una definición | En una instancia |
|---|---|---|---|
| version | Cadena de versión opcional | Recomendado | Opcional |
| metrics | Métricas miembro | Miembros con tipos de datos y valores por defecto | Miembros con valores actuales |
| parameters | Constantes con nombre y tipo | Declarados con valores por defecto | Sobrescritos por instancia |
| template_ref | Nombre de la definición utilizada | No se establece | Obligatorio, apunta a la definición |
| is_definition | Marca una definición | true | false |
Dónde viven las definiciones
Las definiciones de template se publican en el NBIRTH, antes de cualquier instancia que las use. No se repiten en los mensajes de datos. Algunas plataformas anteponen a los nombres de definición una carpeta como _types_/ para separar los tipos de los tags en vivo; es una convención de la plataforma, no un requisito, así que sigue lo que espere tu aplicación host.
Dónde viven las instancias
Las instancias pueden aparecer en el NBIRTH (equipos a nivel de nodo) o en un DBIRTH (equipos detrás de un dispositivo). Cada instancia lleva en el birth el conjunto completo de métricas miembro con sus valores actuales. Después, los mensajes de datos transportan los cambios.
Un ejemplo decodificado
Esta es una definición de bomba tal como aparece en un NBIRTH tras decodificarla:
{
"name": "_types_/Pump",
"datatype": 19,
"templateValue": {
"version": "1.2",
"isDefinition": true,
"parameters": [
{ "name": "RatedSpeed", "type": 9, "floatValue": 1500.0 }
],
"metrics": [
{ "name": "Running", "datatype": 11, "booleanValue": false },
{ "name": "Speed", "datatype": 9, "floatValue": 0.0 },
{ "name": "DischargePressure", "datatype": 9, "floatValue": 0.0 },
{ "name": "StartCount", "datatype": 8, "longValue": "0" }
]
}
}
Y una instancia de ella en un DBIRTH:
{
"name": "CoolingPump1",
"alias": 20,
"datatype": 19,
"templateValue": {
"templateRef": "_types_/Pump",
"version": "1.2",
"isDefinition": false,
"parameters": [
{ "name": "RatedSpeed", "type": 9, "floatValue": 2900.0 }
],
"metrics": [
{ "name": "Running", "datatype": 11, "booleanValue": true },
{ "name": "Speed", "datatype": 9, "floatValue": 2875.5 },
{ "name": "DischargePressure", "datatype": 9, "floatValue": 3.4 },
{ "name": "StartCount", "datatype": 8, "longValue": "1287" }
]
}
}
La instancia sobrescribe el parámetro RatedSpeed porque esta bomba tiene un motor distinto. Los parámetros son ideales para datos de placa y límites que difieren por activo pero no cambian en tiempo de ejecución.
Construir una plantilla en Python
Este fragmento construye la definición y una instancia usando clases generadas a partir del archivo sparkplug_b.proto de Eclipse Tahu. Nuestro tutorial sobre cómo decodificar Sparkplug B en Python muestra el paso único de generación de código.
import time
import sparkplug_b_pb2 as spb
BOOLEAN, FLOAT, UINT64, TEMPLATE = 11, 9, 8, 19
MEMBERS = [("Running", BOOLEAN), ("Speed", FLOAT),
("DischargePressure", FLOAT), ("StartCount", UINT64)]
def set_value(metric, datatype, value):
metric.datatype = datatype
if datatype == BOOLEAN:
metric.boolean_value = value
elif datatype == FLOAT:
metric.float_value = value
elif datatype == UINT64:
metric.long_value = value
def add_pump_definition(payload):
m = payload.metrics.add()
m.name = "_types_/Pump"
m.datatype = TEMPLATE
t = m.template_value
t.version = "1.2"
t.is_definition = True
p = t.parameters.add()
p.name, p.type, p.float_value = "RatedSpeed", FLOAT, 1500.0
for name, dtype in MEMBERS:
member = t.metrics.add()
member.name = name
set_value(member, dtype, False if dtype == BOOLEAN else 0)
def add_pump_instance(payload, name, alias, rated_speed, values):
m = payload.metrics.add()
m.name = name
m.alias = alias
m.timestamp = int(time.time() * 1000)
m.datatype = TEMPLATE
t = m.template_value
t.template_ref = "_types_/Pump"
t.version = "1.2"
t.is_definition = False
p = t.parameters.add()
p.name, p.type, p.float_value = "RatedSpeed", FLOAT, rated_speed
for name, dtype in MEMBERS:
member = t.metrics.add()
member.name = name
set_value(member, dtype, values[name])
nbirth = spb.Payload(timestamp=int(time.time() * 1000), seq=0)
add_pump_definition(nbirth)
dbirth = spb.Payload(timestamp=int(time.time() * 1000), seq=1)
add_pump_instance(dbirth, "CoolingPump1", 20, 2900.0,
{"Running": True, "Speed": 2875.5,
"DischargePressure": 3.4, "StartCount": 1287})
print(len(nbirth.SerializeToString()), "byte NBIRTH,",
len(dbirth.SerializeToString()), "byte DBIRTH")
En un edge node real, el NBIRTH también contiene bdSeq y Node Control/Rebirth, como se muestra en certificados birth y death de Sparkplug. Publica estos payloads en spBv1.0/{group}/NBIRTH/{node} y spBv1.0/{group}/DBIRTH/{node}/{device}.
Actualizar valores de plantillas en tiempo de ejecución
Los births transportan instancias completas, pero los mensajes de datos deben seguir siendo pequeños. Las plataformas difieren en cómo informan de los cambios en un miembro de un template. Algunas vuelven a enviar la métrica de la instancia en NDATA o DDATA con solo los miembros que han cambiado. Otras exponen cada miembro como su propia métrica con alias tras el birth, de modo que un cambio de velocidad viaja como un único valor escalar. Ambos enfoques dependen del birth para establecer nombres, tipos de datos y alias, y por eso un host que se pierde el birth tampoco puede interpretar las actualizaciones de templates.
Antes de comprometerte con un diseño, publica un birth y algunos mensajes de datos desde tu plataforma edge y observa el resultado decodificado. Confirma que tu aplicación host actualiza el miembro correcto de la instancia, que los miembros sin cambios conservan sus valores y que un rebirth restaura la instancia completa. Hacerlo con dos o tres bombas en un grupo de pruebas lleva minutos y evita descubrir una discrepancia después de haber configurado cientos de activos.
Anidamiento y composición
Una métrica miembro de un template puede ser a su vez un template. Un template Skid podría contener dos instancias de Pump, una instancia de Tank y algunas métricas escalares. La composición mantiene los modelos legibles y te permite reutilizar tipos pequeños y bien probados.
Mantén el anidamiento poco profundo. Cada host debe resolver cada nivel, y las jerarquías profundas hacen que los births sean grandes. Los births grandes importan en infraestructura compartida: el servidor MQTT público gratuito, pensado solo para pruebas, acepta mensajes de hasta 64 KB y cada plan tiene su propio límite de tamaño de mensaje. Si un nodo modela cientos de activos, comprueba pronto el tamaño del birth.
Mantener la coherencia de las plantillas entre edge nodes
Las plantillas solo compensan cuando todos los edge nodes usan la misma definición. En la práctica, las divergencias aparecen cuando los nodos los configuran personas distintas o se actualizan en momentos distintos. Estos hábitos lo evitan:
- Mantén una biblioteca central de definiciones bajo control de versiones y genera a partir de ella la configuración de los edge nodes.
- Rellena el campo
versione increméntalo con cada cambio de miembros, tipos de datos o parámetros. - No cambies nunca el tipo de datos de un miembro sobre la marcha; añade un miembro nuevo o una versión nueva.
- Usa el mismo nombre de definición en todas partes, incluidas mayúsculas y minúsculas.
- Lleva las diferencias por activo a los parámetros, no a miembros adicionales.
- Tras el despliegue, compara las definiciones del NBIRTH decodificado de cada nodo con la biblioteca.
- Despliega las versiones nuevas nodo a nodo y confirma que el host acepta ambas durante la transición.
En el paso 6 es donde ayuda una vista decodificada. ConnectMQ decodifica Sparkplug B en el explorador de topics de su portal, así que puedes abrir el NBIRTH de cada nodo y comparar definiciones sin escribir un script. Ten en cuenta que muestra los births cuando se publican; si un nodo ya está online, solicita un rebirth para verlos.
Problemas habituales
| Síntoma | Causa probable | Solución |
|---|---|---|
| El host muestra los miembros de la instancia como desconocidos | Falta la definición en el NBIRTH | Publica las definiciones en el NBIRTH antes que las instancias |
| Instancia rechazada o mostrada parcialmente | template_ref no coincide con el nombre de la definición | Nombre exacto, distingue mayúsculas |
| El mismo tipo se comporta distinto en cada nodo | Las definiciones han divergido entre nodos | Biblioteca central y comprobación de versiones |
| Births demasiado grandes | Muchas instancias muy anidadas | Aplana, divide dispositivos o revisa los límites del plan |
| Algunos consumidores ignoran las plantillas | La herramienta no admite el tipo de datos 19 | Usa un host que admita templates o aplana para ese consumidor |
Plantillas, ISA-95 y el Unified Namespace
Las plantillas modelan qué es un equipo; ISA-95 modela dónde está. Ambos se complementan: un tipo Pump, instanciado bajo planta, área y línea. ConnectMQ puede republicar datos Sparkplug en topics ISA-95 como connectmq/factory1/production/line1/cnc1/telemetry, lo que ofrece a los consumidores que no usan Sparkplug una vista legible basada en la ubicación. Para saber más sobre cómo estructurar los datos de planta, consulta MQTT, Sparkplug B y el Unified Namespace, la referencia de tipos de datos de Sparkplug B y la guía principal sobre IoT industrial con MQTT.
Empieza con ConnectMQ
Ejecuta tus edge nodes basados en plantillas en un broker MQTT gestionado sin operar tú mismo la infraestructura, o haz primero un prototipo en el servidor MQTT público gratuito con un group ID aleatorio. Crea un broker Serverless gratuito en app.connectmq.com, sin tarjeta de crédito, e inspecciona tus definiciones e instancias de template decodificadas en el portal.
Preguntas frecuentes
¿Las plantillas Sparkplug son lo mismo que los UDT de un PLC?
Cumplen la misma función: un tipo reutilizable con miembros con nombre y tipo. Muchas plataformas edge asignan directamente los UDT de PLC o SCADA a templates de Sparkplug.
¿Dónde deben publicarse las definiciones de template?
En el NBIRTH, antes de cualquier instancia que haga referencia a ellas. Después, las instancias pueden aparecer en el NBIRTH o en mensajes DBIRTH.
¿Para qué sirven los parámetros de un template?
Los parámetros contienen constantes por instancia, como la velocidad nominal, límites o datos de placa. La definición proporciona los valores por defecto y cada instancia puede sobrescribirlos.
¿Todas las aplicaciones host de Sparkplug admiten plantillas?
No. El soporte varía, así que comprueba tu host antes de modelarlo todo como templates. Para consumidores sin soporte, aplana los miembros en métricas normales o usa una vista legible republicada en ISA-95.



