MQTT Industrial: Ejemplos Prácticos para Conectar Tu Planta
Ejemplos prácticos de MQTT en entornos industriales: monitorización de PLCs, alertas en tiempo real y dashboards de producción. Con código listo para usar.
MQTT Industrial: Ejemplos Prácticos para Conectar Tu Planta
MQTT nació en 1999 para monitorizar oleoductos por satélite. Tenía que funcionar con ancho de banda ridículo, conexiones inestables y dispositivos con la potencia de una calculadora. Veinticinco años después, esos mismos requisitos lo han convertido en el protocolo rey del IoT industrial. Si buscas mqtt industrial ejemplos que puedas replicar en tu planta, estás en el sitio correcto: aquí no hay diagramas teóricos bonitos, sino código y configuraciones que funcionan en producción.
El problema con la mayoría de artículos sobre MQTT industrial es que se quedan en la teoría. Te explican qué es publish/subscribe, te ponen un diagrama con flechitas y te dejan solo. Aquí vamos a hacer lo contrario: cuatro ejemplos reales, con el código, la configuración del broker y la arquitectura que los sostiene. Empezando por lo básico y terminando con un sistema completo que podrías desplegar mañana en tu planta.
Por qué MQTT se ha comido la industria
Antes de entrar en los ejemplos, conviene entender por qué MQTT ha ganado la partida a otros protocolos en entornos industriales. No es casualidad ni moda: tiene ventajas técnicas concretas que encajan como un guante en las restricciones de una planta de producción.
Ligero de verdad
El overhead de un mensaje MQTT es de 2 bytes. Compara eso con HTTP (cientos de bytes de cabeceras) o con OPC UA (decenas de bytes de estructura). En una planta con 200 sensores publicando cada segundo por una conexión 4G, esa diferencia se traduce en megabytes de ahorro al día y en una latencia que se mide en milisegundos, no en cientos de milisegundos.
Desacoplado y resiliente
El modelo publish/subscribe significa que el PLC publica datos en un topic y se olvida. No necesita saber quién los consume, ni cuántos suscriptores hay, ni si están conectados en ese momento. Si el suscriptor se cae, el broker retiene los mensajes (con QoS 1 o 2) y los entrega cuando vuelve. Eso es oro puro en un entorno donde el servidor de base de datos puede reiniciarse por mantenimiento sin que pierdas ni un dato del PLC.
Soporte nativo en PLCs modernos
Hace cinco años necesitabas un gateway externo para que un PLC hablara MQTT. Hoy, los principales fabricantes lo integran de serie:
- Siemens S7-1500/1200: cliente MQTT nativo desde firmware V4.4 con el bloque LMQTT_Client.
- Beckhoff TwinCAT 3: librería Tc3_IotMqtt con soporte TLS y Sparkplug B.
- Schneider M340/M580: módulo de comunicación MQTT integrado en EcoStruxure.
- Omron NX/NJ: función MQTT nativa en Sysmac Studio.
- Wago PFC200: cliente MQTT con Node-RED preinstalado.
Si tu PLC no soporta MQTT de forma nativa (la mayoría de los Siemens S7-300/400, Allen-Bradley SLC/MicroLogix, PLCs antiguos en general), la solución es un gateway Edge — un equipo pequeño que lee del PLC por su protocolo nativo (PROFINET, EtherNet/IP, Modbus TCP) y publica en MQTT. Equipos como el Hilscher netFIELD, el HMS Ewon o simplemente un PC industrial con Node-RED hacen ese trabajo.
Configuración del broker: Mosquitto para producción industrial
Todos los ejemplos que vienen a continuación usan Mosquitto como broker MQTT. Es open source, consume pocos recursos y en producción industrial maneja sin despeinarse miles de mensajes por segundo. Pero necesitas configurarlo bien, no con los valores por defecto.
Una configuración de producción para /etc/mosquitto/mosquitto.conf:
# Listener principal con TLS
listener 8883
cafile /etc/mosquitto/certs/ca.crt
certfile /etc/mosquitto/certs/server.crt
keyfile /etc/mosquitto/certs/server.key
tls_version tlsv1.2
require_certificate false
# Listener local sin TLS (solo para servicios en el mismo servidor)
listener 1883 127.0.0.1
# Autenticación
allow_anonymous false
password_file /etc/mosquitto/passwd
# ACLs: cada dispositivo solo publica en su topic
acl_file /etc/mosquitto/acl
# Persistencia (recuperar mensajes retenidos tras reinicio)
persistence true
persistence_location /var/lib/mosquitto/
# Rendimiento
max_inflight_messages 200
message_size_limit 10240
# Logging
log_dest file /var/log/mosquitto/mosquitto.log
log_type warning
log_type error
log_type notice
Y el fichero de ACL (/etc/mosquitto/acl) para que cada PLC solo pueda publicar en sus topics:
# PLC Línea 1 solo publica en su topic
user plc_linea1
topic write planta/linea1/#
# PLC Línea 2
user plc_linea2
topic write planta/linea2/#
# Servicio de dashboard lee todo
user dashboard_reader
topic read planta/#
# Servicio de inserción en BD lee todo
user db_writer
topic read planta/#
Esto no es opcional. Un broker MQTT sin autenticación ni ACLs en una red industrial es una puerta abierta. Cualquier equipo de la red podría publicar datos falsos o suscribirse a información confidencial de producción.
Ejemplo 1: Monitorización de una línea de producción en tiempo real
Escenario: una línea de embotellado con un PLC Siemens S7-1500. Necesitas capturar producción, velocidad, temperatura de llenado y estado de la máquina cada 2 segundos, almacenarlo en PostgreSQL y visualizarlo en Grafana.
Estructura de topics
Antes de escribir código, define una estructura de topics que escale. Esta convención funciona bien:
planta/{area}/{maquina}/{variable}
En nuestro caso:
planta/embotellado/linea1/produccion → {"botellas_ok": 4521, "botellas_nok": 12}
planta/embotellado/linea1/velocidad → {"valor": 320.5, "unidad": "bot/min"}
planta/embotellado/linea1/temperatura → {"llenado": 4.2, "ambiente": 22.8}
planta/embotellado/linea1/estado → {"modo": "produccion", "alarma": false}
Cada mensaje lleva timestamp ISO 8601 generado en el PLC o en el gateway Edge:
{
"ts": "2026-06-13T08:15:32.450Z",
"botellas_ok": 4521,
"botellas_nok": 12
}
Suscriptor Python para inserción en PostgreSQL
#!/usr/bin/env python3
"""
Suscriptor MQTT → PostgreSQL para datos de línea de embotellado.
Ejecutar como servicio systemd para producción.
"""
import json
import logging
import paho.mqtt.client as mqtt
import psycopg2
from psycopg2.extras import execute_batch
from datetime import datetime
from collections import deque
from threading import Timer
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(levelname)s %(message)s')
# Buffer para inserciones batch (más eficiente que INSERT individual)
buffer = deque(maxlen=5000)
FLUSH_INTERVAL = 2 # segundos
FLUSH_SIZE = 100 # filas mínimas para flush
DB_CONFIG = {
"host": "localhost",
"database": "produccion",
"user": "mqtt_writer",
"password": "contraseña_desde_variable_entorno" # En producción: os.environ['DB_PASS']
}
TOPIC_MAP = {
"planta/embotellado/linea1/produccion": "produccion",
"planta/embotellado/linea1/velocidad": "velocidad",
"planta/embotellado/linea1/temperatura": "temperatura",
"planta/embotellado/linea1/estado": "estado",
}
def get_db_connection():
return psycopg2.connect(**DB_CONFIG)
def flush_buffer():
"""Vacía el buffer insertando en batch."""
if not buffer:
Timer(FLUSH_INTERVAL, flush_buffer).start()
return
rows = []
while buffer and len(rows) < 500:
rows.append(buffer.popleft())
try:
conn = get_db_connection()
cur = conn.cursor()
execute_batch(cur, """
INSERT INTO datos_embotellado (time, topic, payload)
VALUES (%s, %s, %s)
""", rows)
conn.commit()
cur.close()
conn.close()
logging.info(f"Insertadas {len(rows)} filas")
except Exception as e:
# Devolver al buffer si falla
for row in reversed(rows):
buffer.appendleft(row)
logging.error(f"Error BD: {e}")
Timer(FLUSH_INTERVAL, flush_buffer).start()
def on_connect(client, userdata, flags, rc):
if rc == 0:
logging.info("Conectado al broker MQTT")
client.subscribe("planta/embotellado/linea1/#", qos=1)
else:
logging.error(f"Conexión fallida: código {rc}")
def on_message(client, userdata, msg):
try:
payload = json.loads(msg.payload.decode())
ts = payload.pop("ts", datetime.utcnow().isoformat())
buffer.append((ts, msg.topic, json.dumps(payload)))
except json.JSONDecodeError:
logging.warning(f"Payload no JSON en {msg.topic}")
client = mqtt.Client(client_id="db_writer_linea1")
client.username_pw_set("db_writer", "contraseña_segura")
client.tls_set(ca_certs="/etc/mosquitto/certs/ca.crt")
client.on_connect = on_connect
client.on_message = on_message
client.connect("broker.planta.local", 8883, keepalive=60)
# Arrancar flush periódico
Timer(FLUSH_INTERVAL, flush_buffer).start()
client.loop_forever()
Detalle clave: inserciones batch. Insertar fila a fila con un commit por mensaje es un desperdicio. Acumulas en un buffer y haces un INSERT batch cada 2 segundos (o cuando acumulas 100 filas). La diferencia de rendimiento es de 10x frente a inserciones individuales. En una planta con 200 tags publicando cada segundo, esto es la diferencia entre que el sistema respire o se ahogue.
Servicio systemd para producción
No ejecutes scripts Python con nohup ni en un screen. Un servicio systemd reinicia automáticamente si el proceso se cae:
# /etc/systemd/system/mqtt-db-writer.service
[Unit]
Description=MQTT to PostgreSQL writer - Línea embotellado
After=network.target mosquitto.service postgresql.service
Wants=mosquitto.service
[Service]
Type=simple
User=mqttwriter
ExecStart=/opt/mqtt-writer/venv/bin/python /opt/mqtt-writer/main.py
Restart=always
RestartSec=5
Environment=DB_PASS=contraseña_segura
[Install]
WantedBy=multi-user.target
sudo systemctl enable mqtt-db-writer
sudo systemctl start mqtt-db-writer
Si el script se cae, systemd lo levanta en 5 segundos. Si quieres saber que se ha caído (porque aunque se recupere solo, te interesa investigar la causa), añade una alerta: un healthcheck que compruebe cuántos reinicios ha tenido el servicio en la última hora.
Ejemplo 2: Sistema de alertas industriales por Telegram
Escenario: cuando la temperatura de un horno supera los 420 °C o un motor consume más de 35 A, quieres un mensaje instantáneo en Telegram con los datos del evento. No mañana en un informe: ahora.
MQTT brilla en este caso porque las alertas son eventos aislados, no flujos continuos de datos. Publicar un mensaje MQTT cuando salta una condición en el PLC es trivial, y un microservicio suscrito reacciona en milisegundos.
Publicación desde el PLC (Siemens TIA Portal)
En el PLC, publicas solo cuando se cumple la condición de alarma. No cada ciclo — solo en el flanco de subida de la condición:
// SCL - Publicar alarma por MQTT cuando temperatura supera límite
IF Temperatura_Horno > 420.0 AND NOT Alarma_Temp_Enviada THEN
MQTT_Publish(
Topic := 'planta/hornos/horno1/alarma',
Payload := CONCAT(
'{"tipo":"temperatura_alta",',
'"valor":', REAL_TO_STRING(Temperatura_Horno),
',"limite":420.0,',
'"ts":"', DT_TO_STRING(FechaHora_Actual), '"}'
),
QoS := 1,
Retain := FALSE
);
Alarma_Temp_Enviada := TRUE;
END_IF;
IF Temperatura_Horno <= 410.0 THEN
Alarma_Temp_Enviada := FALSE; // Reset con histéresis de 10°C
END_IF;
Dos detalles que importan:
- QoS 1 (at least once): quieres garantía de entrega. Una alerta perdida puede significar un horno dañado.
- Histéresis: el reset de la alarma se hace a 410 °C, no a 420 °C. Sin histéresis, si la temperatura oscila alrededor de 420 °C, recibirías una ráfaga de alertas cada segundo. Con histéresis, recibes una alerta al subir y silencio hasta que baje de verdad.
Microservicio de alertas con Python
#!/usr/bin/env python3
"""
Suscriptor MQTT que envía alertas industriales por Telegram.
"""
import json
import requests
import paho.mqtt.client as mqtt
from datetime import datetime
import time
TELEGRAM_TOKEN = "tu_token_de_bot"
TELEGRAM_CHAT_ID = "tu_chat_id"
COOLDOWN = {} # Anti-spam: una alerta por tipo cada 5 minutos
def send_telegram(message):
url = f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage"
requests.post(url, json={
"chat_id": TELEGRAM_CHAT_ID,
"text": message,
"parse_mode": "HTML"
})
def on_message(client, userdata, msg):
data = json.loads(msg.payload.decode())
alert_key = f"{msg.topic}:{data.get('tipo', 'unknown')}"
# Cooldown: no repetir la misma alerta en 5 minutos
now = time.time()
if alert_key in COOLDOWN and now - COOLDOWN[alert_key] < 300:
return
COOLDOWN[alert_key] = now
# Formatear mensaje legible
tipo = data.get("tipo", "desconocida").replace("_", " ").upper()
valor = data.get("valor", "N/A")
limite = data.get("limite", "N/A")
zona = msg.topic.split("/")[2] # "horno1" del topic
message = (
f"🚨 <b>ALARMA: {tipo}</b>\n"
f"📍 Zona: {zona}\n"
f"📊 Valor: {valor} (límite: {limite})\n"
f"🕐 {datetime.utcnow().strftime('%H:%M:%S UTC')}"
)
send_telegram(message)
client = mqtt.Client(client_id="alert_service")
client.username_pw_set("alert_svc", "contraseña")
client.tls_set(ca_certs="/etc/mosquitto/certs/ca.crt")
client.on_message = on_message
client.connect("broker.planta.local", 8883, keepalive=60)
client.subscribe("planta/+/+/alarma", qos=1)
client.loop_forever()
El wildcard planta/+/+/alarma suscribe a las alarmas de cualquier zona y cualquier máquina. Añades un horno nuevo, publicas en planta/hornos/horno2/alarma, y el servicio de alertas lo recoge automáticamente sin tocar una línea de código.
El cooldown de 5 minutos es fundamental. Sin él, una alarma oscilante te bombardea el Telegram hasta que lo silencias — y entonces te pierdes la alarma de verdad.
Ejemplo 3: Gateway Edge con Node-RED para PLCs sin MQTT
Escenario: tienes tres PLCs antiguos (un Siemens S7-300, un Allen-Bradley CompactLogix y un Modbus TCP de un analizador de energía) que no hablan MQTT. Necesitas integrarlos en tu infraestructura MQTT sin cambiar los PLCs.
Este es probablemente el caso más común en la industria real. Nadie tiene una planta con PLCs nuevos y homogéneos. Lo normal es un zoo de equipos de distintas generaciones y fabricantes que tienen que convivir.
Arquitectura del gateway
S7-300 (MPI/PROFINET) ──┐
├── Node-RED en PC industrial ──→ Mosquitto MQTT
CompactLogix (EtherNet/IP)┤ (gateway Edge)
│
Analizador energía ──────┘
(Modbus TCP)
Flow de Node-RED
El flow tiene tres ramas independientes, una por protocolo:
Rama 1 — Siemens S7-300 (nodo node-red-contrib-s7):
[
{
"type": "s7 in",
"name": "S7-300 Temperaturas",
"variable": "DB10,REAL0;DB10,REAL4;DB10,REAL8",
"interval": 5000,
"wires": [["formatear_s7"]]
}
]
El nodo Function formatear_s7 transforma los datos brutos en el formato MQTT estándar:
// Nodo Function: formatear datos del S7-300
const valores = msg.payload;
msg.topic = "planta/tratamiento/reactor1/temperaturas";
msg.payload = JSON.stringify({
ts: new Date().toISOString(),
entrada: valores[0],
proceso: valores[1],
salida: valores[2]
});
return msg;
Rama 2 — Allen-Bradley (nodo node-red-contrib-cip-ethernet-ip):
// Leer tags del CompactLogix cada 3 segundos
msg.topic = "planta/ensamblaje/robot1/estado";
msg.payload = JSON.stringify({
ts: new Date().toISOString(),
ciclos: msg.payload.Program_MainProgram.CiclosTotal,
piezas_ok: msg.payload.Program_MainProgram.PiezasOK,
estado: msg.payload.Program_MainProgram.Estado
});
return msg;
Rama 3 — Modbus TCP (nodo node-red-contrib-modbus):
// Leer registros del analizador de energía
// Registros 0-1: Tensión L1 (FLOAT32)
// Registros 2-3: Corriente L1 (FLOAT32)
// Registros 4-5: Potencia activa (FLOAT32)
const buf = Buffer.from(msg.payload);
msg.topic = "planta/servicios/cuadro_general/energia";
msg.payload = JSON.stringify({
ts: new Date().toISOString(),
tension_l1: buf.readFloatBE(0),
corriente_l1: buf.readFloatBE(4),
potencia_kw: buf.readFloatBE(8)
});
return msg;
Las tres ramas convergen en un nodo mqtt out que publica en Mosquitto. El resultado: tres protocolos distintos, un único flujo MQTT unificado que cualquier servicio puede consumir. Añadir un cuarto equipo es añadir una rama más en Node-RED. Sin tocar el broker, sin tocar los suscriptores, sin tocar las bases de datos.
Rendimiento real: un PC industrial básico (Celeron J6412, 8 GB RAM) corriendo Node-RED como gateway para 20 PLCs y 500 tags consume menos de 400 MB de RAM y el 8% de CPU. Te sobran recursos para duplicar la carga.
Ejemplo 4: Sparkplug B para infraestructuras grandes
Cuando pasas de 10-15 equipos MQTT a 50 o 100, la gestión de topics, payloads y estados de conexión se vuelve caótica si cada instalación inventa su propia convención. Sparkplug B es la especificación que resuelve esto: define una estructura de topics estándar, un formato de payload binario (Protocol Buffers) y gestión de nacimiento/muerte de nodos.
Estructura de topics Sparkplug B
spBv1.0/{group_id}/NBIRTH/{edge_node_id} → Nacimiento del nodo
spBv1.0/{group_id}/NDATA/{edge_node_id} → Datos del nodo
spBv1.0/{group_id}/NDEATH/{edge_node_id} → Muerte del nodo
spBv1.0/{group_id}/DBIRTH/{edge_node_id}/{device} → Nacimiento del dispositivo
spBv1.0/{group_id}/DDATA/{edge_node_id}/{device} → Datos del dispositivo
En una planta real:
spBv1.0/PlantaMadrid/NBIRTH/GatewayLinea1
spBv1.0/PlantaMadrid/DDATA/GatewayLinea1/PLC_Horno1
spBv1.0/PlantaMadrid/DDATA/GatewayLinea1/PLC_Enfriador1
spBv1.0/PlantaMadrid/NDEATH/GatewayLinea1
Por qué Sparkplug B importa en planta
- Report by exception: solo se publican los valores que cambian. Si la temperatura se mantiene estable, no se envía nada. Cuando tienes 5.000 tags, esto reduce el tráfico MQTT un 90%.
- Mensajes de nacimiento/muerte: cuando un gateway se conecta, publica un
NBIRTHcon todos sus tags, tipos de datos y valores actuales. Si se desconecta (o se cae), el broker publica automáticamente unNDEATHusando el mecanismo Last Will and Testament de MQTT. Cualquier aplicación SCADA suscrita sabe al instante que ese nodo está offline. - Tipos de datos definidos: nada de parsear JSON y adivinar si
"valor": "42"es un string o un número. Sparkplug B define cada métrica con su tipo (Int32, Float, Boolean, DateTime, String…) desde el nacimiento. - Estado conocido: una aplicación que se conecta tarde recibe automáticamente el estado completo de todos los nodos a través de los mensajes retenidos de NBIRTH/DBIRTH. No necesita esperar al siguiente ciclo de publicación para saber qué hay.
Implementación con Python (librería sparkplug-b)
#!/usr/bin/env python3
"""
Nodo Edge Sparkplug B básico que publica datos de un PLC.
"""
import sparkplug_b as sparkplug
import sparkplug_b_pb2
import paho.mqtt.client as mqtt
import time
GROUP_ID = "PlantaMadrid"
NODE_ID = "GatewayLinea1"
DEVICE_ID = "PLC_Horno1"
def get_node_birth():
payload = sparkplug.getNodeBirthPayload()
sparkplug.addMetric(payload, "bdSeq", None,
sparkplug_b_pb2.Int64, 0)
return payload
def get_device_birth():
payload = sparkplug.getDeviceBirthPayload()
sparkplug.addMetric(payload, "Temperatura", None,
sparkplug_b_pb2.Float, 0.0)
sparkplug.addMetric(payload, "Presion", None,
sparkplug_b_pb2.Float, 0.0)
sparkplug.addMetric(payload, "Estado", None,
sparkplug_b_pb2.Boolean, False)
sparkplug.addMetric(payload, "Produccion", None,
sparkplug_b_pb2.Int32, 0)
return payload
def publish_data(client, temperatura, presion, estado, produccion):
payload = sparkplug.getDdataPayload()
sparkplug.addMetric(payload, "Temperatura", None,
sparkplug_b_pb2.Float, temperatura)
sparkplug.addMetric(payload, "Presion", None,
sparkplug_b_pb2.Float, presion)
sparkplug.addMetric(payload, "Estado", None,
sparkplug_b_pb2.Boolean, estado)
sparkplug.addMetric(payload, "Produccion", None,
sparkplug_b_pb2.Int32, produccion)
topic = f"spBv1.0/{GROUP_ID}/DDATA/{NODE_ID}/{DEVICE_ID}"
client.publish(topic, payload.SerializeToString(), qos=0)
# Configurar Last Will (NDEATH automático si se pierde la conexión)
death_payload = sparkplug.getNodeDeathPayload()
death_topic = f"spBv1.0/{GROUP_ID}/NDEATH/{NODE_ID}"
client = mqtt.Client(client_id=f"spb_{NODE_ID}")
client.will_set(death_topic, death_payload.SerializeToString(),
qos=1, retain=False)
client.connect("broker.planta.local", 1883, keepalive=30)
client.loop_start()
# Publicar nacimiento
birth_topic = f"spBv1.0/{GROUP_ID}/NBIRTH/{NODE_ID}"
client.publish(birth_topic,
get_node_birth().SerializeToString(), qos=1)
dbirth_topic = f"spBv1.0/{GROUP_ID}/DBIRTH/{NODE_ID}/{DEVICE_ID}"
client.publish(dbirth_topic,
get_device_birth().SerializeToString(), qos=1)
# Bucle de publicación (en producción: leer del PLC)
while True:
# Estos valores vendrían del PLC vía OPC UA, S7, Modbus...
publish_data(client, 385.2, 4.8, True, 12847)
time.sleep(5)
¿Merece la pena Sparkplug B para tu instalación? Si tienes menos de 10 equipos MQTT y un solo suscriptor, probablemente no. JSON plano con una convención de topics coherente te basta. Pero si gestionas múltiples plantas, decenas de gateways y necesitas que aplicaciones SCADA como Ignition o AVEVA consuman los datos de forma estandarizada, Sparkplug B te ahorra semanas de integración.
Seguridad MQTT en entornos industriales: lo que no puedes ignorar
Un broker MQTT sin securizar en una red industrial es como dejar la puerta de la sala de control abierta de noche. Y no es un escenario teórico: en 2023, investigadores de Nozomi Networks encontraron más de 60.000 brokers MQTT expuestos a internet sin autenticación. Algunos controlaban sistemas industriales.
Las cinco medidas mínimas
1. TLS obligatorio. Todo el tráfico MQTT va cifrado. Sin TLS, cualquiera en la red puede leer los datos de producción con un Wireshark. Usa el puerto 8883 (MQTT sobre TLS) y desactiva el 1883 en interfaces que no sean localhost.
2. Autenticación por usuario y contraseña. Cada dispositivo tiene sus credenciales. Nada de allow_anonymous true en producción. Si quieres más seguridad, usa certificados X.509 de cliente — cada PLC o gateway tiene su propio certificado.
3. ACLs granulares. El PLC de la línea 1 solo puede publicar en planta/linea1/#. No puede leer los datos de la línea 2 ni publicar en topics ajenos. Si un equipo se compromete, el daño queda contenido.
4. Segmentación de red. El broker MQTT debe estar en una DMZ o zona intermedia entre la red OT (planta) y la red IT (oficinas/cloud). Nunca en la misma VLAN que los PLCs, y nunca accesible directamente desde internet. Un firewall con reglas explícitas entre las zonas. Esto no es paranoia: es lo que dice la norma IEC 62443 y lo que cualquier auditoría de ciberseguridad industrial va a exigirte.
5. Actualización del broker. Mosquitto, como cualquier software, tiene vulnerabilidades que se parchean con cada versión. Un broker desactualizado en una red industrial es un vector de ataque. Actualiza con regularidad, aunque sea en ventanas de mantenimiento planificadas.
Monitorización del broker
Un broker MQTT en producción necesita monitorización. Mosquitto publica métricas en $SYS/#:
# Mensajes recibidos por segundo
mosquitto_sub -t '$SYS/broker/messages/received' -h localhost
# Clientes conectados
mosquitto_sub -t '$SYS/broker/clients/connected' -h localhost
# Bytes enviados/recibidos
mosquitto_sub -t '$SYS/broker/bytes/#' -h localhost
Recoge estas métricas con Telegraf o un suscriptor dedicado y ponlas en un panel de Grafana. Si el número de clientes baja repentinamente, un gateway se ha desconectado. Si los mensajes por segundo caen a cero, algo se ha roto. Quieres saberlo antes de que te llame el jefe de planta preguntando por qué el dashboard está en blanco.
Dimensionamiento: qué hardware necesitas
Una duda recurrente: ¿qué servidor necesito para montar esto? La respuesta depende de la escala, pero MQTT es sorprendentemente ligero.
| Escala | Equipos MQTT | Mensajes/s | Hardware broker | Hardware BD |
|---|---|---|---|---|
| Pequeña | 5-20 | 50-200 | Raspberry Pi 4 | Mismo equipo |
| Mediana | 20-100 | 200-2.000 | PC industrial (4 cores, 8 GB) | Servidor dedicado (8 GB, SSD) |
| Grande | 100-500 | 2.000-20.000 | Servidor (8 cores, 16 GB) | Servidor potente (32 GB, NVMe) |
| Muy grande | 500+ | 20.000+ | Cluster EMQX/HiveMQ | TimescaleDB cluster |
Para la mayoría de plantas con 50-200 tags publicando cada 1-5 segundos, un PC industrial modesto con Mosquitto, PostgreSQL y Grafana todo junto es más que suficiente. No necesitas cloud ni infraestructura compleja. Un equipo de 500 € con un SSD decente te da cinco años de tranquilidad.
Cuando superas los 5.000 tags o necesitas alta disponibilidad (el broker no puede caerse ni un segundo), entonces sí conviene pasar a un broker de nivel enterprise como EMQX o HiveMQ, que soportan clustering, balanceo de carga y failover automático.
MQTT vs OPC UA: no es uno u otro
Una pregunta que aparece siempre: ¿MQTT o OPC UA? La respuesta correcta es “ambos, cada uno en su capa”.
OPC UA es un protocolo de acceso a datos. Tiene modelado de información rico, descubrimiento de variables, tipos de datos complejos y seguridad integrada. Es perfecto para la comunicación PLC-a-gateway o PLC-a-SCADA dentro de la planta.
MQTT es un protocolo de transporte de mensajes. Es ligero, desacoplado y resiliente. Es perfecto para la comunicación gateway-a-cloud, planta-a-centro-de-datos o cualquier enlace donde la fiabilidad de la conexión no está garantizada.
La combinación ganadora en 2026:
PLC → (OPC UA) → Gateway Edge → (MQTT/Sparkplug B) → Broker → Servicios (BD, dashboards, alertas, ERP)
OPC UA habla con los PLCs. MQTT transporta los datos al mundo exterior. No compiten: se complementan. Y con la especificación OPC UA Pub/Sub sobre MQTT, la línea entre ambos se difumina cada vez más.
Conclusión
MQTT en entornos industriales no es experimentar con tecnología nueva: es la forma más eficiente de sacar datos de tu planta y convertirlos en información útil. Con un broker Mosquitto bien configurado, un par de scripts Python (o un Node-RED) y una base de datos PostgreSQL, montas una infraestructura de monitorización industrial por una fracción de lo que cuesta un historian comercial.
Los cuatro ejemplos que hemos visto — monitorización en tiempo real, alertas por Telegram, gateway para PLCs legacy y Sparkplug B para escalas grandes — cubren el 90% de los escenarios reales que te vas a encontrar. Empieza por el primero, con un PLC y cinco tags. Cuando compruebes que funciona (y funciona), escalar es cuestión de añadir topics y suscriptores.
Los datos están ahí, generándose cada segundo en tus PLCs. La pregunta no es si merece la pena capturarlos, sino cuánto tiempo más puedes permitirte no hacerlo.
¿Quieres implementar MQTT en tu planta y no sabes por dónde empezar? En automatizatodo.com ayudamos a empresas industriales a conectar sus equipos, capturar datos de producción y montar dashboards de planta — desde un piloto rápido con un PLC hasta despliegues multi-planta con Sparkplug B. Cuéntanos tu caso y diseñamos la solución que se adapta a tu instalación.
Sigue leyendo
Python Modbus TCP: Tutorial Completo para Comunicarte con un PLC
Aprende a usar Python con Modbus TCP para leer y escribir datos de un PLC. Tutorial con PyModbus, ejemplos reales y código listo para producción.
Leer artículoBackup SCADA y Plan de Recuperación ante Desastres en la Industria: Guía Práctica
Plan de backup SCADA y recuperación ante desastres paso a paso. Qué respaldar, con qué frecuencia y cómo restaurar tu planta en horas, no en semanas.
Leer artículo