Flota heterogénea — 3 marcas de PLC distintas, un solo túnel VPN (Modbus TCP)
Una panadería tiene mezcladora Schneider, cámara Carel y horno WAGO. Las 3 hablan Modbus TCP pero con quirks distintos. Cómo configurarlas en un solo tenant de Rela AI sin perder horas debuggeando byte order.
Flota heterogénea — 3 marcas de PLC, un túnel VPN
Modbus TCP es la lingua franca de la automatización industrial: prácticamente cualquier marca de PLC lo habla. Pero "habla Modbus" no significa "habla igual" — cada fabricante tiene quirks de byte order, convención de direcciones y límites de conexiones concurrentes. Este caso muestra cómo Rela AI maneja una flota mixta sin que el cliente tenga que normalizar nada del lado del PLC.
Resumen ejecutivo
El cambio de juego: un solo tenant de Rela maneja PLC de marcas distintas como si fueran del mismo fabricante. El AHI, el agente IA, las reglas de alarma y los KPIs operan sobre eventos unificados — la heterogeneidad queda atrapada en la configuración de cada source y no se filtra al pipeline predictivo.
| Antes | Después |
|---|---|
| Cada marca exige una integración SCADA distinta | 3 sources Modbus en Rela, 1 click cada uno |
| Operadores aprenden 3 SCADAs diferentes (uno por marca) | Un solo dashboard, mismo lenguaje para los 3 equipos |
| Reglas duplicadas por marca: una para hornos Schneider, otra para hornos WAGO | Una sola regla "horno temperatura > 280" — tag_enrichment discrimina |
| Cuando una sonda deriva en una marca, no hay aprendizaje cruzado | El detector de anomalías ML aprende patrones independientes por activo, sin importar la marca |
¿Para qué sirve?
- Vigilar máquinas de marcas distintas que ya están en planta sin reemplazar nada.
- Centralizar el inbox de alarmas: una sola pantalla, un solo agente IA, escalation común.
- Aprovechar reglas determinísticas y mantenimiento predictivo sobre los activos que más importan, no solo los que ya están en un SCADA homogéneo.
- Entender en qué se diferencian las marcas (byte order, addressing) una sola vez durante la configuración, y olvidarlo después.
Antes de empezar — por qué configurás vos el peer
Aun con flota multi-marca, el modelo de acceso no cambia: el cliente crea su propio peer WireGuard, Rela nunca recibe credenciales de VPN corporativa.
Tres razones críticas (las 9 completas en Por qué un túnel dedicado):
- Mínimo privilegio: tu VPN corporativa abre toda tu red (ERP, mail, SharePoint, OT). Rela solo necesita la subnet de los 3 PLC. Una brecha del lado de Rela afecta una subnet OT, no toda la empresa.
- Revocación en segundos: borrás el peer en tu router cuando quieras, sin tickets ni coordinar con nosotros. La llave privada vive en tu hardware y no sale nunca.
- Compliance (IEC 62443, NIST SP 800-82, SOC 2, ISO 27001): todas exigen separación IT/OT vía túnel dedicado. Compartir credenciales corporativas es findable directo en cualquier auditoría.
El modelo peer-dedicado funciona idéntico para 1, 3 o N PLC detrás de la misma LAN — el sitio físico decide el número de túneles, no las máquinas. Compartir credenciales sale del modelo self-service y escala a deployment custom enterprise.
¿Cómo funciona?
flowchart LR
M["Schneider M340<br/>Mezcladora<br/>192.168.10.55"] --> R[("Router del cliente<br/>WireGuard + DNAT")]
C["Carel pCO5 plus<br/>Camara fermentacion<br/>192.168.10.50"] --> R
W["WAGO 750-881<br/>Horno rotativo<br/>192.168.10.65"] --> R
R -- "1 solo tunnel" --> CONC[("Concentrador<br/>Rela VPN")]
CONC --> WORKER[("Cloud Run worker<br/>3 modbus_listener tasks")]
WORKER --> PIPE[("Pipeline unificado<br/>_machine_events")]
PIPE --> AGENT[("Agente IA<br/>Vigilante Planta")]
PIPE --> MAST[Asset Mezcladora]
PIPE --> CAST[Asset Camara]
PIPE --> WAST[Asset Horno]El truco: las 3 tareas de listener corren en el mismo worker, cada una decodifica según su propia configuración (byte order específico por registro), pero todas emiten al mismo _machine_events. El agente IA y las reglas no saben — ni les importa — que vienen de marcas distintas.
Parámetros / Configuración
Byte order por marca (la gotcha más común)
modbus_byte_order se configura por registro, no por source — porque algunos PLC mezclan convenciones según el firmware o el DB. Defaults realistas:
| Marca / firmware | Default float32 | Default 32-bit int | Notas |
|---|---|---|---|
| Schneider Modicon M340 / M580 | big_endian_swap (CDAB) | big_endian (ABCD) | Word-swap es histórico de Modicon. Verificar con mbpoll |
| Carel pCO5+ / pCOWeb | big_endian (ABCD) | big_endian | Estándar Carel |
| WAGO 750-881 | big_endian (ABCD) | big_endian | Configurable en TIA-equivalente, default ABCD |
| Siemens S7 con módulo Modbus | big_endian (ABCD) | big_endian | Big-endian nativo S7 |
| Allen-Bradley vía gateway Modbus | little_endian (DCBA) | little_endian | Inverso a Schneider |
| Omron CJ/CP | big_endian (ABCD) | big_endian | Estándar |
Tip operativo: ante la duda, leer un registro de un valor conocido (ej: setpoint =
25.0) con los 4 byte orders y ver cuál devuelve el número correcto. Toma 5 minutos y evita días de debugging.
Convención de direcciones
Rela usa la dirección 0-based de la wire Modbus (la que pymodbus envía). Conversión rápida desde la doc del fabricante:
| Vendor doc dice | En Rela poné |
|---|---|
Modicon 40101 (5-digit) | address 100, FC 3 |
Modicon 30015 | address 14, FC 4 |
| Carel "register 1" | address 0, FC 3 |
| WAGO "%MW100" | address 100, FC 3 |
| Siemens DB1.DBW0 vía Modbus | depende del mapping en TIA |
Límites de conexiones concurrentes
| PLC | Conexiones concurrentes | Implicación |
|---|---|---|
| Schneider M340 | 8 | Sobra para Rela + SCADA + HMI |
| Schneider M580 | 16 | Holgado |
| Carel pCOWeb | 4 | Crítico: SCADA + Rela + HMI + tablet ya satura |
| WAGO 750-881 | 4-8 (firmware) | Verificar antes de agregar conexiones |
| Siemens S7 con CM 1241 | 1-3 | Muy escaso — Rela puede ser la única |
Mapeo Modbus de la flota (resumen ejecutable)
Mezcladora — Schneider Modicon M340 (10.200.7.55:502, unit_id 1)
| Registro | Address | FC | Tipo | Byte order | Unidad |
|---|---|---|---|---|---|
| motor_torque | 100 | 3 | float32 | big_endian_swap | % |
| motor_speed | 102 | 3 | float32 | big_endian_swap | rpm |
| motor_temperature | 104 | 3 | float32 | big_endian_swap | °C |
| mixing_time_seconds | 106 | 3 | uint16 | big_endian | s |
| motor_running | 50 | 1 | bool | — | 0/1 |
| alarm_overpressure | 60 | 1 | bool | — | 0/1 |
Cámara fermentación — Carel pCO5+ (10.200.7.50:502, unit_id 1)
| Registro | Address | FC | Tipo | Byte order | Unidad |
|---|---|---|---|---|---|
| measured_temperature | 2 | 3 | float32 | big_endian | °C |
| measured_humidity | 4 | 3 | float32 | big_endian | % |
| cycle_phase | 10 | 3 | uint16 | big_endian | 1-5 |
| door_open | 1 | 2 | bool | — | 0/1 |
| probe_t_alarm | 20 | 2 | bool | — | 0/1 |
Horno rotativo — WAGO 750-881 (10.200.7.65:502, unit_id 1)
| Registro | Address | FC | Tipo | Byte order | Unidad |
|---|---|---|---|---|---|
| chamber_temperature | 200 | 3 | float32 | big_endian | °C |
| deck_temperature | 202 | 3 | float32 | big_endian | °C |
| fan_speed | 204 | 3 | uint16 | big_endian | % |
| batch_count | 206 | 3 | uint32 | big_endian | piezas |
| door_open | 10 | 2 | bool | — | 0/1 |
| alarm_overheat | 11 | 2 | bool | — | 0/1 |
¿Cómo usarlo?
Paso 1 — Crear el túnel VPN (uno solo, igual que en flotas homogéneas)
Sidebar -> Settings -> Connectivity -> etiqueta Panaderia - Produccion -> descargar .conf -> importar en router.
Paso 2 — DNAT de las 3 IPs en el router
/ip firewall nat
add chain=dstnat dst-address=10.200.7.55 dst-port=502 \
protocol=tcp action=dst-nat to-addresses=192.168.10.55 to-ports=502
add chain=dstnat dst-address=10.200.7.50 dst-port=502 \
protocol=tcp action=dst-nat to-addresses=192.168.10.50 to-ports=502
add chain=dstnat dst-address=10.200.7.65 dst-port=502 \
protocol=tcp action=dst-nat to-addresses=192.168.10.65 to-ports=502
/ip firewall filter
add action=accept chain=forward in-interface=rela-vpn src-address=10.200.0.0/16Paso 3 — Crear los 3 activos
| Campo | Mezcladora | Cámara | Horno |
|---|---|---|---|
| Nombre | Mezcladora Espiral | Cámara Fermentación 1 | Horno Rotativo 1 |
| Asset code | MEZ-01 | LIE-01 | HOR-01 |
| Asset type | mixer | fermentation_chamber | rotary_oven |
| Criticidad | medium | high | high |
| Plant | Panadería Centrale | Panadería Centrale | Panadería Centrale |
| Area | Producción | Producción | Producción |
Paso 4 — Agente IA común
Sidebar -> Alarmas -> Agentes de máquina -> + Nuevo.
Nombre: Vigilante Planta
Modelo: gemini-3.1-pro-preview
Auto-task: si -> Departamento "Mantenimiento"
Auto-notify: si -> WhatsApp del jefe de planta
Escalation: si (5 min / 15 min / 30 min)Un solo agente cubre las 3 marcas. Las reglas filtran por tag_enrichment.machine_type cuando hace falta especialización.
Paso 5 — Crear los 3 sources Modbus con la config específica de cada marca
Sidebar -> Alarmas -> Fuentes -> + Nueva fuente, repetir 3 veces.
Source 1: Mezcladora Schneider
Source ID: mezcladora-schneider
Agent: Vigilante Planta
Protocol: Modbus TCP
Modbus host: 10.200.7.55
Modbus port: 502
Unit ID: 1
Registros:
- motor_torque addr=100 fc=3 type=float32 byte_order=big_endian_swap unit="%"
- motor_speed addr=102 fc=3 type=float32 byte_order=big_endian_swap unit="rpm"
- motor_temperature addr=104 fc=3 type=float32 byte_order=big_endian_swap unit="°C"
- alarm_overpressure addr=60 fc=1 type=bool emit=bit_flip
Field mapping:
tag_enrichment:
machine_type: mixer
brand: schneider
model: modicon-m340Source 2: Cámara Carel
Source ID: camara-carel
Agent: Vigilante Planta
Protocol: Modbus TCP
Modbus host: 10.200.7.50
Modbus port: 502
Unit ID: 1
Registros:
- measured_temperature addr=2 fc=3 type=float32 byte_order=big_endian unit="°C"
- measured_humidity addr=4 fc=3 type=float32 byte_order=big_endian unit="%"
- door_open addr=1 fc=2 type=bool emit=bit_flip
Field mapping:
tag_enrichment:
machine_type: proofer
brand: carel
model: pco5plusSource 3: Horno WAGO
Source ID: horno-wago
Agent: Vigilante Planta
Protocol: Modbus TCP
Modbus host: 10.200.7.65
Modbus port: 502
Unit ID: 1
Registros:
- chamber_temperature addr=200 fc=3 type=float32 byte_order=big_endian unit="°C"
- deck_temperature addr=202 fc=3 type=float32 byte_order=big_endian unit="°C"
- fan_speed addr=204 fc=3 type=uint16 unit="%"
- alarm_overheat addr=11 fc=2 type=bool emit=bit_flip
Field mapping:
tag_enrichment:
machine_type: oven
brand: wago
model: 750-881Paso 6 — Vincular cada source a su activo
Sidebar -> Activos -> editar cada uno -> agregar event_source_ids correspondiente.
Paso 7 — Una sola regla que abarca múltiples marcas
Sidebar -> Alarmas -> Reglas -> + Nueva regla.
Nombre: Horno sobrecalentamiento
Source: (vacío — aplica global)
Conditions:
- field: machine_type
operator: eq
value: oven
- field: chamber_temperature
operator: gt
value: 280
Recurrence:
count_threshold: 3
window_minutes: 2
Actions:
- change_severity: critical
- create_task:
title: "Sobrecalentamiento horno"
priority: urgent
department_id: <Mantenimiento>
- trigger_escalationPor qué importa: esta regla aplica a TODOS los hornos del tenant (hoy WAGO 750-881, mañana si agregás un Schneider o Siemens). La discriminación la hace tag_enrichment.machine_type, no el source_id. Reusable y libre de duplicación.
Casos de uso reales
Una sola alarma para 3 marcas distintas
Operador deja la puerta del horno abierta + el door_open del WAGO flippea a 1. Misma lógica que el evento door_open de la cámara Carel en el caso proofer-fleet-modbus — pero el horno tiene su propio bit_flip y su propio activo. El agente envía dos WhatsApps (uno por máquina), no uno mezclado.
Mantenimiento predictivo cruzado
El motor de la mezcladora (Schneider) muestra motor_temperature deriva +3°C sostenido durante 7 días. AHI baja de A a C. El detector ML aprendió el patrón normal de ESA mezcladora (no el patrón promedio de mezcladoras Schneider en general — el modelo es por activo). Genera task PM "Revisar enfriamiento motor" 2 semanas antes del fallo proyectado.
Lo mismo pasaría si la marca cambiara a Siemens o ABB — el modelo es por activo, no por marca. Cero entrenamiento previo, cero migración.
Comparación cross-brand
/dashboard/operational muestra los 3 activos lado a lado:
- Mezcladora MEZ-01: AHI 92 (A), última falla hace 180 días, RUL 90 días.
- Cámara LIE-01: AHI 78 (B), 2 alarmas críticas últimos 7 días, RUL 30 días.
- Horno HOR-01: AHI 85 (B), uso 1200 horas/mes, RUL 120 días.
El gerente ve eso y decide priorizar la cámara aunque sea de marca distinta. Sin Rela tendría que comparar entre 3 SCADAs distintos con interfaces incompatibles.
Limitaciones y supuestos
- Cada marca exige su manual de Modbus. No hay magia. Las direcciones, byte orders y FCs salen del manual del fabricante o se descubren con
mbpolldesde la LAN antes de configurar. - Si una marca expone el dato vía protocolo no-Modbus que el PLC sí habla (típico: Siemens habla S7comm nativamente, Allen-Bradley habla EtherNet/IP CIP), conviene usar ESE protocolo en Rela en lugar de forzar Modbus por compatibilidad. Los listeners nativos extraen más metadata (data type sin ambigüedad, status codes, timestamps de fuente).
- Conexión concurrente saturada: si el cliente ya tiene SCADA + HMI poleando un PLC con
max_connections=4(típico Carel), agregar Rela puede tirar la conexión más vieja. Coordinar el rollout con el equipo de automatización. - Mezclar marcas multiplica vectores de fallo en config. El troubleshooting sin metodología cuesta días. Por eso este doc tiene la matriz de byte_order — es la primera cosa a verificar ante datos raros.
Troubleshooting
| Síntoma | Causa probable | Fix |
|---|---|---|
Mezcladora muestra motor_speed = 1.4e-39 (número absurdo) | byte_order mal: probaste big_endian y la M340 quería big_endian_swap | Cambiar byte_order del registro a big_endian_swap y verificar con mbpoll -t 4 -1 -r 102 -c 2 -F |
| Carel desconectado pero Schneider y WAGO OK | Carel pCOWeb saturado: 4 conexiones tomadas | Cerrar 1 cliente (HMI duplicada, SCADA duplicado) o pedir upgrade a pCOWeb Pro |
| Horno con eventos solo cada 30 min en vez de 10s | min_interval muy alto, o WAGO firmware desactualizado | Bajar min_interval a 10s; si persiste, actualizar firmware WAGO |
| 3 sources verdes pero "machine_type" no aparece en eventos | tag_enrichment no se aplicó (fuente sin field_mapping en config) | Editar source y agregar bloque field_mapping.tag_enrichment |
Reglas con field: machine_type no disparan | tag_enrichment está en source pero la regla evalúa antes de la inyección | Verificar orden de pipeline en logs: field_mapping corre antes de event_rules_engine |
Beneficios clave
- 1 tenant, 1 VPN, N marcas: cualquier combinación de PLC Modbus TCP entra al mismo dashboard sin overhead organizacional.
- Reglas que escalan con la planta:
tag_enrichmentpermite escribir 1 regla por tipo de máquina en vez de 1 por marca; agregar una nueva marca no exige duplicar reglas. - Mantenimiento predictivo agnóstico de marca: el modelo ML aprende patrones por activo, no por marca. Cambiar de PLC en una máquina (upgrade tecnológico) no exige re-entrenar.
- Onboarding pedagógico: la matriz de byte_order y la convención de addresses están documentadas en este caso para que el equipo de implementación no descubra los gotchas en el peor momento (cliente en producción).
- Cross-brand benchmarking: ver lado a lado el AHI y RUL de máquinas heterogéneas en el mismo dashboard — algo imposible con SCADA por marca.
Ver también
- Flota homogénea — 3 cámaras de fermentación del mismo modelo — más simple, mismo principio de túnel único.
- Múltiples máquinas, un solo túnel VPN — el modelo mental "1 túnel = 1 sitio físico".
- Por qué un túnel dedicado — las 9 razones por las que no compartimos la VPN corporativa del cliente.
Panadería — Cámara de Fermentación por VPN (cero a producción)
Paso a paso real: una panadería conecta su cámara de fermentación a Rela AI vía VPN WireGuard, sin saber de qué marca es el PLC y sin tener router empresarial.
Múltiples máquinas, un solo túnel VPN
Cómo agregar horno, amasadoras, compresores y sensores bajo el mismo túnel VPN de la panadería — sin reconfigurar ni tocar el Mikrotik.