Manufatura inteligente

Monitoramento de OEE em tempo real com MQTT: construa um serviço de OEE ao vivo

Um tutorial prático de monitoramento de OEE em tempo real: as máquinas publicam estados e contagens via MQTT, um serviço Python calcula disponibilidade, performance e qualidade a cada minuto e publica o OEE ao vivo para qualquer dashboard.

Ilustração de monitoramento de OEE em tempo real com máquinas publicando estado e contagens via MQTT para um serviço que atualiza um medidor de OEE ao vivo
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

O monitoramento de OEE em tempo real substitui a planilha de fim de turno por um número que se atualiza a cada minuto, enquanto ainda há tempo de agir. Neste tutorial você vai construir o pipeline completo: as máquinas publicam estado e contagens via MQTT, um serviço Python calcula disponibilidade, performance e qualidade do turno atual e o resultado é publicado de volta no broker para qualquer dashboard exibir. Você pode rodar todas as peças em um notebook contra um broker real.

Por que o OEE em tempo real supera os relatórios de fim de turno

Um relatório de turno conta o que deu errado ontem. A essa altura o turno acabou, os operadores foram para casa e os motivos estão meio esquecidos. Um OEE ao vivo exibido na linha permite que o supervisor veja a disponibilidade cair depois de uma quebra, perceba a performance diminuindo quando a velocidade é reduzida e reaja enquanto as mesmas pessoas e condições ainda estão ali.

O cálculo automático também acaba com as discussões sobre os dados de entrada. Quando estados e contagens vêm direto da máquina, o número é o mesmo para operadores, supervisores e gerentes, e a conversa passa de "o número está certo?" para "o que vamos fazer a respeito?".

Arquitetura

[Machine / PLC gateway] --state, counts--> [MQTT broker] --> [OEE service] --oee--> [MQTT broker] --> [Dashboards, andon, historian]

Cada componente fala apenas com o broker. As máquinas não sabem que o serviço de OEE existe, e os dashboards não sabem de onde vieram os dados. Esse desacoplamento é o principal motivo para usar um broker; se o padrão for novo para você, leia antes o que faz um broker MQTT.

Topics de uma máquina:

Topic Payload Publicado
.../line2/filler/state {"state": "RUNNING", "ts": ...} A cada mudança, retained
.../line2/filler/counts {"total": 1234, "reject": 12, "ts": ...} A cada 10 s, cumulativo
.../line2/filler/config {"ideal_cycle_s": 1.0, "shift_start": ...} Ao mudar, retained
.../line2/filler/oee {"availability": ..., "oee": ...} A cada 60 s pelo serviço, retained

Os estados são RUNNING, DOWN, IDLE, SETUP e PLANNED_STOP. Paradas planejadas são excluídas do tempo de produção planejado; qualquer outro estado diferente de RUNNING conta como perda de disponibilidade. Os detalhes da fórmula e os casos-limite estão em como calcular o OEE.

Passo 1: simule uma máquina

Use o servidor MQTT público para desenvolvimento; ele serve para testes e protótipos, não para dados de produção. Escolha um ID aleatório para o prefixo dos seus topics, porque a área pública é visível para todos.

# machine_sim.py
import json
import random
import time

import paho.mqtt.client as mqtt

BASE = "connectmq/demo/q8n3d1/plant1/line2/filler"
IDEAL_CYCLE_S = 1.0

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="filler-sim-q8n3d1")
client.connect("broker.connectmq.com", 1883, keepalive=30)
client.loop_start()

shift_start = time.time()
client.publish(f"{BASE}/config", json.dumps({"ideal_cycle_s": IDEAL_CYCLE_S, "shift_start": shift_start}), qos=1, retain=True)

state, total, reject = "RUNNING", 0, 0
client.publish(f"{BASE}/state", json.dumps({"state": state, "ts": time.time()}), qos=1, retain=True)

while True:
    # Mudanças de estado aleatórias
    if state == "RUNNING" and random.random() < 0.02:
        state = random.choice(["DOWN", "IDLE", "SETUP"])
        client.publish(f"{BASE}/state", json.dumps({"state": state, "ts": time.time()}), qos=1, retain=True)
    elif state != "RUNNING" and random.random() < 0.2:
        state = "RUNNING"
        client.publish(f"{BASE}/state", json.dumps({"state": state, "ts": time.time()}), qos=1, retain=True)

    if state == "RUNNING":
        made = int(10 / IDEAL_CYCLE_S * random.uniform(0.8, 0.98))
        total += made
        reject += sum(1 for _ in range(made) if random.random() < 0.02)

    client.publish(f"{BASE}/counts", json.dumps({"total": total, "reject": reject, "ts": time.time()}), qos=1)
    time.sleep(10)

Passo 2: construa o serviço de monitoramento de OEE em tempo real

O serviço assina todas as máquinas da linha com curingas, mantém o histórico de estados do turno atual e recalcula a cada 60 segundos. Ele lida com várias máquinas ao mesmo tempo, porque o nome da máquina é extraído do topic.

# oee_service.py
import json
import threading
import time
from collections import defaultdict

import paho.mqtt.client as mqtt

LINE = "connectmq/demo/q8n3d1/plant1/line2"
INTERVAL_S = 60

machines = defaultdict(lambda: {
    "events": [],          # lista de (ts, state)
    "total": 0, "reject": 0,
    "ideal_cycle_s": None, "shift_start": None,
})
lock = threading.Lock()


def on_connect(client, userdata, flags, reason_code, properties):
    print("Connected:", reason_code)
    client.subscribe([(f"{LINE}/+/state", 1), (f"{LINE}/+/counts", 1), (f"{LINE}/+/config", 1)])


def on_message(client, userdata, msg):
    parts = msg.topic.split("/")
    machine, kind = parts[-2], parts[-1]
    data = json.loads(msg.payload)
    with lock:
        m = machines[machine]
        if kind == "config":
            m["ideal_cycle_s"] = data["ideal_cycle_s"]
            if m["shift_start"] != data["shift_start"]:
                m["shift_start"] = data["shift_start"]
                # Leva o estado atual para o novo turno e descarta o histórico anterior
                m["events"] = [(data["shift_start"], m["events"][-1][1])] if m["events"] else []
        elif kind == "state":
            m["events"].append((data["ts"], data["state"]))
        elif kind == "counts":
            m["total"], m["reject"] = data["total"], data["reject"]


def calculate(m, now):
    start = m["shift_start"]
    events = sorted(e for e in m["events"] if e[0] <= now)
    run = planned_stop = 0.0
    for i, (ts, state) in enumerate(events):
        seg_start = max(ts, start)
        seg_end = events[i + 1][0] if i + 1 < len(events) else now
        if seg_end <= seg_start:
            continue
        if state == "RUNNING":
            run += seg_end - seg_start
        elif state == "PLANNED_STOP":
            planned_stop += seg_end - seg_start
    planned = (now - start) - planned_stop
    total, reject = m["total"], m["reject"]
    a = run / planned if planned > 0 else 0.0
    p = min(m["ideal_cycle_s"] * total / run, 1.0) if run > 0 else 0.0
    q = (total - reject) / total if total > 0 else 0.0
    return {
        "availability": round(a, 4), "performance": round(p, 4), "quality": round(q, 4),
        "oee": round(a * p * q, 4), "run_min": round(run / 60, 1),
        "planned_min": round(planned / 60, 1), "good": total - reject, "ts": now,
    }


client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="oee-service-q8n3d1")
client.on_connect = on_connect
client.on_message = on_message
client.connect("broker.connectmq.com", 1883, keepalive=30)
client.loop_start()

while True:
    time.sleep(INTERVAL_S)
    now = time.time()
    with lock:
        ready = {k: v for k, v in machines.items() if v["shift_start"] and v["ideal_cycle_s"]}
        results = {k: calculate(v, now) for k, v in ready.items()}
    for machine, result in results.items():
        client.publish(f"{LINE}/{machine}/oee", json.dumps(result), qos=1, retain=True)
        print(machine, result)

Rode o simulador em um terminal e o serviço em outro. Depois de um minuto você verá os valores de OEE impressos e publicados. Acompanhe-os em um terceiro terminal:

mosquitto_sub -h broker.connectmq.com -p 1883 -i oee-viewer-q8n3d1 -t "connectmq/demo/q8n3d1/plant1/line2/+/oee" -v

Como o projeto lida com problemas do mundo real

Reinícios do serviço. Como estado e config são retained, um serviço reiniciado recebe imediatamente o estado atual e o início de turno de cada máquina. Ele perde o histórico das mudanças de estado anteriores, então a disponibilidade fica aproximada até o próximo turno. Em produção, persista os eventos em um pequeno banco de dados ou reconstrua-os a partir de um historiador na inicialização.

Mensagens perdidas. As contagens são cumulativas, então uma mensagem de contagem perdida apenas atrasa a atualização. As mensagens de estado usam QoS 1 para que o broker as confirme.

Troca de turno. O gateway da máquina (ou um MES) publica um novo config com o novo shift_start. O serviço zera automaticamente o histórico daquela máquina. Os contadores devem zerar no mesmo momento.

Desvio de relógio. Todos os timestamps vêm da borda. Mantenha os gateways sincronizados por NTP; caso contrário, um gateway com o relógio um minuto adiantado vai gerar durações negativas ou infladas.

Performance acima de 100 por cento. O limite superior esconde um tempo de ciclo ideal errado. Registre um aviso quando ele for acionado e corrija o cadastro.

Exibindo o OEE ao vivo

Qualquer tela compatível com MQTT pode assinar os topics oee. Opções que funcionam bem:

  1. Node-RED Dashboard 2.0 com um medidor para o OEE e três barras para A, P e Q. O guia de dashboard de OEE no Node-RED mostra o passo a passo.
  2. Grafana alimentado por Telegraf e InfluxDB para o histórico de turnos ao lado do valor ao vivo, como no tutorial de dashboard de monitoramento de linha de produção.
  3. Uma página no navegador usando MQTT.js via wss://broker.connectmq.com:8084/mqtt, sem precisar de backend algum.

Mostre os três fatores, não apenas o OEE. Um supervisor que vê disponibilidade em 95 por cento e performance em 70 por cento sabe que deve olhar para velocidade e microparadas, não para quebras. Combine o número ao vivo com o rastreamento de paradas de máquina para que as paradas longas recebam um motivo enquanto ainda estão frescas na memória. Os mesmos dados de estado e contagem também podem alimentar um gêmeo digital da linha.

Adicionando metas e alertas

Um OEE ao vivo fica mais útil quando é comparado com alguma coisa. Vale fazer duas adições logo no início.

Ritmo em relação à meta do turno. Publique no topic config uma meta de contagem para o turno e deixe o serviço acrescentar expected_now e gap a cada resultado: a contagem que a linha já deveria ter atingido neste ponto do turno e a diferença em relação ao real. Os operadores reagem muito mais a "estamos 340 unidades atrás" do que a "o OEE está em 71 por cento", e os dois números vêm dos mesmos dados.

Alertas por limite. Decida quais condições exigem a ação de uma pessoa, por exemplo disponibilidade abaixo de 80 por cento na última hora ou uma máquina em DOWN por mais de dez minutos. Há duas formas simples de implementá-los:

  1. Deixar o serviço de OEE publicar uma mensagem alert quando uma regra disparar e fazer um fluxo Node-RED, uma luz de andon ou uma integração de mensagens assiná-la.
  2. Usar as regras de alerta por limite do portal ConnectMQ sobre os topics oee publicados, que registram cada disparo no log de alertas sem código extra.

Mantenha a lista de regras curta. Alertas que disparam várias vezes por turno sem que ninguém aja devem ser ajustados ou removidos; caso contrário, as pessoas param de ler todos eles.

Janelas móveis. Além do número acumulado do turno, calcule o OEE dos últimos 60 minutos. Valores acumulados do turno mudam devagar no fim do turno e podem esconder um problema recente; uma janela móvel de uma hora o mostra imediatamente. A mesma função calculate funciona se você passar como início da janela o momento de uma hora atrás, em vez do início do turno.

Indo para produção

Saia do broker público antes de conectar máquinas reais. Com o ConnectMQ você cria um broker privado no portal e recebe username, senha e client ID para cada cliente. Altere a conexão nos dois scripts:

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="YOUR_CLIENT_ID")
client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
client.tls_set()  # o repositório de CAs do sistema valida o certificado Let's Encrypt
client.connect("broker.connectmq.com", 8883, keepalive=30)

Um broker MQTT gerenciado tira a manutenção do broker das mãos da equipe da planta, e o explorador de topics ao vivo do portal mostra se todas as máquinas estão publicando antes de você depurar o serviço. Para a visão geral, leia o guia de manufatura inteligente.

Rode seu serviço de OEE no ConnectMQ

Crie um broker gratuito em app.connectmq.com no plano Serverless, sem cartão de crédito, aponte seus gateways e o serviço de OEE para ele e tenha o OEE ao vivo na tela da linha ainda hoje.

Perguntas frequentes

Com que frequência o OEE em tempo real deve ser recalculado?

Uma vez por minuto é um bom padrão. É responsivo o suficiente para o chão de fábrica e evita um número que pula a cada mensagem de contagem.

Onde o cálculo de OEE deve rodar?

Em um único serviço que publica o resultado no broker, para que todos os dashboards mostrem o mesmo valor. Evite recalcular em cada dashboard com lógicas ligeiramente diferentes.

Isso funciona com várias máquinas e linhas?

Sim. O serviço usa assinaturas com curinga e indexa tudo pelo nome da máquina extraído do topic. Acrescente um nível ao curinga para cobrir várias linhas.

O que acontece se uma máquina parar de publicar?

O último estado retained continua no broker. Adicione uma mensagem Last Will no gateway que publique OFFLINE, para que o serviço trate o silêncio como desconhecido em vez de considerar a máquina rodando.

Artigos relacionados

OEE vs TEEP vs OOE: qual indicador você deve usar?

OEE, OOE e TEEP compartilham os mesmos fatores de desempenho e qualidade, mas medem contra bases de tempo diferentes. Esta comparação resolve um único exemplo e lista os sinais MQTT de que cada indicador precisa.

8 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.