La monitorización del estado de máquinas responde a la pregunta más básica del taller, "¿está funcionando ahora mismo?", para todas las máquinas a la vez. En este tutorial definirás un modelo de estados sencillo, deducirás en una Raspberry Pi los estados de marcha, espera y parada a partir de balizas luminosas o señales del PLC, y publicarás el estado como mensajes MQTT retenidos para que cualquier dashboard muestre el estado actual en el instante en que se conecta.
Por qué empezar por el estado de la máquina
El estado de la máquina es la base de casi todos los KPI de producción. La disponibilidad del OEE, el análisis de paradas, las estadísticas de tiempo de ciclo y la energía por hora en espera dependen de saber cuándo estaba funcionando una máquina y cuándo no. También es el dato más barato de obtener: muchas máquinas ya muestran su estado en una baliza luminosa, y casi todos los PLC tienen un bit de "ciclo activo" y otro de "fallo".
La dificultad no es la señal, sino la coherencia. Si una máquina informa "auto", otra "RUN" y una tercera solo enciende una lámpara verde, compararlas es un suplicio. Por eso el modelo de estados va primero.
Un modelo de estados práctico
Mantén pocos estados y definiciones sin ambigüedad. Este modelo de cinco estados cubre la mayor parte de la fabricación discreta:
| Estado | Definición | Señal típica |
|---|---|---|
| running | La máquina produce; un ciclo terminó dentro del tiempo esperado | Pulso de ciclo, luz verde, "en ciclo" del PLC |
| idle | La máquina está sana y disponible pero no produce (espera material, operario, proceso anterior) | Sin fallo, sin ciclo reciente |
| down | La máquina no puede producir por un fallo o alarma | Luz roja, bit de fallo del PLC |
| setup | Cambio de formato planificado, cambio de herramienta o mantenimiento | Entrada del operario, modo del PLC, estado de la orden en el MES |
| offline | No hay datos del dispositivo edge | Last Will de MQTT |
Dos reglas hacen que el modelo funcione:
- Prioridad: si se cumplen varias condiciones, gana la de mayor prioridad. Un orden sensato es offline, down, setup, running, idle.
- Antirrebote temporal: una máquina que completa un ciclo cada 40 segundos no debería parpadear a espera entre ciclos. Trátala como en marcha hasta que no se haya visto ningún ciclo durante, por ejemplo, el doble del tiempo de ciclo nominal.
Los motivos de parada y espera (sin material, rotura de herramienta, retención de calidad) son una dimensión aparte. Recógelos más adelante en un campo reason en lugar de multiplicar los estados.
Diseño de topics con mensajes retenidos
Cada máquina tiene un topic state que guarda su estado actual como mensaje retenido. El broker almacena el último mensaje retenido de cada topic y lo envía de inmediato a cada nuevo suscriptor. Un dashboard abierto a las 10:15 no tiene que esperar al siguiente cambio; ve al instante "parada desde las 09:52". Lee mensajes retenidos en MQTT para conocer todo el mecanismo.
connectmq/plant1/machining/line1/cnc-01/state retained current state
connectmq/plant1/machining/line1/cnc-01/events not retained, one message per transition
connectmq/plant1/machining/line1/cnc-01/status retained online / offline (Last Will)
Payload de estado:
{
"state": "down",
"since": 1760599920,
"previous": "running",
"reason": null,
"source": "edge-cnc-01"
}
Incluir since permite a un dashboard mostrar la duración sin ninguna base de datos histórica. El topic events es al que se suscribe un historian para los informes de paradas y disponibilidad.
Cableado de las señales
En máquinas sin acceso de red al PLC, toma las señales de la baliza luminosa o de salidas libres del PLC mediante módulos de entrada con optoacoplador de 24 V hacia los GPIO de la Raspberry Pi. Nunca conectes señales de 24 V directamente a la Pi; sus pines GPIO solo admiten 3,3 V. Los trabajos eléctricos en armarios de máquina deben realizarlos profesionales cualificados.
| Señal | GPIO (BCM) | Significado cuando está activa |
|---|---|---|
| Pulso de ciclo completado o luz verde | 17 | Ha terminado un ciclo / máquina en ciclo |
| Luz roja o salida de fallo | 27 | Máquina en fallo |
| Llave de setup o luz amarilla | 22 | Setup o cambio de formato planificado |
Donde el PLC sea accesible por red, lee los mismos bits por Modbus TCP, OPC UA o S7; solo cambia la función de entrada.
El script edge de monitorización del estado de máquinas
Instala las librerías en un entorno virtual (pip install gpiozero lgpio paho-mqtt). El script siguiente evalúa las entradas dos veces por segundo, aplica las reglas de prioridad y tiempo, y publica solo en las transiciones.
#!/usr/bin/env python3
"""Machine status monitoring edge script: GPIO signals -> retained MQTT state."""
import json
import time
import paho.mqtt.client as mqtt
from gpiozero import DigitalInputDevice
BROKER = "broker.connectmq.com"
MACHINE = "cnc-01"
BASE = f"connectmq/demo/msm-5t2r/line1/{MACHINE}"
NOMINAL_CYCLE_S = 40
RUN_TIMEOUT_S = 2 * NOMINAL_CYCLE_S
cycle_in = DigitalInputDevice(17, pull_up=True, bounce_time=0.02)
fault_in = DigitalInputDevice(27, pull_up=True, bounce_time=0.05)
setup_in = DigitalInputDevice(22, pull_up=True, bounce_time=0.05)
last_cycle = 0.0
state, since = None, 0
def on_cycle():
global last_cycle
last_cycle = time.time()
cycle_in.when_activated = on_cycle
def derive_state(now):
if fault_in.is_active:
return "down"
if setup_in.is_active:
return "setup"
if cycle_in.is_active or now - last_cycle < RUN_TIMEOUT_S:
return "running"
return "idle"
def on_connect(client, userdata, flags, reason_code, properties):
print("Connected:", reason_code)
client.publish(f"{BASE}/status", "online", qos=1, retain=True)
if state is not None: # restore after reconnect
publish_state(client, state, state, since, event=False)
def publish_state(client, new, previous, ts, event=True):
payload = {"state": new, "since": ts, "previous": previous,
"reason": None, "source": f"edge-{MACHINE}"}
client.publish(f"{BASE}/state", json.dumps(payload), qos=1, retain=True)
if event:
client.publish(f"{BASE}/events", json.dumps(payload), qos=1)
print(payload)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"msm-5t2r-{MACHINE}")
client.will_set(f"{BASE}/status", "offline", qos=1, retain=True)
client.on_connect = on_connect
client.connect(BROKER, 1883, keepalive=30)
client.loop_start()
while True:
now = time.time()
new = derive_state(now)
if new != state:
previous, state, since = state, new, int(now)
publish_state(client, state, previous, since)
time.sleep(0.5)
Algunas decisiones de diseño que merece la pena destacar:
- El Last Will en
statusconvierte un script caído, una red perdida o un corte de corriente en un estado "offline" visible, en lugar de un "running" congelado. - Al reconectarse, el script vuelve a publicar el estado retenido, de modo que el broker siempre conserva la última verdad aunque haya habido una transición mientras la conexión estaba caída.
- Las entradas usan
pull_up=Trueporque los módulos de optoacoplador típicos llevan la línea a nivel bajo cuando están activos. Inviértelo si tu módulo se comporta de otra forma.
Ejecútalo como servicio de systemd para que arranque al iniciar. Para un broker de producción, añade client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD"), client.tls_set(), usa el client ID que se te haya emitido y conéctate al puerto 8883.
Ver el panel de estado
Cualquier cliente MQTT puede mostrar ya el estado en vivo. Desde un terminal:
mosquitto_sub -h broker.connectmq.com -p 1883 -i msm-board-$RANDOM \
-t "connectmq/demo/msm-5t2r/line1/+/state" -v
Los estados retenidos aparecen de inmediato, una línea por máquina. En un dashboard de navegador, MQTT.js se conecta por WebSocket seguro y hace lo mismo:
import mqtt from "mqtt";
const client = mqtt.connect("wss://broker.connectmq.com:8084/mqtt", {
clientId: "msm-board-" + Math.random().toString(16).slice(2, 8),
protocolVersion: 5,
});
const board = {};
client.on("connect", () => client.subscribe("connectmq/demo/msm-5t2r/line1/+/state"));
client.on("message", (topic, payload) => {
const machine = topic.split("/").at(-2);
board[machine] = JSON.parse(payload.toString());
const lines = Object.entries(board).map(([m, s]) => {
const minutes = Math.round((Date.now() / 1000 - s.since) / 60);
return `${m}: ${s.state} for ${minutes} min`;
});
console.clear();
console.log(lines.join("\n"));
});
Recuerda que los topics del servidor MQTT público gratuito son visibles para todos; es para pruebas, y los nombres y estados de máquina de una planta real deben ir a un broker privado. El mismo flujo de estados también puede colorear cada máquina en un gemelo digital 3D de la planta.
Del estado a las métricas
Una vez que existe el flujo events, el resto de la monitorización de la línea se construye sobre él:
- Disponibilidad: tiempo en marcha dividido entre el tiempo de producción planificado, a partir del historial de eventos.
- Registro de paradas: cada transición de marcha a parada abre un registro de parada; los operarios añaden los motivos en una tableta.
- Tiempo de ciclo: la misma entrada de ciclo, con marca de tiempo, da la duración ciclo a ciclo, explicada en monitorización del tiempo de ciclo.
- Recuento de piezas: un contador sobre el mismo pulso alimenta el conteo de producción con sensores IoT.
- Escalado: una máquina parada más tiempo que un umbral dispara un aviso en un sistema andon digital.
Solución de problemas
| Síntoma | Causa | Solución |
|---|---|---|
| El estado parpadea entre running e idle | El timeout de marcha es menor que la variación real del ciclo | Aumenta RUN_TIMEOUT_S |
| El dashboard muestra un "running" obsoleto tras un corte de corriente | Sin Last Will, o el dashboard ignora status |
Usa Last Will y muestra offline con prioridad |
| Siguen apareciendo máquinas antiguas en el panel | Los mensajes retenidos permanecen tras darlas de baja | Publica un mensaje retenido vacío para limpiar el topic |
| La entrada de fallo siempre está activa | Lógica invertida del módulo de optoacoplador | Cambia pull_up o usa active_state |
Funcionamiento a escala
Docenas de máquinas, cada una con un estado retenido y un cliente edge que debe reconectarse de forma fiable, es exactamente la carga de trabajo para la que está pensado un broker MQTT gestionado. ConnectMQ expone además el estado de conexión de cada cliente en $SYS/broker/connection/{clientId}/state, lo que ayuda a detectar dispositivos edge que se desconectan una y otra vez. La guía de fabricación inteligente muestra cómo encaja el estado de máquina en una hoja de ruta completa de monitorización.
Empieza con ConnectMQ
ConnectMQ te ofrece un broker MQTT 5 protegido con TLS, con mensajes retenidos, un explorador de topics en vivo y una vista de dispositivos ISA-95 que agrupa las máquinas por área y línea. Crea un broker Serverless gratuito sin tarjeta de crédito y pasa tu monitorización del estado de máquinas del servidor de pruebas a producción.
Preguntas frecuentes
¿Por qué usar mensajes retenidos para el estado de las máquinas?
Un mensaje retenido se entrega de inmediato a cada nuevo suscriptor, así que dashboards, tabletas y servicios empiezan siempre con el estado actual de cada máquina en lugar de esperar al siguiente cambio.
¿Cómo detecto que el dispositivo edge de una máquina está offline?
Configura un mensaje Last Will en un topic de estado retenido cuando el cliente se conecte. Si el dispositivo se desconecta de forma inesperada, el broker publica el Will y los dashboards pueden mostrar la máquina como offline.
¿Puedo obtener el estado de la máquina directamente del PLC en lugar de las balizas?
Sí. Lee los bits de ciclo y fallo del PLC por Modbus TCP, OPC UA o un protocolo del fabricante y pásalos a la misma lógica de estados. Las señales del PLC suelen ser más precisas que las balizas luminosas.
¿Cuántos estados debe tener un modelo de estado de máquina?
Los menos posibles que sigan respondiendo a tus preguntas. Running, idle, down, setup y offline cubren la mayoría de las necesidades; usa códigos de motivo para los detalles en lugar de añadir estados.



