Node-RED

Dashboard OEE en Node-RED con datos de máquina por MQTT

Calcula el OEE en Node-RED a partir de dos topics MQTT, el estado de la máquina y los contadores de piezas, muestra disponibilidad, rendimiento, calidad y OEE en indicadores de Dashboard 2.0 y vuelve a publicar los KPIs para otros sistemas.

Dashboard OEE en Node-RED con cuatro indicadores de disponibilidad, rendimiento, calidad y OEE alimentados por datos de máquina MQTT
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

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

  1. Un nodo mqtt in en .../press1/state, con salida "a UTF-8 string".
  2. Un nodo mqtt in en .../press1/count, con salida "a parsed JSON object".
  3. Un nodo inject llamado "tick" que se repite cada 5 segundos con msg.topic establecido en tick.
  4. Un ui-button con la etiqueta "Start new shift" y msg.topic establecido en shift/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 out con los campos topic y retain vacíos, para que use msg.topic y msg.retain del mensaje. Añade un nodo ui-text tras un nodo change que extraiga payload.state si 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

  1. 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.
  2. 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_STOP y deja ese tiempo fuera de plannedMs si tu planta usa esta definición.
  3. Perder el estado tras los reinicios. Por defecto, el contexto del flujo está en memoria. Guarda el objeto oee en un almacén de contexto en archivo para que un reinicio de Node-RED a mitad de turno no ponga a cero los KPIs.
  4. 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.
  5. 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.

Artículos relacionados

Cliente OPC UA en Node-RED hacia MQTT con topics ISA-95

Usa node-red-contrib-opcua para suscribirte a variables OPC UA de un PLC o de un servidor de simulación, asignar los node IDs a topics MQTT de estilo ISA-95 y publicar los valores con calidad y marcas de tiempo.

10 min de lectura

Node-RED Dashboard 2.0 con MQTT: indicadores, gráficos y controles

Un tutorial práctico de Node-RED Dashboard 2.0: suscríbete a telemetría MQTT en directo, muéstrala en indicadores y en un gráfico de líneas, y controla un dispositivo con un interruptor que publica comandos y refleja el estado real.

9 min de lectura

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.