Manufatura inteligente

Rastreamento de Paradas de Máquina com Sensores IoT e MQTT

Rastreie paradas em qualquer máquina, antiga ou nova: um ESP32 com sensor de corrente do tipo alicate publica estados de operação e parada via MQTT, e um logger em Python registra cada parada com sua duração e seu motivo.

Ilustração de rastreamento de paradas de máquina com um sensor de corrente tipo alicate no cabo de um motor, um ESP32 publicando estados de operação e parada via MQTT e um registro de paradas
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 rastreamento de paradas de máquina não exige um controlador novo nem um MES caro. Neste tutorial você vai prender um sensor de corrente no cabo do motor de uma máquina, usar um ESP32 para publicar os estados de operação e parada via MQTT e rodar um logger em Python que registra cada parada com início, fim, duração e motivo. A abordagem funciona em máquinas antigas sem porta de dados, que é justamente onde os registros manuais são menos confiáveis.

Por que o rastreamento de paradas baseado em sensores funciona

Registros em papel e terminais de operador dependem de as pessoas se lembrarem de apontar as paradas. As quebras longas costumam ser registradas; os dez minutos esperando uma empilhadeira ou uma troca rápida de ferramenta muitas vezes não. O resultado é um retrato das paradas sistematicamente otimista demais.

Um sensor de corrente elimina o problema da memória. Um motor consome corrente quando a máquina está trabalhando e muito pouca quando não está, então o estado pode ser detectado automaticamente, sem ligação com a lógica do painel de comando. O único trabalho do operador é informar um motivo, e mesmo isso pode ser solicitado no momento certo.

Hardware para cada máquina

Peça Finalidade Observações
Placa de desenvolvimento ESP32 Lê o sensor e publica via Wi-Fi Qualquer placa ESP32 comum funciona
Transformador de corrente de núcleo dividido (por exemplo, do tipo SCT-013, saída em corrente) Mede a corrente do motor sem interromper o circuito Abrace um único condutor, nunca o cabo inteiro
Resistor de carga (burden) Converte a corrente do TC em tensão Dimensione para a faixa de corrente esperada
Dois resistores e um capacitor Polarizam o sinal na metade da alimentação O ADC do ESP32 não lê tensões negativas
Fonte de 5 V e caixa Alimentação e proteção Mantenha fora do painel, se possível

Segurança em primeiro lugar: qualquer instalação perto de condutores da rede elétrica deve ser feita por um eletricista qualificado, seguindo as normas locais (como a NR-10). Um TC de núcleo dividido não é invasivo, mas o painel não é lugar para improviso. A ligação detalhada está no guia do sensor de corrente SCT-013 com ESP32.

Passo 1: firmware do ESP32 que publica operação e parada

O sketch amostra o sensor por 200 ms, calcula o valor RMS, aplica histerese e um tempo de debounce para que quedas curtas não criem paradas falsas e publica uma mensagem de estado retida a cada mudança. Ele também publica a corrente medida a cada 30 segundos, o que ajuda a ajustar os limites depois. Usa a biblioteca PubSubClient.

#include <WiFi.h>
#include <PubSubClient.h>

const char* WIFI_SSID = "YOUR_WIFI";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";
const char* MQTT_HOST = "broker.connectmq.com";
const int   MQTT_PORT = 1883;
const char* CLIENT_ID = "press04-ct-m6j2";
const char* TOPIC_STATE   = "connectmq/demo/m6j2/plant1/stamping/press04/state";
const char* TOPIC_CURRENT = "connectmq/demo/m6j2/plant1/stamping/press04/current";

const int   CT_PIN = 34;
const float AMPS_PER_ADC_RMS = 0.0488;  // calibre com um alicate amperímetro
const float RUN_ON_AMPS  = 3.0;          // acima disto -> operando
const float RUN_OFF_AMPS = 2.0;          // abaixo disto -> parada
const unsigned long DEBOUNCE_MS = 5000;  // o estado precisa persistir por este tempo

WiFiClient net;
PubSubClient mqtt(net);
bool running = false;
unsigned long candidateSince = 0;
unsigned long lastCurrentPub = 0;

float readAmpsRms() {
  const int samples = 2000;
  long sum = 0;
  long sumSq = 0;
  unsigned long start = millis();
  int n = 0;
  while (millis() - start < 200 && n < samples) {
    int v = analogRead(CT_PIN);
    sum += v;
    sumSq += (long)v * v;
    n++;
  }
  float mean = (float)sum / n;
  float variance = (float)sumSq / n - mean * mean;
  return sqrt(max(variance, 0.0f)) * AMPS_PER_ADC_RMS;
}

void publishState(float amps) {
  char payload[96];
  snprintf(payload, sizeof(payload), "{\"state\":\"%s\",\"amps\":%.2f}", running ? "RUNNING" : "STOPPED", amps);
  mqtt.publish(TOPIC_STATE, payload, true);  // retido
}

void connectMqtt() {
  while (!mqtt.connected()) {
    if (mqtt.connect(CLIENT_ID, TOPIC_STATE, 1, true, "{\"state\":\"OFFLINE\"}")) {
      publishState(0);
    } else {
      delay(2000);
    }
  }
}

void setup() {
  Serial.begin(115200);
  analogReadResolution(12);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) delay(500);
  mqtt.setServer(MQTT_HOST, MQTT_PORT);
  connectMqtt();
}

void loop() {
  if (!mqtt.connected()) connectMqtt();
  mqtt.loop();

  float amps = readAmpsRms();
  bool candidate = running ? (amps > RUN_OFF_AMPS) : (amps > RUN_ON_AMPS);

  if (candidate != running) {
    if (candidateSince == 0) candidateSince = millis();
    if (millis() - candidateSince >= DEBOUNCE_MS) {
      running = candidate;
      candidateSince = 0;
      publishState(amps);
    }
  } else {
    candidateSince = 0;
  }

  if (millis() - lastCurrentPub > 30000) {
    char buf[32];
    snprintf(buf, sizeof(buf), "{\"amps\":%.2f}", amps);
    mqtt.publish(TOPIC_CURRENT, buf);
    lastCurrentPub = millis();
  }
}

A mensagem Last Will marca a máquina como OFFLINE se o ESP32 perder a alimentação ou o Wi-Fi, para que o logger nunca confunda um sensor morto com uma máquina parada. Confira a saída com:

mosquitto_sub -h broker.connectmq.com -p 1883 -i dt-viewer-m6j2 -t "connectmq/demo/m6j2/plant1/+/+/state" -v

Passo 2: escolhendo os limites

Acompanhe o topic current por um dia antes de confiar nos estados. Normalmente você verá três faixas: perto de zero quando a máquina está desligada, uma faixa mais baixa quando ela está energizada mas ociosa (bomba hidráulica, refrigeração, fuso parado) e uma faixa mais alta quando está trabalhando. Defina RUN_ON_AMPS com folga acima da faixa ociosa e RUN_OFF_AMPS um pouco abaixo dela, para que o intervalo entre os dois gere histerese. Se as faixas ociosa e de trabalho se sobrepõem, mova o sensor para um motor que só funciona durante a produção, como o fuso principal ou o acionamento da prensa, em vez da alimentação geral da máquina.

Passo 3: registre cada evento de parada em Python

O logger assina todas as máquinas, abre um registro de parada quando uma máquina para, fecha-o quando ela volta a operar e o armazena em SQLite. Os motivos chegam em um topic reason separado, publicado por um tablet do operador ou por uma botoeira, e são associados ao evento mais recente, aberto ou recém-fechado, daquela máquina.

import json
import sqlite3
import time

import paho.mqtt.client as mqtt

PREFIX = "connectmq/demo/m6j2/plant1"
db = sqlite3.connect("downtime.db", check_same_thread=False)
db.execute("""CREATE TABLE IF NOT EXISTS downtime (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    machine TEXT, started REAL, ended REAL, seconds REAL, reason TEXT)""")
db.commit()

current_state = {}


def on_connect(client, userdata, flags, reason_code, properties):
    client.subscribe([(f"{PREFIX}/+/+/state", 1), (f"{PREFIX}/+/+/reason", 1)])


def on_message(client, userdata, msg):
    parts = msg.topic.split("/")
    machine, kind = parts[-2], parts[-1]
    data = json.loads(msg.payload)
    now = time.time()

    if kind == "state":
        new, old = data["state"], current_state.get(machine)
        current_state[machine] = new
        if msg.retain or new == old:
            return  # ignora reenvios retidos e repetições
        if new in ("STOPPED", "OFFLINE") and old == "RUNNING":
            db.execute("INSERT INTO downtime (machine, started, reason) VALUES (?, ?, ?)",
                       (machine, now, "OFFLINE" if new == "OFFLINE" else None))
        elif new == "RUNNING":
            db.execute("""UPDATE downtime SET ended = ?, seconds = ? - started
                          WHERE machine = ? AND ended IS NULL""", (now, now, machine))
        db.commit()
        print(machine, old, "->", new)

    elif kind == "reason":
        db.execute("""UPDATE downtime SET reason = ? WHERE id = (
                        SELECT id FROM downtime WHERE machine = ?
                        ORDER BY started DESC LIMIT 1)""", (data["code"], machine))
        db.commit()
        print(machine, "reason", data["code"])


client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="downtime-logger-m6j2")
client.on_connect = on_connect
client.on_message = on_message
client.connect("broker.connectmq.com", 1883, keepalive=30)
client.loop_forever()

Simule um operador informando um motivo:

mosquitto_pub -h broker.connectmq.com -p 1883 -i tablet-m6j2 -t "connectmq/demo/m6j2/plant1/stamping/press04/reason" -m '{"code":"MAT-WAIT"}'

Observe que o logger registra o horário no recebimento. Isso basta para paradas medidas em minutos; se você precisar de precisão de segundos, adicione um timestamp vindo do ESP32 depois de sincronizar o relógio dele com NTP.

Passo 4: transforme o registro em decisões

Depois de algumas semanas de dados, um punhado de consultas responde à maioria das perguntas:

sqlite3 downtime.db "SELECT machine, reason, COUNT(*) AS stops, ROUND(SUM(seconds)/60,1) AS minutes FROM downtime WHERE ended IS NOT NULL GROUP BY machine, reason ORDER BY minutes DESC LIMIT 15;"

Use o resultado nesta ordem:

  1. Ordene os motivos pelo total de minutos para encontrar as maiores perdas.
  2. Ordene pela contagem para encontrar paradas curtas frequentes, que muitas vezes têm soluções baratas.
  3. Olhe os eventos sem explicação (reason NULL) e pergunte por que os operadores não informaram um motivo; geralmente a lista de motivos está errada.
  4. Calcule o tempo médio entre falhas e o tempo médio de reparo para os motivos de quebra, como descrito em cálculo de MTBF e MTTR.
  5. Use o tempo de operação na disponibilidade do monitoramento de OEE em tempo real.

A qualidade do passo 3 depende da própria lista de motivos. Uma lista longa demais, vaga demais ou organizada por departamentos em vez de causas gera dados inutilizáveis; por isso, leia como criar códigos de motivo de parada antes da implantação.

Escalando de uma máquina para o chão de fábrica

O primeiro sensor prova o conceito; o valor aparece quando toda máquina importante reporta. Algumas práticas deixam a implantação tranquila:

  1. Padronize a árvore de topics. Mantenha o padrão plant/area/machine/state em todo lugar, para que o logger e os dashboards não precisem de mudanças quando máquinas forem adicionadas.
  2. Dê a cada ESP32 um client ID único e credenciais próprias. Na ConnectMQ, os client IDs ficam vinculados às credenciais emitidas, então uma imagem de firmware clonada com o mesmo ID não consegue derrubar silenciosamente outro sensor.
  3. Registre os limites por máquina. Guarde RUN_ON_AMPS e RUN_OFF_AMPS em uma planilha ou arquivo de configuração junto com o condutor em que o sensor foi preso, para que um sensor substituto seja configurado da mesma forma.
  4. Planeje a cobertura de Wi-Fi. Máquinas e painéis metálicos bloqueiam sinais. Teste a intensidade do sinal em cada ponto de instalação antes de fixar a caixa.
  5. Acompanhe a saúde dos sensores. Uma verificação diária das máquinas que estão OFFLINE há muito tempo pega fontes queimadas antes que deixem lacunas nos dados.
  6. Comece pelo gargalo. A parada na máquina restritiva custa a linha inteira, então instrumente-a primeiro e mostre os resultados ali.

Configuração de produção

O servidor MQTT público é ideal para testes, mas tudo o que é publicado ali pode ser lido por outras pessoas. Para uma implantação real, crie um broker privado na ConnectMQ e, no sketch do ESP32, passe para WiFiClientSecure na porta 8883 com o usuário, a senha e o client ID emitidos para você. Um broker MQTT gerenciado mantém a equipe da planta longe da administração do broker e, por ser um broker MQTT na nuvem, permite que o gerente de manutenção veja as paradas de qualquer site. A visão de dispositivos do portal mostra quais sensores estão conectados, e as regras de alerta por limite podem avisar quando uma máquina reportar OFFLINE. Para o roteiro mais amplo, veja o guia de manufatura inteligente.

Comece com a ConnectMQ

Crie um broker gratuito em app.connectmq.com no plano Serverless, sem cartão de crédito, grave suas credenciais no ESP32 e comece a registrar paradas reais ainda esta semana.

Perguntas frequentes

Um sensor de corrente consegue dizer por que uma máquina parou?

Não. Ele detecta com confiabilidade que a máquina parou e por quanto tempo. O motivo vem de um apontamento do operador, dos códigos de alarme da máquina ou de uma combinação dos dois.

Em qual condutor devo prender o sensor?

Em um condutor de fase de um motor que só funciona durante a produção, como o fuso ou o acionamento principal. Prender o cabo inteiro cancela o campo e gera leituras próximas de zero.

Como evitar que pausas curtas sejam contadas como parada?

Use o tempo de debounce no firmware e, nos relatórios, um filtro de duração mínima. Paradas muito curtas são mais bem analisadas como microparadas, dentro da perda de desempenho.

Isso funciona em máquinas que já têm CLP?

Sim, mas, se o CLP já expõe um sinal de operação, lê-lo por meio de um gateway dá mais detalhes. O sensor de corrente é o caminho mais rápido para máquinas sem acesso ao comando.

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.