Un puente de EtherNet/IP a MQTT te permite sacar datos de controladores Rockwell Logix, variadores y otros dispositivos CIP y llevarlos a dashboards, historiadores y aplicaciones en la nube sin tocar el programa de control. En este tutorial aprenderás la diferencia entre mensajería implícita y explícita, y después usarás la librería de código abierto pycomm3 en un gateway para leer tags Logix y atributos CIP genéricos y publicarlos por MQTT con payloads limpios y autodescriptivos.
EtherNet/IP y CIP en cinco minutos
EtherNet/IP es el Common Industrial Protocol (CIP) transportado sobre Ethernet estándar y TCP/UDP. CIP modela cada dispositivo como un conjunto de objetos con clases, instancias y atributos. Un variador, por ejemplo, tiene un objeto Identity (clase 0x01) que describe fabricante, código de producto y número de serie, además de objetos de aplicación para velocidad, corriente y estado.
El tráfico CIP se presenta en dos variantes:
| Tipo de mensajería | Transporte | Patrón | Uso típico | ¿Adecuado para un gateway? |
|---|---|---|---|---|
| Implícita (I/O) | UDP, puerto 2222 | Conexiones cíclicas y planificadas entre scanner y adapter | Control en tiempo real de E/S y variadores | Rara vez; exige ser el scanner o un adapter configurado |
| Explícita | TCP, puerto 44818 | Petición/respuesta | Lectura de tags, atributos y diagnósticos | Sí, es el enfoque habitual |
Un gateway de monitorización debe usar mensajería explícita. No interfiere con las conexiones de E/S del controlador, no requiere cambios en el proyecto del PLC y se puede añadir o retirar sin paradas. El precio es que se trata de un protocolo de sondeo (polling), así que tú decides con qué frecuencia leer.
Dos tipos de destino
- Controladores Logix (ControlLogix, CompactLogix y, en parte, Micro800). Admiten acceso simbólico a tags: lees
Line1_SpeedoConveyor[3].Statuspor su nombre. Es, con diferencia, la fuente de datos más cómoda. - Dispositivos CIP genéricos (variadores, adaptadores de E/S, scanners, dispositivos de terceros). Exponen atributos de objetos que se leen por número de clase, instancia y atributo, según la documentación del dispositivo o su archivo EDS.
pycomm3 cubre ambos casos: LogixDriver para controladores basados en tags y CIPDriver para mensajes explícitos genéricos. pylogix es otra opción de código abierto muy popular para tags Logix. Siemens, Beckhoff y otras plataformas tienen sus propias vías de acceso; para temas específicos de Rockwell, como la configuración del controlador, consulta nuestra guía Allen-Bradley MQTT.
Requisitos previos
- Python 3.10 o posterior en un gateway que alcance el controlador por TCP 44818.
pycomm3ypaho-mqtt2.x.- La dirección IP del controlador y, en controladores de chasis, el número de slot (por ejemplo
10.0.0.20/0para el slot 0). - Una lista de tags de solo lectura acordada con el ingeniero de control.
python -m venv .venv && source .venv/bin/activate
pip install pycomm3 "paho-mqtt>=2.0"
Paso 1: explorar el controlador
Antes de escribir el puente, confirma la conectividad y lista los tags disponibles. Este script muestra la información del controlador y los primeros tags de ámbito de controlador.
# explore_logix.py - confirm connectivity and list tags
from pycomm3 import LogixDriver
with LogixDriver("10.0.0.20/0") as plc: # IP/slot of the controller
print(plc.info) # vendor, product, revision, name
tags = plc.get_tag_list() # controller-scoped tags
for t in tags[:30]:
print(t["tag_name"], t["data_type_name"])
get_tag_list() descarga las definiciones de tags, incluidos los tipos definidos por el usuario, por lo que puede tardar un poco en proyectos grandes. Los tags de ámbito de programa se leen con la sintaxis Program:MainProgram.TagName.
Paso 2: leer un atributo CIP genérico
Para un variador o cualquier dispositivo CIP sin tags simbólicos, usa mensajes explícitos. El ejemplo lee el nombre del producto del objeto Identity, que implementan todos los dispositivos EtherNet/IP.
# identity.py - read Identity object attribute 7 (Product Name) from any CIP device
from pycomm3 import CIPDriver, Services, ClassCode, SHORT_STRING
with CIPDriver("10.0.0.31") as dev:
resp = dev.generic_message(
service=Services.get_attribute_single,
class_code=ClassCode.identity_object, # 0x01
instance=1,
attribute=7, # Product Name
data_type=SHORT_STRING,
connected=False,
name="product_name",
)
print(resp.value if not resp.error else resp.error)
Sustituye clase, instancia, atributo y tipo de dato por los valores del manual del dispositivo para leer datos de aplicación como la frecuencia de salida o la corriente del motor.
Paso 3: el puente de EtherNet/IP a MQTT
En cada ciclo, el puente lee una lista de tags con una sola petición multitag, publica los valores que han cambiado (o que superan una banda muerta) e informa de los errores de lectura como calidad mala en lugar de conservar en silencio los valores antiguos.
# eip_mqtt_bridge.py - poll Logix tags over EtherNet/IP, publish to MQTT
import json, logging, time, uuid
import paho.mqtt.client as mqtt
from pycomm3 import LogixDriver, CommError
logging.basicConfig(level=logging.INFO)
log = logging.getLogger("eip")
PLC_PATH = "10.0.0.20/0"
POLL_SECONDS = 1.0
TOPIC_ROOT = "connectmq/demo/9e6d/plant1/packaging/line2" # your own random id
MQTT_HOST, MQTT_PORT = "broker.connectmq.com", 1883 # production: 8883 with TLS
MQTT_USER, MQTT_PASS = None, None # production: "YOUR_USERNAME", "YOUR_PASSWORD"
CLIENT_ID = f"eip-bridge-{uuid.uuid4().hex[:8]}" # production: "YOUR_CLIENT_ID"
# PLC tag -> (topic leaf, unit, deadband)
TAGS = {
"Line2_Speed": ("speed", "ppm", 1.0),
"Line2_Running": ("running", "", 0),
"Line2_GoodCount": ("good_count", "count", 0),
"Line2_RejectCount": ("reject_count", "count", 0),
"Filler.MotorCurrent": ("filler_current", "A", 0.2),
}
def mqtt_client():
c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID)
if MQTT_USER:
c.username_pw_set(MQTT_USER, MQTT_PASS)
c.tls_set()
c.will_set(f"{TOPIC_ROOT}/bridge", "offline", qos=1, retain=True)
c.on_connect = lambda cl, u, f, rc, p: cl.publish(f"{TOPIC_ROOT}/bridge", "online", qos=1, retain=True)
c.connect(MQTT_HOST, MQTT_PORT, keepalive=30)
c.loop_start()
return c
def changed(old, new, deadband):
if old is None:
return True
if isinstance(new, (int, float)) and not isinstance(new, bool) and deadband:
return abs(new - old) >= deadband
return new != old
def main():
mq = mqtt_client()
last = {}
while True:
try:
with LogixDriver(PLC_PATH) as plc:
log.info("connected to %s", plc.info.get("product_name"))
while True:
results = plc.read(*TAGS.keys())
if not isinstance(results, list):
results = [results]
now = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
for r in results:
leaf, unit, deadband = TAGS[r.tag]
topic = f"{TOPIC_ROOT}/{leaf}"
if r.error:
if last.get(r.tag, {}).get("quality") != "bad":
mq.publish(topic, json.dumps({"value": None, "quality": "bad",
"error": r.error, "ts": now}), qos=1, retain=True)
last[r.tag] = {"value": None, "quality": "bad"}
continue
prev = last.get(r.tag, {})
if prev.get("quality") == "bad" or changed(prev.get("value"), r.value, deadband):
payload = {"value": r.value, "unit": unit, "type": r.type,
"quality": "good", "ts": now}
mq.publish(topic, json.dumps(payload), qos=1, retain=True)
last[r.tag] = {"value": r.value, "quality": "good"}
time.sleep(POLL_SECONDS)
except CommError as exc:
log.error("EtherNet/IP connection lost: %s - retrying in 5 s", exc)
time.sleep(5)
if __name__ == "__main__":
main()
plc.read() con varios nombres de tag los agrupa en tan pocas peticiones como permita el tamaño de la conexión, lo que supone mucha menos carga para el controlador que una petición por tag. Cada resultado es un Tag con los campos tag, value, type y error, de modo que el puente puede publicar el tipo de dato junto al valor.
Paso 4: ejecutar y verificar
python eip_mqtt_bridge.py
mosquitto_sub -h broker.connectmq.com -p 1883 -i eip-viewer-9e6d -t "connectmq/demo/9e6d/#" -v
connectmq/demo/9e6d/plant1/packaging/line2/speed {"value": 118.0, "unit": "ppm", "type": "REAL", "quality": "good", "ts": "2026-10-16T12:00:01Z"}
connectmq/demo/9e6d/plant1/packaging/line2/running {"value": true, "unit": "", "type": "BOOL", "quality": "good", "ts": "2026-10-16T12:00:01Z"}
El árbol de topics sigue una estructura de estilo ISA-95 (planta, área, línea, punto), así que los mismos consumidores funcionan tanto si los datos vienen de EtherNet/IP como de Modbus u OPC UA. Nuestra guía de topics MQTT ISA-95 explica las convenciones.
Elegir tags y frecuencias de sondeo
No todos los tags de un proyecto Logix merecen un topic. Parte de las preguntas que harán los consumidores: ¿está la línea en marcha?, ¿a qué velocidad?, ¿cuántas unidades buenas y rechazadas?, ¿qué alarmas están activas? y ¿cuánta energía consumen los variadores principales? Normalmente eso supone unas pocas decenas de valores por línea, no miles. Agrúpalos según lo rápido que cambian. Estados y contadores pueden leerse cada segundo y publicarse al cambiar; los valores analógicos lentos, como niveles de depósito o temperaturas, pueden leerse cada cinco o diez segundos con banda muerta. Si un valor solo es útil como total de turno, plantéate dejar que el PLC lo acumule y publicarlo en un ciclo más lento.
Buenas prácticas para producción
- Solo lectura por diseño. No añadas escritura a un puente de monitorización. Si alguna vez necesitas enviar comandos, crea un servicio aparte con lógica de aprobación explícita.
- Usa un conjunto de tags de interfaz. Pide al ingeniero de control que copie los valores que necesitas en un UDT o array dedicado. Así lees una sola estructura en lugar de decenas de tags dispersos, y los cambios de programa no rompen el puente.
- Vigila los límites de conexiones. Los controladores admiten un número limitado de conexiones CIP, compartidas con HMI, SCADA y herramientas de ingeniería. Lo ideal es una conexión de larga duración por puente; evita reconectar en cada ciclo.
- Sondea a la velocidad que necesitas. Un segundo es más que suficiente para contadores y estados de producción. Sondear más rápido añade carga sin aportar información.
- Segmentación de red. Coloca el gateway en la red OT o en una DMZ con acceso solo al puerto 44818 de los controladores y salida por el 8883 hacia el broker. Nuestro artículo sobre MQTT en una DMZ describe la arquitectura.
- Broker privado para datos reales. El servidor MQTT público gratuito sirve para pruebas y prototipos, pero lo puede leer cualquiera. Pasa a un broker MQTT gestionado con TLS y credenciales por broker antes de conectar una línea real.
Solución de problemas
| Síntoma | Causa probable | Solución |
|---|---|---|
CommError al conectar |
IP incorrecta, firewall bloqueando el 44818, slot incorrecto | Haz ping al controlador y revisa la ruta ip/slot |
La lectura de tags devuelve Tag doesn't exist |
Tag de ámbito de programa o errata | Usa Program:Name.Tag y revisa get_tag_list() |
| Funciona y falla al cabo de horas | El controlador o el switch cortó la conexión | El bucle de reintento reconecta; revisa el uso de conexiones del controlador |
| El mensaje genérico devuelve un error de estado CIP | Clase, instancia, atributo o tipo de dato incorrectos | Consulta el manual del dispositivo o el archivo EDS |
| Los valores se actualizan despacio | Lista de tags grande dividida en muchas peticiones | Usa un UDT de interfaz y reduce el número de tags |
Dónde encaja ConnectMQ
ConnectMQ es el lado cloud de esta cadena: un broker MQTT gestionado con TLS en el puerto 8883 y WebSockets para dashboards en el navegador. Detecta estructuras de topics ISA-95, de modo que la vista de dispositivos agrupa líneas y máquinas automáticamente, y las reglas de alerta por umbral pueden avisar cuando un contador de rechazos o la corriente de un motor se salen de sus límites. Con los tags ya organizados por línea y máquina, el mismo flujo puede alimentar un gemelo digital 3D de la planta, por ejemplo una línea de envasado en Querétaro o en Barcelona. Si eres nuevo en los brokers MQTT gestionados, mira cómo se compara un broker MQTT en la nube con el autoalojamiento y, para la arquitectura completa, lee la guía de MQTT para IoT industrial.
Empieza con ConnectMQ
Conecta hoy tu primera línea EtherNet/IP. Regístrate en app.connectmq.com, elige el plan Serverless gratuito sin tarjeta de crédito, crea un broker y pega en el puente el usuario, la contraseña y el client ID generados. Tus tags Logix aparecerán en el explorador de topics de ConnectMQ en cuestión de segundos.
Preguntas frecuentes
¿Leer tags por EtherNet/IP afecta al programa del PLC?
La mensajería explícita usa los recursos de comunicación del controlador, no sus conexiones de E/S, y no modifica el programa. Mantén un sondeo moderado y pocas conexiones para no afectar a HMI y SCADA.
¿Puedo usar mensajería implícita de E/S para un gateway MQTT?
Es posible si el gateway se configura como adapter en el árbol de E/S del PLC, pero eso requiere un cambio en el proyecto. Para monitorización, la lectura explícita de tags es más sencilla y no intrusiva.
¿Funciona pycomm3 con Micro800 y con controladores PLC-5 o SLC más antiguos?
pycomm3 está orientado a controladores Logix y tiene soporte parcial para Micro800; las familias PLC-5 y SLC más antiguas usan otro direccionamiento. Consulta la documentación de la librería para tu controlador concreto.
¿Qué puerto usa la mensajería explícita de EtherNet/IP?
El puerto TCP 44818. El tráfico de E/S implícito usa el puerto UDP 2222.



