IoT industrial

MTConnect a MQTT: transmitir datos de máquinas CNC a dashboards

Los agentes MTConnect exponen los datos de las CNC mediante HTTP y XML. Construye un puente que sigue los números de secuencia del agente, elige los data items que importan y publica el estado de la máquina por MQTT para dashboards y analítica.

Máquinas CNC que alimentan un agente MTConnect cuyo flujo XML convierte un puente de MTConnect a MQTT en topics para dashboards en vivo
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

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, Fault o Unavailable, 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 requests y paho-mqtt 2.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: /current vuelve 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:

  1. En marcha: disponibilidad AVAILABLE, modo AUTOMATIC, ejecución ACTIVE.
  2. Inactiva: disponible, ejecución READY o STOPPED.
  3. Interrumpida: ejecución INTERRUPTED, FEED_HOLD u OPTIONAL_STOP, a menudo una espera del operario.
  4. Alarma: cualquier condition con nivel Fault.
  5. 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 DeviceStream y 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.

Artículos relacionados

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.