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:
- Ordena los motivos por minutos totales para encontrar las mayores pérdidas.
- Ordena por número de paradas para encontrar paradas cortas frecuentes, que a menudo tienen soluciones baratas.
- Revisa los eventos sin explicar (motivo NULL) y pregunta por qué los operarios no introdujeron uno; normalmente la lista de motivos está mal.
- 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.
- 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:
- Estandariza el árbol de topics. Mantén el patrón
plant/area/machine/stateen todas partes para que el registrador y los dashboards no necesiten cambios al añadir máquinas. - 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.
- Registra los umbrales por máquina. Guarda
RUN_ON_AMPSyRUN_OFF_AMPSen 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. - 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.
- 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.
- 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.



