Los topics MQTT ISA-95 llevan la jerarquía de equipos de la planta directamente a la ruta del topic, de modo que acme/dallas/packaging/line-02/filler/telemetry indica a cada suscriptor exactamente de dónde procede un valor. En esta guía aprenderás a codificar cada nivel ISA-95, a diseñar la hoja del topic, a planificar las suscripciones con comodín antes de necesitarlas y qué errores provocan más retrabajo cuando un namespace ya está en producción.
Por qué codificar ISA-95 en los topics MQTT
Los topics MQTT son cadenas libres separadas por barras, y el broker MQTT no les atribuye ningún significado más allá de emparejar suscripciones. Esa libertad es peligrosa a escala de planta, ya sea una embotelladora en Bogotá o una planta de envases en Bilbao. Si cada integrador inventa una estructura, los consumidores acaban con una tabla de correspondencias por cada fuente.
La jerarquía de equipos ISA-95 lo resuelve aportando una estructura que ya existe en la documentación de la planta, el MES y el sistema de mantenimiento. Codificarla en los topics te da tres propiedades gratis:
- Descubribilidad. Cualquiera que conozca la planta puede encontrar un valor navegando por el árbol.
- Contexto. El topic lleva la dirección del activo, así que los payloads no necesitan repetirla.
- Suscripción selectiva. Los comodines se corresponden de forma natural con ámbitos organizativos: un sitio, un área, todas las llenadoras.
Si necesitas repasar los niveles, lee primero la guía sobre la jerarquía de equipos ISA-95.
El patrón básico
El patrón canónico coloca los niveles del más amplio al más concreto, seguidos de un namespace que indica qué tipo de datos contiene el mensaje:
<enterprise>/<site>/<area>/<work center>/<work unit>/<namespace>[/<detail>]
acme/dallas/packaging/line-02/filler/telemetry
acme/dallas/packaging/line-02/filler/state
acme/dallas/packaging/line-02/filler/alarms
acme/dallas/packaging/line-02/filler/events/batch-complete
acme/dallas/packaging/line-02/filler/cmd/stop
Reglas para cada nivel
| Nivel | Regla | Ejemplo |
|---|---|---|
| Empresa | Un nombre corto y estable; omítelo solo si el broker sirve a una única compañía | acme |
| Sitio | Código de ubicación que sobreviva a las reorganizaciones | dallas, izmir |
| Área | Área de función o de producto | packaging, utilities |
| Centro de trabajo | Línea, celda, celda de proceso, unidad o zona de almacenamiento | line-02, cell-03 |
| Unidad de trabajo | Máquina o unidad, idealmente por etiqueta de activo | filler, cnc-01 |
| Namespace | Vocabulario fijo | telemetry, state, alarms, events, cmd |
Mantén fija la profundidad de los topics de equipo. Cuando todas las unidades de trabajo están en el nivel cinco, +/+/+/+/+/state devuelve el estado de todas las máquinas de la empresa y la posición de cada nivel en la ruta está garantizada.
Diseñar la hoja
El namespace y lo que viene después es donde se producen la mayoría de los debates de diseño. Hay dos opciones principales para la telemetría.
Un objeto por equipo. Todos los valores actuales de una máquina van en un único documento JSON en .../filler/telemetry. Es compacto, mantiene coherentes en el tiempo los valores relacionados y solo necesita un retained message por máquina.
{ "ts": "2026-10-16T08:15:02Z", "speed_bpm": 118.4, "temp_c": 41.2, "good_count": 18342, "v": 1 }
Un topic por métrica. Cada valor tiene su propio topic, como .../filler/telemetry/speed_bpm, con un payload escalar o un JSON pequeño. Se corresponde de forma natural con herramientas basadas en tags, como los sistemas SCADA, y los consumidores pueden suscribirse exactamente a las señales que necesitan.
Ambas son válidas. Elige por namespace y documéntalo. Un híbrido habitual es un objeto para la telemetría y valores retenidos por topic para state y unos pocos KPI clave.
Planifica tus suscripciones con comodín
Antes de cerrar la estructura, anota las suscripciones que necesitarán tus consumidores y comprueba que cada una sea un único filtro con comodín:
| Consumidor | Necesita | Suscripción |
|---|---|---|
| Dashboard del sitio | Todo lo de un sitio | acme/dallas/# |
| Supervisor de envasado | Todos los estados de un área | acme/dallas/packaging/+/+/state |
| Analítica de flota | Todas las llenadoras, en todas partes | acme/+/+/+/filler/telemetry |
| Servicio de alarmas | Todas las alarmas | acme/+/+/+/+/alarms |
| Historian | Toda la telemetría | acme/+/+/+/+/telemetry |
Si un ámbito necesario requiere varias suscripciones o filtrado en el cliente, la estructura está jugando en tu contra. Normalmente significa que falta un nivel, que el orden es incoherente o que el vocabulario de la hoja es demasiado laxo.
Publicar topics ISA-95 desde código
Generar los topics en un solo lugar evita que las erratas creen ramas descontroladas. Este helper de Python construye y valida la ruta y después publica el estado y la telemetría retenidos. Instala el cliente con pip install "paho-mqtt>=2.0". Usa el servidor MQTT público gratuito con un prefijo de demo; en un broker privado, asigna a PREFIX el nombre de tu empresa.
import json
import re
import threading
from dataclasses import dataclass
from datetime import datetime, timezone
import paho.mqtt.client as mqtt
PREFIX = "connectmq/demo/r8t5" # en tu propio broker: "acme"
SAFE = re.compile(r"^[a-z0-9]+(-[a-z0-9]+)*$")
NAMESPACES = {"telemetry", "state", "alarms", "events", "cmd"}
@dataclass(frozen=True)
class Asset:
site: str
area: str
work_center: str
work_unit: str
def topic(self, namespace: str) -> str:
for level in (self.site, self.area, self.work_center, self.work_unit):
if not SAFE.match(level):
raise ValueError(f"invalid level: {level!r}")
if namespace not in NAMESPACES:
raise ValueError(f"unknown namespace: {namespace!r}")
return f"{PREFIX}/{self.site}/{self.area}/{self.work_center}/{self.work_unit}/{namespace}"
filler = Asset("dallas", "packaging", "line-02", "filler")
connected = threading.Event()
def on_connect(client, userdata, flags, reason_code, properties):
print("connected:", reason_code)
connected.set()
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="isa95-pub-r8t5")
client.on_connect = on_connect
client.connect("broker.connectmq.com", 1883, keepalive=60)
client.loop_start()
connected.wait(timeout=10)
client.publish(filler.topic("state"), "running", qos=1, retain=True).wait_for_publish()
telemetry = {"ts": datetime.now(timezone.utc).isoformat(), "speed_bpm": 118.4, "temp_c": 41.2, "v": 1}
client.publish(filler.topic("telemetry"), json.dumps(telemetry), qos=1, retain=True).wait_for_publish()
print("published to", filler.topic("telemetry"))
client.loop_stop()
client.disconnect()
Verifica con un comodín que coincida con la tabla anterior:
mosquitto_sub -h broker.connectmq.com -p 1883 -i isa95-sub-r8t5 -t "connectmq/demo/r8t5/+/packaging/+/+/state" -v
El servidor público, pensado solo para pruebas, es legible por cualquiera y admite 8 niveles de topic, por eso aquí el prefijo de demo sustituye al nivel de empresa. Reserva los nombres y valores reales de los activos para un broker privado.
Integrar Sparkplug B en el árbol ISA-95
Los topics Sparkplug B siguen un formato fijo, spBv1.0/<group>/<type>/<edge node>/<device>, con payloads Protobuf. No siguen ISA-95, y los consumidores que no entienden Sparkplug no pueden leerlos. En lugar de abandonar Sparkplug en el edge, republica sus datos en tu árbol ISA-95.
ConnectMQ lo hace automáticamente: decodifica los payloads Sparkplug B, Protobuf o JSON, y los republica en topics ISA-95. Por ejemplo, spBv1.0/factory1/DDATA/line1/cnc1 se republica como connectmq/factory1/production/line1/cnc1/telemetry. Elige IDs de grupo y de nodo edge Sparkplug que contengan nombres de planta con sentido para que las rutas ISA-95 resultantes sean legibles. Las ventajas e inconvenientes de ambos enfoques se analizan en MQTT, Sparkplug B y el Unified Namespace.
Errores que conviene evitar
- Barra inicial.
/acme/dallascrea un primer nivel vacío y divide las suscripciones. - Mayúsculas y minúsculas mezcladas. Los topics distinguen mayúsculas;
Line-02yline-02son ramas distintas. - Nombres de protocolo o fabricante en la ruta.
acme/dallas/opcua/...acopla a los consumidores a la tecnología de integración actual. - Profundidad variable. Saltarse un nivel para una máquina rompe los comodines posicionales. Usa en su lugar un centro de trabajo comodín.
- Timestamps o IDs en los topics. Topics como
.../telemetry/20261016crean ramas ilimitadas y acumulan retained messages. - Retener eventos. Retén el estado, no los flujos de eventos; un nuevo suscriptor no debería recibir un "batch complete" obsoleto.
- Comandos sin petición y respuesta. Usa los response topics y los correlation data de MQTT 5 para
cmd, de modo que quien envía sepa si el comando tuvo éxito. - Rutas profundas en enlaces limitados. Los topics largos añaden bytes a cada mensaje. Los topic aliases de MQTT 5 reducen esa sobrecarga sin cambiar el diseño.
Documenta el estándar antes del despliegue
Un estándar de topics que solo vive en la cabeza de un ingeniero lo romperá el segundo integrador. Antes de que el primer gateway entre en servicio, redacta un estándar de una página que cubra:
- El orden de los niveles y los caracteres permitidos en cada uno.
- La lista de namespaces y lo que puede contener cada uno, con un payload de ejemplo.
- Qué topics son retenidos y cuáles no.
- El formato de timestamp, la zona horaria y la convención de unidades.
- Quién aprueba nuevos sitios, áreas y centros de trabajo, y cómo se solicitan.
Entrega esta página a cada integrador de sistemas y proveedor de gateways junto con el helper de topics anterior o su equivalente en su lenguaje. Cuando el estándar y el código coinciden, la mayoría de los errores se detectan antes de llegar al broker.
La seguridad merece su propia nota: emite una credencial por gateway y aplicación para que cada publicador sea identificable y revocable, y usa TLS en el puerto 8883 en todo lo que no sea el laboratorio. Nuestras buenas prácticas de seguridad MQTT cubren el resto.
Para árboles completos listos para copiar, consulta estructura de topics UNS. Para la arquitectura que los rodea, la guía de MQTT para IoT industrial es el punto de partida.
Ejecuta topics MQTT ISA-95 en ConnectMQ
ConnectMQ es un broker MQTT gestionado que entiende las estructuras de topics industriales: detecta topics ISA-95 y de Unified Namespace, muestra los dispositivos por nivel ISA-95 en el portal y republica Sparkplug B en tu árbol. Crea un broker en el plan gratuito Serverless en app.connectmq.com, sin tarjeta de crédito, y publica hoy tu primera rama ISA-95.
Preguntas frecuentes
¿Debe ser el nombre de la empresa el primer nivel del topic?
Sí, si el broker puede llegar a servir a más de una compañía o unidad de negocio. En un broker de una sola compañía puedes omitirlo, pero añadirlo más tarde implica renombrar todos los topics.
¿Cómo gestiono las máquinas que no pertenecen a una línea?
Asígnales un centro de trabajo comodín como standalone o shared para que queden a la misma profundidad que todo lo demás.
¿Puedo usar espacios o mayúsculas en los topics MQTT ISA-95?
MQTT los permite, pero provocan errores sutiles. Usa solo letras minúsculas, dígitos y guiones.
¿Funcionan los topics ISA-95 con MQTT 3.1.1?
Sí. El diseño de topics es independiente de la versión. MQTT 5 añade extras útiles, como los topic aliases y los response topics para comandos.



