Fabricación inteligente

Seguimiento de paradas de máquina con sensores IoT y MQTT

Registra las paradas de cualquier máquina, antigua o nueva: un ESP32 con un sensor de corriente de pinza publica los estados de marcha y parada por MQTT, y un registrador en Python guarda cada parada con su duración y su motivo.

Ilustración de seguimiento de paradas de máquina con un sensor de corriente de pinza en el cable de un motor, un ESP32 que publica estados de marcha y parada por MQTT y un registro de paradas
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

El seguimiento de paradas de máquina no necesita un controlador nuevo ni un MES caro. En este tutorial colocarás un sensor de corriente en el cable del motor de una máquina, usarás un ESP32 para publicar los estados de marcha y parada por MQTT y ejecutarás un registrador en Python que guarda cada parada con su inicio, fin, duración y motivo. El enfoque funciona en máquinas antiguas sin puerto de datos, que es justo donde los registros manuales son menos fiables.

Por qué funciona el seguimiento de paradas basado en sensores

Los registros en papel y los terminales de operario dependen de que la gente se acuerde de anotar las paradas. Las averías largas suelen registrarse; la espera de diez minutos a una carretilla o un cambio de herramienta rápido, a menudo no. El resultado es una imagen de las paradas sistemáticamente demasiado optimista.

Un sensor de corriente elimina el problema de la memoria. Un motor consume corriente cuando la máquina trabaja y muy poca cuando no, así que el estado puede detectarse automáticamente sin cablear nada en la lógica del armario de control. El único trabajo del operario es añadir un motivo, e incluso eso se le puede pedir en el momento adecuado.

Hardware para cada máquina

Pieza Función Notas
Placa de desarrollo ESP32 Lee el sensor y publica por Wi-Fi Sirve cualquier placa ESP32 habitual
Transformador de corriente de núcleo partido (por ejemplo, tipo SCT-013 con salida en corriente) Mide la corriente del motor sin abrir el circuito Ábrelo alrededor de un solo conductor, nunca del cable completo
Resistencia de carga (burden) Convierte la corriente del TC en tensión Dimensiónala para el rango de corriente esperado
Dos resistencias y un condensador Polarizan la señal a la mitad de la alimentación El ADC del ESP32 no puede leer tensiones negativas
Fuente de 5 V y caja Alimentación y protección Mantenla fuera del armario si es posible

Lo primero, la seguridad: cualquier instalación cerca de conductores de red debe realizarla un electricista cualificado conforme a la normativa local. Un TC de núcleo partido no es invasivo, pero el armario no es lugar para improvisar. El cableado detallado se explica en la guía del sensor de corriente SCT-013 con ESP32.

Paso 1: firmware del ESP32 que publica marcha y parada

El sketch muestrea el sensor durante 200 ms, calcula el valor eficaz (RMS), aplica histéresis y un tiempo de antirrebote para que las caídas breves no generen falsas paradas, y publica un mensaje de estado retenido en cada cambio. También publica la corriente medida cada 30 segundos, lo que te ayuda a ajustar los umbrales más adelante. Usa la librería 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;  // calibrate against a clamp meter
const float RUN_ON_AMPS  = 3.0;          // above this -> running
const float RUN_OFF_AMPS = 2.0;          // below this -> stopped
const unsigned long DEBOUNCE_MS = 5000;  // state must persist this long

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);  // retained
}

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();
  }
}

El mensaje Last Will marca la máquina como OFFLINE si el ESP32 pierde la alimentación o el Wi-Fi, de modo que el registrador nunca confunde un sensor caído con una máquina parada. Comprueba la salida con:

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

Paso 2: elegir los umbrales

Observa el topic current durante un día antes de fiarte de los estados. Normalmente verás tres franjas: casi cero con la máquina apagada, una franja baja cuando está alimentada pero en vacío (bomba hidráulica, refrigerante, husillo parado) y una franja alta cuando trabaja. Fija RUN_ON_AMPS holgadamente por encima de la franja de vacío y RUN_OFF_AMPS un poco por debajo, para que la diferencia dé histéresis. Si vacío y trabajo se solapan, mueve el sensor a un motor que solo funcione durante la producción, como el husillo principal o el accionamiento de la prensa, en lugar de la alimentación general de la máquina.

Paso 3: registrar cada parada en Python

El registrador se suscribe a todas las máquinas, abre un registro de parada cuando una máquina se detiene, lo cierra cuando vuelve a funcionar y lo guarda en SQLite. Los motivos llegan por un topic reason aparte, publicado por una tableta del operario o una botonera, y se asocian al evento abierto o recién cerrado más reciente de esa 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  # ignore retained replays and repeats
        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()

Simula que un operario introduce un motivo:

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

Ten en cuenta que el registrador pone la marca de tiempo al recibir el mensaje. Eso basta para paradas medidas en minutos; si necesitas precisión de segundos, añade una marca de tiempo desde el ESP32 tras sincronizar su reloj con NTP.

Paso 4: convertir el registro en decisiones

Cuando ya hay unas semanas de datos, un puñado de consultas responde a la mayoría de las preguntas:

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;"

Usa el resultado en este orden:

  1. Ordena los motivos por minutos totales para encontrar las mayores pérdidas.
  2. Ordena por número de paradas para encontrar paradas cortas frecuentes, que a menudo tienen soluciones baratas.
  3. Revisa los eventos sin explicar (motivo NULL) y pregunta por qué los operarios no introdujeron uno; normalmente la lista de motivos está mal.
  4. Calcula el tiempo medio entre fallos y el tiempo medio de reparación para los motivos de avería, como se describe en cálculo de MTBF y MTTR.
  5. Alimenta la disponibilidad con el tiempo de funcionamiento para la monitorización del OEE en tiempo real.

La calidad del paso 3 depende de la propia lista de motivos. Una lista demasiado larga, demasiado vaga u organizada por departamentos en lugar de por causas produce datos inservibles, así que lee cómo diseñar códigos de motivo de parada antes del despliegue.

Escalar de una máquina a todo el taller

El primer sensor demuestra el concepto; el valor llega cuando informan todas las máquinas importantes. Algunas prácticas facilitan el despliegue:

  1. Estandariza el árbol de topics. Mantén el patrón plant/area/machine/state en todas partes para que el registrador y los dashboards no necesiten cambios al añadir máquinas.
  2. Da a cada ESP32 un client ID único y sus propias credenciales. En ConnectMQ, los client ID están vinculados a las credenciales emitidas, así que una imagen de firmware clonada con el mismo ID no puede desconectar en silencio a otro sensor.
  3. Registra los umbrales por máquina. Guarda RUN_ON_AMPS y RUN_OFF_AMPS en una hoja de cálculo o un archivo de configuración junto con el conductor en el que se colocó la pinza, para poder configurar igual un sensor de repuesto.
  4. Planifica la cobertura Wi-Fi. Las máquinas y armarios metálicos bloquean la señal. Prueba la intensidad de señal en cada punto de instalación antes de montar la caja.
  5. Vigila la salud de los sensores. Una comprobación diaria de las máquinas que llevan mucho tiempo OFFLINE detecta fuentes de alimentación muertas antes de que dejen huecos en los datos.
  6. Empieza por el cuello de botella. Las paradas de la máquina restrictiva le cuestan a toda la línea, así que instruméntala primero y muestra allí los resultados.

Configuración para producción

El servidor MQTT público gratuito es ideal para pruebas, pero cualquier cosa publicada allí la pueden leer otros, así que no lo uses para datos de producción. Para un despliegue real, crea un broker privado en ConnectMQ y, en el sketch del ESP32, cambia a WiFiClientSecure en el puerto 8883 con el usuario, la contraseña y el client ID que se te hayan emitido. Un broker MQTT gestionado libera al equipo de planta de administrar el broker y, como es un broker MQTT en la nube, el responsable de mantenimiento puede ver las paradas desde cualquier planta. La vista de dispositivos del portal muestra qué sensores están conectados, y sus reglas de alerta por umbral pueden avisarte cuando una máquina informa OFFLINE. Los mismos estados pueden colorear cada máquina en un gemelo digital 3D del taller. Para la hoja de ruta más amplia, consulta la guía de fabricación inteligente.

Empieza con ConnectMQ

Crea un broker gratuito en app.connectmq.com con el plan Serverless y sin tarjeta de crédito, graba el ESP32 con tus credenciales y empieza a registrar paradas reales esta misma semana.

Preguntas frecuentes

¿Puede un sensor de corriente decirme por qué se paró una máquina?

No. Detecta de forma fiable que la máquina se detuvo y durante cuánto tiempo. El motivo procede de una entrada del operario, de los códigos de alarma de la máquina o de una combinación de ambos.

¿En qué conductor debo colocar el sensor?

En un conductor de fase de un motor que solo funcione durante la producción, como el husillo o el accionamiento principal. Abrazar el cable completo anula el campo y la lectura queda cerca de cero.

¿Cómo evito que las pausas cortas cuenten como paradas?

Usa el tiempo de antirrebote del firmware y, en los informes, un filtro de duración mínima. Las paradas muy cortas se analizan mejor como microparadas dentro de la pérdida de rendimiento.

¿Funcionará en máquinas que ya tienen un PLC?

Sí, pero si el PLC ya expone una señal de marcha, leerla mediante un gateway da más detalle. El sensor de corriente es la vía más rápida para máquinas sin controles accesibles.

Artículos relacionados

OEE vs TEEP vs OOE: ¿qué indicador deberías usar?

OEE, OOE y TEEP comparten los mismos factores de rendimiento y calidad, pero se miden frente a bases de tiempo distintas. Esta comparativa resuelve un único ejemplo y enumera las señales MQTT que necesita cada indicador.

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