MTConnect a MQTT es el camino más corto desde los datos estandarizados de una máquina CNC hasta dashboards en vivo, alertas e informes de utilización. En este tutorial verás cómo expone los datos un agente MTConnect y después construirás un puente en Python que sigue los números de secuencia del agente sin perder observaciones, mapea data items como el estado de ejecución, la velocidad del husillo y el contador de piezas a topics legibles, y los publica por MQTT. El mismo puente sirve para una máquina o para una célula entera.
Cómo expone MTConnect los datos de la máquina
MTConnect es un estándar abierto para equipos de fabricación, especialmente máquinas herramienta. Define un vocabulario (qué significan "Execution", "SpindleSpeed" o "PartCount") y una interfaz HTTP de solo lectura servida por un agente. Las máquinas hablan con el agente a través de un adaptador, a menudo suministrado por el fabricante del control o integrado en él, y los clientes leen del agente.
El agente ofrece tres peticiones principales:
| Petición | Devuelve | Úsala para |
|---|---|---|
/probe |
El modelo del dispositivo: componentes y data items con IDs, tipos y unidades | Construir un catálogo de data items una vez al arrancar |
/current |
El último valor de cada data item, más la información de secuencia de la cabecera | Estado inicial y resincronización tras errores |
/sample?from=N&count=M |
Todas las observaciones a partir de la secuencia N, hasta M elementos | Seguir los cambios sin perder ninguno |
Cada observación lleva un número de sequence asignado por el agente, un timestamp y un dataItemId. La cabecera de la respuesta contiene firstSequence, lastSequence, nextSequence y un instanceId. Esos cuatro valores son la clave de un puente fiable: empiezas desde /current, recuerdas nextSequence y pides muestras a partir de ahí.
Las observaciones MTConnect se dividen en tres categorías, que encajan perfectamente con cómo debes tratarlas en MQTT:
- Samples son mediciones numéricas, como la velocidad del husillo, la carga o la posición.
- Events son estados o valores discretos, como el estado de ejecución, el modo del controlador, el nombre del programa o el contador de piezas.
- Conditions describen la salud:
Normal,Warning,FaultoUnavailable, a menudo con un código de alarma nativo.
Un data item cuyo valor se desconoce informa UNAVAILABLE. Tu puente debe transmitirlo, no ocultarlo, porque "la máquina está apagada o desconectada" es información importante.
Por qué hacer de puente entre MTConnect y MQTT
MTConnect funciona por consulta (pull) y usa mucho XML. Eso está bien para un dashboard, pero cada nuevo consumidor sondea al agente por separado y tiene que parsear XML. Hacer de puente hacia MQTT te da entrega push, un único formato JSON cómodo para los consumidores, estado actual retenido para quienes se incorporan tarde y la posibilidad de combinar los datos de máquina con datos de PLC, energía y calidad en una sola jerarquía de topics. Si estás construyendo un Unified Namespace, las máquinas MTConnect se convierten en una fuente más que lo alimenta; nuestra guía de topics MQTT ISA-95 muestra cómo ubicarlas.
Las versiones recientes del agente de referencia de MTConnect también pueden publicar directamente en un broker MQTT mediante un sink MQTT integrado. Si tu versión del agente lo soporta, merece la pena evaluarlo; las claves de configuración varían entre versiones, así que sigue la documentación del propio agente. El puente de este tutorial funciona con cualquier versión del agente y te da control total sobre los nombres de los topics y la forma del payload.
Requisitos previos
- Python 3.10 o posterior con
requestsypaho-mqtt2.x. - La URL de un agente MTConnect, como
http://192.168.10.50:5000. Si no tienes una máquina conectada, el proyecto del agente de referencia incluye configuraciones de ejemplo y un simulador que puedes ejecutar en local. - Un broker. Para pruebas, usa el servidor MQTT público gratuito con un prefijo de topic único.
pip install requests "paho-mqtt>=2.0"
curl -s http://localhost:5000/probe | head -40 # comprobar que el agente responde y listar los data items
Paso 1: elige los data items
Una CNC moderna puede exponer cientos de data items. Publica lo que necesitan los consumidores, no todo. Un conjunto inicial práctico para dashboards de utilización y estado:
| Tipo MTConnect | Categoría | Valores de ejemplo | Por qué importa |
|---|---|---|---|
| AVAILABILITY | Event | AVAILABLE, UNAVAILABLE | Si la máquina está conectada siquiera |
| EXECUTION | Event | ACTIVE, READY, INTERRUPTED, FEED_HOLD, STOPPED | Núcleo del seguimiento de la utilización |
| CONTROLLER_MODE | Event | AUTOMATIC, MANUAL, MANUAL_DATA_INPUT | Separa la producción de la preparación |
| PROGRAM | Event | O1234 | Qué trabajo se está ejecutando |
| PART_COUNT | Event | 1843 | Producción |
| ROTARY_VELOCITY (husillo) | Sample | 12000 | Actividad de corte |
| PATH_FEEDRATE_OVERRIDE | Event o Sample | 100 | Intervención del operario |
| Conditions de sistema y lógica | Condition | Normal, Warning, Fault | Alarmas |
Ejecuta /probe contra tu agente y anota el id de cada data item que quieras. Los IDs los elige quien configuró el agente, así que difieren entre instalaciones.
Paso 2: el puente MTConnect a MQTT
El puente resuelve los IDs de los data items a nombres de topic, lee /current para obtener el estado inicial y después itera sobre /sample. Detecta los reinicios del agente (un instanceId nuevo) y los desbordamientos del búfer (un from anterior a firstSequence) y se resincroniza en ambos casos.
# mtconnect_mqtt_bridge.py - seguir un agente MTConnect y publicar las observaciones en MQTT
import json, logging, time, uuid
import xml.etree.ElementTree as ET
import paho.mqtt.client as mqtt
import requests
logging.basicConfig(level=logging.INFO)
log = logging.getLogger("mtc")
AGENT = "http://localhost:5000"
TOPIC_ROOT = "connectmq/demo/f40b/shop1/cnc" # tu propio id aleatorio
INTERVAL = 1.0 # segundos entre peticiones /sample
MQTT_HOST, MQTT_PORT = "broker.connectmq.com", 1883 # producción: 8883 con TLS
MQTT_USER, MQTT_PASS = None, None # producción: "YOUR_USERNAME", "YOUR_PASSWORD"
CLIENT_ID = f"mtc-bridge-{uuid.uuid4().hex[:8]}" # producción: "YOUR_CLIENT_ID"
# dataItemId -> hoja del topic. Toma los IDs de la salida /probe de tu agente.
WATCH = {
"avail": "availability",
"exec": "execution",
"mode": "controller_mode",
"program": "program",
"pc": "part_count",
"Sspeed": "spindle_speed",
"logic_cond": "logic_condition",
}
CONDITION_TAGS = {"Normal", "Warning", "Fault", "Unavailable"}
def local(tag):
"""Quita el namespace XML versionado de MTConnect del nombre de una etiqueta."""
return tag.rsplit("}", 1)[-1]
def fetch(path):
r = requests.get(f"{AGENT}{path}", timeout=10)
r.raise_for_status()
return ET.fromstring(r.content)
def parse(root):
header = next(e for e in root.iter() if local(e.tag) == "Header")
observations = []
for stream in root.iter():
if local(stream.tag) != "DeviceStream":
continue
device = stream.get("name")
for el in stream.iter():
item_id = el.get("dataItemId")
if item_id is None:
continue
tag = local(el.tag)
if tag in CONDITION_TAGS:
value = {"level": tag, "native_code": el.get("nativeCode"), "text": (el.text or "").strip()}
else:
value = (el.text or "").strip()
observations.append({
"device": device, "id": item_id, "value": value,
"ts": el.get("timestamp"), "seq": int(el.get("sequence", 0)),
})
observations.sort(key=lambda o: o["seq"])
return header, observations
def coerce(value):
if isinstance(value, dict) or value == "UNAVAILABLE":
return value
try:
return float(value) if "." in value else int(value)
except ValueError:
return value
def publish(mq, obs):
leaf = WATCH.get(obs["id"])
if not leaf:
return
topic = f"{TOPIC_ROOT}/{obs['device']}/{leaf}"
payload = {"value": coerce(obs["value"]), "ts": obs["ts"], "seq": obs["seq"]}
mq.publish(topic, json.dumps(payload), qos=1, retain=True)
log.info("%s = %s", topic, payload["value"])
def main():
mq = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID)
if MQTT_USER:
mq.username_pw_set(MQTT_USER, MQTT_PASS)
mq.tls_set()
mq.will_set(f"{TOPIC_ROOT}/bridge", "offline", qos=1, retain=True)
mq.on_connect = lambda c, u, f, rc, p: c.publish(f"{TOPIC_ROOT}/bridge", "online", qos=1, retain=True)
mq.connect(MQTT_HOST, MQTT_PORT, keepalive=30)
mq.loop_start()
instance, next_seq = None, None
while True:
try:
if next_seq is None: # (re)sincronizar desde /current
header, obs = parse(fetch("/current"))
instance, next_seq = header.get("instanceId"), int(header.get("nextSequence"))
for o in obs:
publish(mq, o)
log.info("synchronised: instance %s, next %s", instance, next_seq)
else:
header, obs = parse(fetch(f"/sample?from={next_seq}&count=1000"))
if header.get("instanceId") != instance:
log.warning("agent restarted, resynchronising")
next_seq = None
continue
for o in obs:
publish(mq, o)
next_seq = int(header.get("nextSequence"))
except (ET.ParseError, StopIteration, TypeError) as exc:
# XML incorrecto, o un documento MTConnectError sin una cabecera normal.
log.error("unexpected agent response: %s", exc)
next_seq = None
except requests.RequestException as exc:
# Errores de red, o un estado de error como un "from" fuera de rango.
log.error("agent request failed: %s", exc)
next_seq = None
time.sleep(5)
time.sleep(INTERVAL)
if __name__ == "__main__":
main()
Algunos puntos sobre el diseño:
- Ordenar por secuencia garantiza que las observaciones se publiquen en el orden en que las registró el agente, aunque el XML las agrupe por componente.
- Los retained messages hacen que un dashboard abierto a mitad de turno muestre al instante el estado de ejecución, el programa y el contador de piezas actuales.
- Las conditions se convierten en objetos con un nivel y un código de alarma nativo, para que los consumidores puedan distinguir un aviso de un fallo.
- Resincronizar ante un error es seguro:
/currentvuelve a publicar el último estado, así que los consumidores acaban con los valores correctos aunque se hayan perdido algunas muestras intermedias.
Paso 3: ejecutar y observar
python mtconnect_mqtt_bridge.py
mosquitto_sub -h broker.connectmq.com -p 1883 -i mtc-viewer-f40b -t "connectmq/demo/f40b/#" -v
connectmq/demo/f40b/shop1/cnc/VMC-3Axis/execution {"value": "ACTIVE", "ts": "2026-10-16T11:02:44.501Z", "seq": 90213}
connectmq/demo/f40b/shop1/cnc/VMC-3Axis/spindle_speed {"value": 11980.0, "ts": "2026-10-16T11:02:44.612Z", "seq": 90215}
El broker público permite diez mensajes por segundo por cliente, más que suficiente para los eventos de estado, pero que pueden superar las muestras que cambian rápido, como las posiciones de los ejes. Mantén WATCH acotado o añade una banda muerta para las muestras, como se explica en nuestro artículo sobre el informe por excepción.
Construir un dashboard de estado de máquinas
Con la ejecución, el modo y la disponibilidad en MQTT, una vista básica de utilización es cuestión de suscribirse y cronometrar los cambios de estado. Un mapeo simplificado habitual:
- En marcha: disponibilidad AVAILABLE, modo AUTOMATIC, ejecución ACTIVE.
- Inactiva: disponible, ejecución READY o STOPPED.
- Interrumpida: ejecución INTERRUPTED, FEED_HOLD u OPTIONAL_STOP, a menudo una espera del operario.
- Alarma: cualquier condition con nivel Fault.
- Desconectada: disponibilidad UNAVAILABLE o el topic de estado del puente indica
offline.
Cualquier herramienta de dashboards compatible con MQTT puede calcular estos estados, y un historiador suscrito a los mismos topics te da informes por turno y por trabajo. Nuestra guía del historiador MQTT trata cómo almacenar el flujo.
Notas para producción
- Ubicación en la red. Ejecuta el puente en la red del taller junto al agente y deja que se conecte hacia fuera con el broker en el puerto 8883. No expongas el puerto HTTP del agente a internet.
- Varias máquinas. Un agente suele servir a varios dispositivos; el puente ya gestiona varios elementos
DeviceStreamy pone cada dispositivo en su propia rama. - Calidad del reloj. Las marcas de tiempo de MTConnect proceden del agente o del adaptador. Asegúrate de que esos equipos usan NTP o los cálculos de duraciones se desviarán.
- Broker privado. El broker público es visible para todos, así que los datos reales de las máquinas deben ir a un broker MQTT gestionado privado. En ConnectMQ, la vista de dispositivos agrupa tus máquinas por nivel ISA-95 y la demo de gemelo digital 3D muestra el tipo de visualización CNC en vivo que puedes construir sobre WebSockets seguros.
Para el panorama completo de cómo encajan los datos de máquina, los PLC y los gateways, lee la guía de IoT industrial con MQTT.
Empieza con ConnectMQ
Lleva los datos de tus CNC a un broker privado en minutos. Regístrate en app.connectmq.com, elige el plan Serverless gratuito (sin tarjeta de crédito), crea un broker y pega en el puente el nombre de usuario, la contraseña y el client ID generados. Después, configura una regla de alerta por umbral en el portal de ConnectMQ, por ejemplo sobre la velocidad del husillo, y observa el registro de alertas mientras la máquina trabaja.
Preguntas frecuentes
¿MTConnect soporta MQTT de forma nativa?
El estándar MTConnect se basa en una interfaz HTTP, pero las versiones recientes del agente de referencia incluyen un sink MQTT que publica las observaciones en un broker. Un pequeño puente como el anterior funciona con cualquier versión del agente.
¿Por qué usar /sample en lugar de sondear /current?
Sondear /current solo muestra el último valor y puede perder estados breves entre sondeos. Seguir /sample desde nextSequence devuelve todas las observaciones en orden, así que no se pierden alarmas ni paradas breves.
¿Qué significa UNAVAILABLE en MTConnect?
Significa que el agente no conoce el valor en ese momento, normalmente porque la máquina o el adaptador están desconectados. Publícalo tal cual para que los dashboards puedan mostrar la máquina como desconectada.
¿Puedo escribir en una CNC a través de MTConnect?
No. MTConnect es un estándar de solo lectura para monitorización. Las órdenes a la máquina requieren las interfaces propias del fabricante del control.



