Un dashboard OEE en Node-RED es una de las formas más rápidas de poner un KPI de manufactura real delante de un equipo sin comprar un MES. En este tutorial calcularás la disponibilidad, el rendimiento y la calidad a partir de dos topics MQTT, el estado de la máquina y los contadores de piezas, y mostrarás el OEE en vivo en indicadores de Dashboard 2.0. También volverás a publicar los KPIs en MQTT para que otras herramientas puedan usarlos, y conocerás los casos límite que hacen que los cálculos ingenuos de OEE sean incorrectos.
El OEE en un minuto
La eficiencia general de los equipos (OEE, Overall Equipment Effectiveness) multiplica tres ratios, cada uno entre 0 y 1:
| Factor | Fórmula | Qué pierde |
|---|---|---|
| Disponibilidad | Tiempo en marcha / tiempo de producción planificado | Averías, cambios de formato, espera de material |
| Rendimiento | (Tiempo de ciclo ideal x conteo total) / tiempo en marcha | Ciclos lentos, microparadas |
| Calidad | Conteo de piezas buenas / conteo total | Scrap y retrabajos |
| OEE | Disponibilidad x Rendimiento x Calidad | Todo lo anterior |
Lo bonito de la fórmula es que necesita muy pocos datos: cuándo estaba en marcha la máquina, cuántas piezas fabricó y cuántas eran buenas. Eso encaja perfectamente con MQTT. Para profundizar en las definiciones, incluidas las paradas planificadas frente a las no planificadas, consulta cómo calcular el OEE.
El modelo de datos MQTT
La máquina, o un gateway situado delante de ella, publica dos topics:
| Topic | Payload | Retenido | Cuándo |
|---|---|---|---|
connectmq/demo/a7f3k2/plant1/line1/press1/state |
RUNNING, IDLE o DOWN |
Sí | En cada cambio |
connectmq/demo/a7f3k2/plant1/line1/press1/count |
{"total": 1234, "good": 1201} |
No | Cada pocos segundos |
Dos decisiones de diseño lo hacen robusto:
- El estado se publica por eventos y como retenido. Node-RED conoce el estado actual inmediatamente después de conectarse y acumula el tiempo en marcha a partir de los eventos de cambio.
- Los contadores son acumulados, no por pieza. Si se pierde un mensaje, el siguiente sigue llevando el total correcto. Node-RED calcula los deltas, y un contador que retrocede se trata como un reinicio del PLC y no como una producción negativa.
El topic sigue una jerarquía al estilo ISA-95 (sitio, línea, máquina), detrás de un prefijo de demo. Sustituye a7f3k2 por tu propia cadena aleatoria, porque cualquiera puede leer el servidor público.
Simular una máquina
Este script en Python se comporta como una prensa con un ciclo ideal de 2 segundos, paradas ocasionales y alrededor de un 3 por ciento de scrap. Publica en broker.connectmq.com, el servidor MQTT público gratuito, pensado para pruebas y no para datos de producción.
# pip install "paho-mqtt>=2.0"
import json
import random
import time
import uuid
import paho.mqtt.client as mqtt
BASE = "connectmq/demo/a7f3k2/plant1/line1/press1"
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id=f"press-sim-{uuid.uuid4().hex[:8]}")
client.connect("broker.connectmq.com", 1883, keepalive=60)
client.loop_start()
state = None
total = 0
good = 0
next_cycle = time.time()
last_count_pub = 0.0
def set_state(new_state):
global state
if new_state != state:
state = new_state
client.publish(f"{BASE}/state", state, qos=1, retain=True)
print("State:", state)
set_state("RUNNING")
try:
while True:
now = time.time()
if state == "RUNNING":
if random.random() < 0.004:
set_state(random.choice(["IDLE", "DOWN"]))
elif now >= next_cycle:
total += 1
if random.random() > 0.03:
good += 1
# Los ciclos reales son algo más lentos que el ideal
next_cycle = now + random.uniform(2.0, 2.6)
elif random.random() < 0.02:
set_state("RUNNING")
next_cycle = time.time() + 2.0
if now - last_count_pub >= 5:
client.publish(f"{BASE}/count",
json.dumps({"total": total, "good": good}), qos=1)
last_count_pub = now
time.sleep(0.2)
except KeyboardInterrupt:
pass
finally:
client.loop_stop()
client.disconnect()
Construir el flujo del dashboard OEE en Node-RED
Necesitas Node-RED 4.x con Dashboard 2.0 (@flowfuse/node-red-dashboard) instalado. Si todavía no lo has usado, el tutorial de Dashboard 2.0 cubre la instalación y la estructura de páginas y grupos.
Entradas
- Un nodo
mqtt inen.../press1/state, con salida "a UTF-8 string". - Un nodo
mqtt inen.../press1/count, con salida "a parsed JSON object". - Un nodo inject llamado "tick" que se repite cada 5 segundos con
msg.topicestablecido entick. - Un
ui-buttoncon la etiqueta "Start new shift" ymsg.topicestablecido enshift/reset, más un nodo inject con el mismo topic programado a las horas de cambio de turno (por ejemplo, 06:00, 14:00 y 22:00).
Usa dos suscripciones específicas en lugar de un comodín press1/#. Más adelante publicarás KPIs bajo la misma ruta de la máquina, y un comodín reinyectaría tu propia salida en el cálculo.
El cálculo del OEE
Conecta las cuatro entradas a un único nodo function con cinco salidas:
// Nodo function "OEE calculator" - configura Outputs a 5
const IDEAL_CYCLE_S = 2.0; // segundos ideales por pieza para esta máquina
const now = Date.now();
function newShift() {
return { shiftStart: now, state: "UNKNOWN", since: now, runMs: 0,
lastTotal: null, lastGood: null, total: 0, good: 0 };
}
let s = flow.get("oee") || newShift();
const topic = String(msg.topic || "");
if (topic === "shift/reset") {
const keepState = s.state;
s = newShift();
s.state = keepState; // la máquina sigue en el mismo estado
flow.set("oee", s);
return null;
}
if (topic.endsWith("/state")) {
const st = String(msg.payload).trim().toUpperCase();
if (s.state === "RUNNING") s.runMs += now - s.since;
s.state = st;
s.since = now;
flow.set("oee", s);
return null;
}
if (topic.endsWith("/count")) {
const total = Number(msg.payload.total);
const good = Number(msg.payload.good);
if (!Number.isFinite(total) || !Number.isFinite(good)) return null;
if (s.lastTotal !== null) {
// Un contador que retrocede significa que el PLC lo reinició
const dTotal = total >= s.lastTotal ? total - s.lastTotal : total;
const dGood = good >= s.lastGood ? good - s.lastGood : good;
s.total += dTotal;
s.good += Math.min(dGood, dTotal);
}
s.lastTotal = total;
s.lastGood = good;
flow.set("oee", s);
return null;
}
if (topic !== "tick") return null;
// Calcular los KPIs del turno hasta ahora
const runMs = s.runMs + (s.state === "RUNNING" ? now - s.since : 0);
const plannedMs = now - s.shiftStart;
const availability = plannedMs > 0 ? runMs / plannedMs : 0;
const rawPerformance = runMs > 0 ? (IDEAL_CYCLE_S * 1000 * s.total) / runMs : 0;
const performance = Math.min(1, rawPerformance);
const quality = s.total > 0 ? s.good / s.total : 0;
const oee = availability * performance * quality;
if (rawPerformance > 1.05) {
node.warn("Performance above 100%: check IDEAL_CYCLE_S");
}
const pct = v => Math.round(v * 1000) / 10;
node.status({ fill: s.state === "RUNNING" ? "green" : "red", shape: "dot",
text: `${s.state} OEE ${pct(oee)}%` });
return [
{ payload: pct(availability) },
{ payload: pct(performance) },
{ payload: pct(quality) },
{ payload: pct(oee) },
{ topic: "connectmq/demo/a7f3k2/plant1/line1/press1/oee",
payload: { availability: pct(availability), performance: pct(performance),
quality: pct(quality), oee: pct(oee), total: s.total, good: s.good,
state: s.state, shiftStart: new Date(s.shiftStart).toISOString() },
retain: true }
];
Pulsa Start new shift una vez tras el deploy para que el turno empiece en un momento conocido.
Widgets
- Las salidas 1 a 4 van a cuatro nodos
ui-gauge, con rango de 0 a 100 y unidad%. Un esquema de colores habitual para el OEE es rojo por debajo de 60, ámbar de 60 a 85 y verde por encima de 85, pero elige bandas que reflejen tu propia línea base. - La salida 5 va a un nodo
mqtt outcon los campos topic y retain vacíos, para que usemsg.topicymsg.retaindel mensaje. Añade un nodoui-texttras un nodo change que extraigapayload.statesi también quieres ver en pantalla el estado de la máquina.
Abre /dashboard y observa cómo se estabilizan los números durante los primeros minutos. Al principio de un turno, una sola parada hace oscilar mucho la disponibilidad; es matemáticamente correcto y los operadores se acostumbran rápido.
Errores que distorsionan el OEE
- Tiempo de ciclo ideal incorrecto. Si el rendimiento se mantiene por encima del 100 por ciento, tu ciclo ideal es demasiado lento. No te limites a recortarlo y seguir adelante; el límite del código oculta un problema de datos.
- Contar las paradas planificadas como tiempo de parada. Los descansos, el mantenimiento planificado y los periodos sin órdenes suelen excluirse del tiempo de producción planificado. Añade un estado
PLANNED_STOPy deja ese tiempo fuera deplannedMssi tu planta usa esta definición. - Perder el estado tras los reinicios. Por defecto, el contexto del flujo está en memoria. Guarda el objeto
oeeen un almacén de contexto en archivo para que un reinicio de Node-RED a mitad de turno no ponga a cero los KPIs. - Mensajes fuera de orden. Con QoS 1 y un solo publicador, MQTT conserva el orden por topic, pero el estado y el conteo son topics distintos. Calcular con un temporizador, como arriba, hace irrelevantes las pequeñas diferencias de tiempo.
- Desbordamiento del contador. Los contadores de PLC de 16 bits vuelven a cero en 65.535. La lógica de reinicio trata un desbordamiento como un reinicio, lo que pierde como mucho un intervalo de conteos.
Los motivos de parada son el siguiente paso natural: añade un código de motivo al estado DOWN y acumula minutos por motivo. La guía de seguimiento de paradas de máquina explica cómo estructurar esos códigos.
Conservar el histórico del OEE
Los indicadores muestran solo el turno actual. Para comparar turnos, escribe el mensaje retenido de KPIs en una base de datos de series temporales al final de cada turno, o cada minuto. El tutorial de Node-RED con InfluxDB muestra cómo almacenar exactamente este tipo de JSON con tags como línea y máquina, y después graficarlo.
Escalar a una línea o una planta completa
Un nodo function por máquina funciona para un piloto. Para una línea completa, suscríbete con un comodín de un nivel como plant1/line1/+/state, mantén un objeto de contexto por máquina indexado por el nivel del topic y publica los KPIs de cada máquina en su propio topic. Como el cálculo solo depende de topics MQTT, le da igual si los datos vienen de un gateway de PLC, de un contador ESP32 o de un dispositivo edge. Esos mismos KPIs por máquina pueden colorear cada equipo en un gemelo digital 3D de la planta, ya sea en Monterrey, Bogotá o Barcelona.
Ahí es también donde importa el broker. Una planta con decenas de máquinas, un servidor Node-RED, un historiador y quizá un cliente SCADA conectados a la vez necesita un broker MQTT en la nube fiable y con autenticación, no un servicio público de pruebas. ConnectMQ reconoce topics al estilo ISA-95 como plant1/line1/press1/state y agrupa los dispositivos por esos niveles en su portal, lo que facilita ver qué máquinas están informando. Las reglas de alerta por umbral del portal de ConnectMQ también pueden señalar un valor numérico que cruza un límite que elijas, mientras Node-RED sigue siendo responsable del cálculo en sí. Si prefieres no operar nada de esta infraestructura por tu cuenta, un broker MQTT gestionado saca el lado del broker de tu lista de mantenimiento. Para los fundamentos del broker y de Node-RED, empieza por el tutorial de Node-RED con MQTT.
Lleva tus datos de OEE a ConnectMQ
Regístrate en app.connectmq.com con el plan gratuito Serverless, sin tarjeta de crédito. Crea un broker, usa el username, la password y el client ID generados en tus máquinas y en Node-RED, y cambia a TLS en el puerto 8883. Tendrás un pipeline de OEE privado funcionando antes del próximo cambio de turno.
Preguntas frecuentes
¿Qué datos necesito para calcular el OEE en Node-RED?
Necesitas el estado de la máquina a lo largo del tiempo, un conteo total de piezas, un conteo de piezas buenas y el tiempo de ciclo ideal. Con esas cuatro entradas, Node-RED puede calcular la disponibilidad, el rendimiento, la calidad y el OEE.
¿Por qué deben publicar las máquinas contadores acumulados?
Los contadores acumulados hacen que el cálculo tolere mensajes perdidos o retrasados, porque cada mensaje lleva el total completo. Node-RED calcula la diferencia y gestiona los reinicios.
¿Con qué frecuencia debe actualizarse el dashboard OEE?
Cada 5 a 30 segundos es suficiente para los operadores. Calcular con un temporizador en lugar de en cada mensaje también mantiene fluido el dashboard cuando muchas máquinas publican a la vez.
¿Puedo mostrar el OEE de varias máquinas en una misma página?
Sí. Usa una suscripción con comodín, mantén un objeto de contexto por máquina y añade un grupo del dashboard por máquina o una tabla construida con un nodo ui-template.



