Node-RED

Dashboard OEE no Node-RED a partir de dados de máquina via MQTT

Calcule o OEE no Node-RED a partir de dois topics MQTT, estado da máquina e contadores de peças, depois exiba disponibilidade, desempenho, qualidade e OEE em gauges do Dashboard 2.0 e republique os KPIs para outros sistemas.

Dashboard OEE no Node-RED com quatro gauges de disponibilidade, desempenho, qualidade e OEE alimentados por dados de máquina via MQTT
Rode isso no seu próprio broker MQTTTenha um broker MQTT privado e gerenciado no plano Free e comece com exemplos de código prontos.
Teste grátis Exemplos de código Sem cartão de crédito

Um dashboard OEE no Node-RED é uma das formas mais rápidas de colocar um KPI real de manufatura na frente de uma equipe sem comprar um MES. Neste tutorial você vai calcular disponibilidade, desempenho e qualidade a partir de dois topics MQTT, estado da máquina e contadores de peças, e mostrar o OEE ao vivo em gauges do Dashboard 2.0. Você também vai republicar os KPIs no MQTT para que outras ferramentas possam usá-los e conhecer os casos de borda que deixam errados os números de OEE calculados de forma ingênua.

OEE em um minuto

A Eficiência Global do Equipamento (OEE, Overall Equipment Effectiveness) multiplica três índices, cada um entre 0 e 1:

Fator Fórmula O que ele perde
Disponibilidade Tempo de operação / tempo de produção planejado Quebras, setups, espera por material
Desempenho (Tempo de ciclo ideal x contagem total) / tempo de operação Ciclos lentos, microparadas
Qualidade Contagem de peças boas / contagem total Refugo e retrabalho
OEE Disponibilidade x Desempenho x Qualidade Tudo o que está acima

A beleza da fórmula é que ela precisa de pouquíssimos dados: quando a máquina estava rodando, quantas peças ela fez e quantas eram boas. Isso se encaixa perfeitamente no MQTT. Para um olhar mais aprofundado nas definições, incluindo paradas planejadas e não planejadas, veja como calcular o OEE.

O modelo de dados MQTT

A máquina, ou um gateway na frente dela, publica dois topics:

Topic Payload Retained Quando
connectmq/demo/a7f3k2/plant1/line1/press1/state RUNNING, IDLE ou DOWN Sim A cada mudança
connectmq/demo/a7f3k2/plant1/line1/press1/count {"total": 1234, "good": 1201} Não A cada poucos segundos

Duas decisões de projeto tornam isso robusto:

  • O estado é orientado a eventos e retained. O Node-RED descobre o estado atual logo após se conectar e acumula o tempo de operação a partir dos eventos de mudança.
  • Os contadores são acumulados, não por peça. Se uma mensagem se perder, a próxima ainda traz o total correto. O Node-RED calcula as diferenças, e um contador que volta para trás é tratado como um reset do CLP, e não como produção negativa.

O topic segue uma hierarquia no estilo ISA-95 (site, linha, máquina), atrás de um prefixo de demonstração. Substitua a7f3k2 por uma string aleatória sua, porque o broker público pode ser lido por qualquer pessoa.

Simule uma máquina

Este script Python se comporta como uma prensa com ciclo ideal de 2 segundos, paradas ocasionais e cerca de 3 por cento de refugo. Ele publica em broker.connectmq.com, o servidor MQTT público.

# 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
                # Ciclos reais são um pouco mais lentos que o 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()

Monte o fluxo do dashboard OEE no Node-RED

Você precisa do Node-RED 4.x com o Dashboard 2.0 (@flowfuse/node-red-dashboard) instalado. Se ainda não o usou, o tutorial do Dashboard 2.0 cobre a instalação e a estrutura de páginas e grupos.

Entradas

  1. Um nó mqtt in em .../press1/state, com saída "a UTF-8 string".
  2. Um nó mqtt in em .../press1/count, com saída "a parsed JSON object".
  3. Um nó inject chamado "tick" que se repete a cada 5 segundos com msg.topic definido como tick.
  4. Um ui-button com o rótulo "Iniciar novo turno" e msg.topic definido como shift/reset, mais um nó inject com o mesmo topic agendado nos horários de troca de turno (por exemplo 06:00, 14:00 e 22:00).

Use duas assinaturas específicas em vez de um wildcard press1/#. Mais adiante você vai publicar KPIs sob o mesmo caminho da máquina, e um wildcard realimentaria o cálculo com a sua própria saída.

O cálculo do OEE

Ligue as quatro entradas a um único nó function com cinco saídas:

// Nó function "OEE calculator" - defina Outputs como 5
const IDEAL_CYCLE_S = 2.0;         // segundos ideais por peça 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;            // a máquina continua no mesmo 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) {
        // Um contador que volta para trás significa que o CLP o zerou
        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;

// Calcula os KPIs do turno até agora
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 }
];

Clique em Iniciar novo turno uma vez depois do deploy, para que o turno comece em um momento conhecido.

Widgets

  • As saídas 1 a 4 vão para quatro nós ui-gauge, com faixa de 0 a 100 e unidade %. Um esquema de cores comum para OEE é vermelho abaixo de 60, âmbar de 60 a 85 e verde acima de 85, mas escolha faixas que reflitam a sua própria linha de base.
  • A saída 5 vai para um nó mqtt out com os campos de topic e retain vazios, para que ele use msg.topic e msg.retain da mensagem. Adicione um nó ui-text depois de um nó change que extraia payload.state se quiser exibir também o estado da máquina na tela.

Abra /dashboard e observe os números se estabilizarem nos primeiros minutos. No início de um turno, uma única parada faz a disponibilidade oscilar muito; isso é matematicamente correto, e os operadores se acostumam rapidamente.

Armadilhas que distorcem o OEE

  1. Tempo de ciclo ideal errado. Se o desempenho ficar acima de 100 por cento, o seu ciclo ideal está lento demais. Não se limite a aplicar o teto e seguir em frente; o teto no código esconde um problema de dados.
  2. Contar paradas planejadas como tempo parado. Intervalos, manutenção planejada e períodos sem pedido normalmente são excluídos do tempo de produção planejado. Adicione um estado PLANNED_STOP e deixe esse tempo fora de plannedMs se a sua planta usar essa definição.
  3. Perder o estado em reinícios. O contexto de fluxo fica em memória por padrão. Armazene o objeto oee em um context store baseado em arquivo para que um reinício do Node-RED no meio do turno não zere os KPIs.
  4. Mensagens fora de ordem. Com QoS 1 e um único publicador, o MQTT preserva a ordem por topic, mas estado e contagem são topics separados. Calcular com um temporizador, como acima, torna irrelevantes as pequenas diferenças de tempo.
  5. Estouro do contador. Contadores de CLP de 16 bits voltam a zero em 65.535. A lógica de reset trata o estouro como um reset, o que perde no máximo um intervalo de contagens.

Os motivos de parada são o próximo passo natural: adicione um código de motivo ao estado DOWN e acumule os minutos por motivo. O guia de rastreamento de paradas de máquina explica como estruturar esses códigos.

Mantendo o histórico de OEE

Os gauges mostram apenas o turno atual. Para comparar turnos, grave a mensagem retained de KPIs em um banco de séries temporais ao final de cada turno, ou a cada minuto. O tutorial Node-RED InfluxDB mostra como armazenar exatamente esse tipo de JSON com tags como linha e máquina e depois montar gráficos.

Escalando para uma linha ou planta inteira

Um nó function por máquina funciona para um piloto. Para uma linha completa, assine com um wildcard de nível único como plant1/line1/+/state, mantenha um objeto de contexto por máquina indexado pelo nível do topic e publique os KPIs de cada máquina em seu próprio topic. Como o cálculo depende apenas de topics MQTT, não importa se os dados vêm de um gateway de CLP, de um contador ESP32 ou de um dispositivo de borda. A mesma árvore de topics também serve de base para um gêmeo digital da linha, seja em uma planta em Joinville ou em Manaus.

É aí também que o broker faz diferença. Uma planta com dezenas de máquinas, um servidor Node-RED, um historiador e talvez um cliente SCADA conectados ao mesmo tempo precisa de um broker MQTT na nuvem confiável e com autenticação, e não de um serviço público de testes. A ConnectMQ reconhece topics no estilo ISA-95 como plant1/line1/press1/state e agrupa os dispositivos por esses níveis no portal, o que facilita ver quais máquinas estão reportando. Regras de alerta por limite no portal da ConnectMQ também podem sinalizar um valor numérico que cruza um limite escolhido por você, enquanto o Node-RED continua responsável pelo cálculo em si. Se você prefere não operar nada dessa infraestrutura, um broker MQTT gerenciado tira o lado do broker da sua lista de manutenção. Para os fundamentos de broker e Node-RED, comece pelo tutorial Node-RED MQTT.

Coloque seus dados de OEE na ConnectMQ

Cadastre-se em app.connectmq.com no plano Serverless gratuito, sem cartão de crédito. Crie um broker, use o nome de usuário, a senha e o client ID gerados nas suas máquinas e no Node-RED e passe para TLS na porta 8883. Você terá um pipeline de OEE privado funcionando antes da próxima troca de turno.

Perguntas frequentes

De quais dados preciso para calcular o OEE no Node-RED?

Você precisa do estado da máquina ao longo do tempo, de uma contagem total de peças, de uma contagem de peças boas e do tempo de ciclo ideal. Com essas quatro entradas, o Node-RED consegue calcular disponibilidade, desempenho, qualidade e OEE.

Por que as máquinas devem publicar contadores acumulados?

Contadores acumulados tornam o cálculo tolerante a mensagens perdidas ou atrasadas, porque cada mensagem traz o total completo. O Node-RED calcula a diferença e trata os resets.

Com que frequência o dashboard OEE deve ser atualizado?

A cada 5 a 30 segundos é suficiente para os operadores. Calcular com um temporizador, em vez de a cada mensagem, também mantém o dashboard fluido quando muitas máquinas publicam ao mesmo tempo.

Posso mostrar o OEE de várias máquinas em uma única página?

Sim. Use uma assinatura com wildcard, mantenha um objeto de contexto por máquina e adicione um grupo de dashboard por máquina ou uma tabela montada com um nó ui-template.

Artigos relacionados

Node-RED Dashboard 2.0 com MQTT: gauges, gráficos e controles

Um tutorial prático do Node-RED Dashboard 2.0: assine telemetria MQTT ao vivo, exiba-a em gauges e em um gráfico de linha e controle um dispositivo com um switch que publica comandos e reflete o estado real.

9 min de leitura

Cliente OPC UA no Node-RED para MQTT com topics ISA-95

Use o node-red-contrib-opcua para assinar variáveis OPC UA em um CLP ou servidor de simulação, mapear node IDs para topics MQTT no estilo ISA-95 e publicar os valores com qualidade e timestamps.

10 min de leitura

Todos os artigos →

Seu broker MQTT privado está a poucos cliques de distância

Crie uma conta, receba as credenciais do seu broker e conecte seu primeiro dispositivo — sem servidores para instalar, atualizar ou escalar.