Connessione Modbus TCP — Guida Tecnica
Come collegare un PLC Modbus TCP a Rela AI: campi del form, mappa dei registri, modalità cloud_direct vs edge, emit policies, test della connessione e troubleshooting per tipo di errore.
Connessione Modbus TCP
Modbus TCP è il protocollo più comune negli impianti industriali per esporre registri di PLC, RTU e dispositivi di I/O. Rela AI lo supporta nativamente: il tuo PLC si collega direttamente al worker Cloud Run (o via edge gateway) senza middleware aggiuntivi.
A cosa serve
Leggere lo stato dell'apparecchiatura in tempo quasi reale: temperatura del reattore, RPM del motore, contatori di produzione, allarmi di sicurezza, setpoint di processo. Ogni registro che configuri diventa un evento nel pipeline predittivo — viene normalizzato, correlato, alimenta l'Asset Health Index e valuta le regole che hai definito.
Come funziona
Il worker Rela AI apre una connessione TCP al PLC (porta 502 di default), esegue read_holding_registers / read_coils / ecc. a intervalli configurabili (default 5 secondi), decodifica il valore in base al tipo di dato dichiarato (int16, uint32, float32, IEEE-754…), ed emette un evento solo quando il valore cambia in modo significativo (deadband + emit policy). Il circuit breaker sospende i retry dopo 10 cicli falliti consecutivi ed emette l'allarme canonico PLC_UNREACHABLE che l'operatore vede.
Benefici
| Nessun middleware | Connessione diretta PLC → Rela AI. Nessuno SCADA intermedio, nessun broker MQTT obbligatorio. |
| Decodifica dichiarativa | Scegli data type + byte order nell'UI; il worker fa il decode (IEEE-754, two's complement, word swap, ecc.). |
| Polling ottimizzato | Registri contigui raggruppati in una sola read_holding_registers (fino a 125 words per call) per minimizzare round-trip. |
| Emit policies configurabili | Per registro: data_change (delta assoluta/relativa), threshold_crossed, bit_flip per i booleani. |
| Osservabilità SRE-grade | Metriche Prometheus per tenant+source: modbus_poll_latency_seconds, modbus_connection_state, modbus_tag_staleness_seconds. |
Creare una fonte Modbus — passo per passo
1. Vai a fonti
Dashboard → Allarmi → Fonti → Nuova fonte. Scegli il protocollo Modbus TCP.
2. Compila i campi principali
| Campo | Origine | Esempio |
|---|---|---|
| Nome | Libero (identifica la fonte nell'inbox) | Reattore Linea 3 |
| Host | IP o hostname del PLC nella tua rete | 10.200.7.50 |
| Porta | 502 di default (standard Modbus) | 502 |
| Unit ID | Slave ID del dispositivo (1 per un PLC diretto, >1 se c'è un gateway RS-485) | 1 |
| Deployment Mode | cloud_direct se il PLC è raggiungibile da GCP via VPN/VPC; edge se dietro firewall stretto | cloud_direct |
| Edge Gateway ID | Solo se deployment_mode=edge — riferimento al gateway registrato | gw_a1b2c3 |
Se hai configurato una VPN in Impostazioni → Connettività VPN, l'host del PLC è l'IP privato dentro la tua subnet OT (es. 192.168.10.20). Altrimenti il PLC deve essere raggiungibile tramite tunnel — mai diretto su internet.
3. Aggiungi la mappa dei registri
Ogni riga della mappa descrive un registro da leggere. Campi:
| Campo | Significato |
|---|---|
| Tipo di registro | holding (FC03), input (FC04), coil (FC01), discrete (FC02). holding è il più comune. |
| Nome | Etichetta leggibile — es. reactor.temperature. Usata come tag nelle metriche Prometheus e nell'inbox. |
| Address | Base 0. Se il PLC documenta in base 1 (Modicon: 40001 = holding[0]), sottrai 1. |
| Count | Quante words leggere. 1 per int16/uint16, 2 per int32/uint32/float32, 4 per float64. |
| Data type | int16, uint16, int32, uint32, float32, float64. Per coil/discrete: sempre booleano. |
| Byte order | big_endian (ABCD, default), little_endian (DCBA), big_endian_swap (BADC), little_endian_swap (CDAB). |
| Poll interval | Secondi tra le letture (5 default). Minore = più traffico + carico sul PLC. |
4. Configura l'emit policy per registro
Tre modalità, pensate per diversi tipi di segnale:
- data_change — emette quando il valore cambia oltre il deadband (assoluto o relativo %). Per analogici rumorosi dove ogni jitter non conta.
- threshold_crossed — emette esattamente una volta quando il valore attraversa una soglia. Richiede operatore (
>,>=,<,<=) + valore. Utile per allarmi rigidi tipo "temperatura > 90 °C". - bit_flip — emette ad ogni cambio
0→1o1→0. Obbligatorio per coil/discrete (booleani).
5. Salva e testa la connessione
Al salvataggio, vedi il pulsante Test connection sopra la lista dei registri. Cliccandolo:
- Apre una connessione TCP al PLC.
- Legge ogni registro della mappa una volta.
- Mostra RTT in ms + valori decodificati + warnings se qualche registro è fallito.
Se vedi rtt_ms tra 50 e 300 con i valori attesi, sei a posto. Il listener si avvia automaticamente e inizia a emettere eventi al pipeline.
Deployment modes — quando usare ciascuno
cloud_direct
Il worker Rela AI (Cloud Run in europe-west1) apre la connessione TCP. Richiede che l'IP del PLC sia raggiungibile dal nostro VPC — tipicamente tramite Connettività VPN (WireGuard dedicato, vedi VPN / Configurazione).
Usa questa modalità quando:
- Hai WireGuard VPN o site-to-site configurata.
- Il firewall dell'impianto permette connessioni in ingresso sulla porta 502 dall'IP VPN assegnato (10.200.X.X).
- La rete OT è segmentata ma raggiungibile dalla DMZ o dalla rete corporate.
edge
Installi un container rela-ai-edge su un mini-PC / Raspberry Pi dentro l'impianto. Il container apre una connessione in uscita (HTTPS outbound) al worker Cloud Run, e il polling Modbus avviene localmente nell'impianto.
Usa questa modalità quando:
- Il firewall permette solo traffico in uscita HTTPS (443) verso domini specifici.
- Non puoi configurare la VPN (IT non approva, apparecchiatura vecchia, ecc.).
- Vuoi buffering locale in caso di perdita temporanea di internet.
Vedi Edge Gateway per la guida completa di installazione del container.
Decodifica dei dati — riferimento rapido
Analogici (int16, uint16)
Una sola word, 16 bit. int16 interpreta il MSB come segno (two's complement); uint16 va da 0 a 65535.
Esempio: il tuo PLC espone reactor.temperature × 10 in holding[0]:
data_type: uint16
count: 1Poi dividi per 10 nella logica dell'agente (via field mapping o in una regola).
Interi 32-bit (int32, uint32)
Due words consecutive. L'ordine dei byte conta: big_endian (ABCD) è il default di Modicon/Schneider; big_endian_swap (BADC) è usato da Siemens e alcuni dispositivi Allen-Bradley.
Esempio: runtime.hours come uint32 in holding[4-5]:
address: 4
count: 2
data_type: uint32
byte_order: big_endianSe vedi valori assurdi (numero enorme che dovrebbe essere piccolo), prova big_endian_swap — è l'errore più comune.
Virgola mobile (float32 IEEE-754)
Come int32 ma interpretato come float. Il 99% dei PLC usa big_endian (ABCD) ma Siemens spesso usa big_endian_swap (BADC).
Se il test della connessione ti restituisce un numero tipo 3.14e38, hai il byte order sbagliato.
Coil / Discrete (booleani)
Address per bit, non per byte. Ogni coil = 1 bit. Count controlla quanti bit leggi in una singola richiesta (fino a 2000).
Per un E-stop in coil[1]:
register_type: coil
address: 1
count: 1
emit_as: bit_flipTroubleshooting per tipo di errore
Connection refused
Il PLC rifiuta l'handshake TCP. Cause:
- Il servizio Modbus TCP è spento nel PLC (verifica nella config del PLC).
- Il firewall dell'impianto blocca la porta 502 tra l'origine (gateway VPN o Cloud Run) e il PLC.
- C'è un altro client Modbus che occupa l'unico slot disponibile (alcuni PLC accettano solo 1-2 client concorrenti).
Check: dal gateway VPN o dal container edge, nc -zv <PLC_IP> 502. Se fallisce lì, il problema è di rete.
Connection timeout
Il TCP si avvia ma non risponde. Cause:
- PLC spento o in modalità STOP.
- Latenza di rete eccessiva (VPN con molti hop).
- MTU mismatch nel tunnel WireGuard (1420 vs 1500 — raro con Modbus perché i packet sono piccoli, ma possibile).
Check: ping <PLC_IP> dalla stessa origine del worker. RTT >200 ms di solito indica un problema di rete.
Illegal data address (Modbus exception 02)
Stai chiedendo un indirizzo che il PLC non espone. Cause:
- Offset 0-based vs 1-based: Modicon documenta
40001ma l'indirizzo Modbus reale è0. - Word contate male: float32 richiede address + count=2 per leggere 2 word consecutive. Se hai chiesto count=1, il registro successivo legge "la seconda word del float precedente" — caos.
Check: apri il manuale del PLC nella sezione "Mapping Table" e conferma l'indirizzo esatto.
Illegal function (Modbus exception 01)
Stai chiedendo una funzione che il PLC non supporta. Alcuni PLC (vecchi, MicroLogix, ecc.) supportano solo FC03 (holding) e FC06 (write). Tentare FC04 (input registers) fallisce.
Check: cambia register_type a holding e riprova.
Valori assurdi (molto grandi, molto piccoli, 0xFFFF)
Problemi di decodifica, quasi sempre byte order.
| Sintomo | Causa probabile | Fix |
|---|---|---|
| Valore atteso 25.0, letto ~3.4e38 | float32 con byte order invertito | Prova big_endian_swap |
| Valore atteso 1800, letto 0x0708 (1800) come uint16 ma ci si aspettava uint32 | Data type sbagliato | Cambia a uint16 o aggiusta count |
| Valore atteso 1800, letto 1799 o 1801 | Nulla di rotto — il sensore ha jitter | Aggiusta il deadband |
ConnectionError: operation on closed connection
Il PLC ha chiuso la connessione. Tipicamente:
- Idle timeout del PLC (alcuni Schneider M340 chiudono dopo 30s senza richieste).
- Keepalive TCP disabilitato nel worker —
poll_interval_seconds: 5evita questo ma se qualche valore è >30s, può succedere.
Check: keepalive_seconds sulla fonte (default: TCP_KEEPIDLE=30). Se c'è un timeout noto del PLC, abbassalo sotto quel valore.
Il test della connessione funziona ma non arrivano eventi all'inbox
Il polling parte ma i registri non emettono. Cause:
- Emit policy = threshold_crossed: se il valore non ha ancora attraversato la soglia, non emette nulla. Abbassa la soglia per validare.
- Agente disabilitato: verifica che il machine agent assegnato alla fonte sia
enabled: true. - Severità minima dell'agente: se l'agent ha
min_severity=critical, gli eventiinfovengono filtrati prima di arrivare all'inbox.
Come testare la connessione end-to-end
Checklist operativo (in ordine):
- Dashboard → Allarmi → Fonti → la-tua-fonte-modbus: il banner di stato è verde (
connected). - Test connection:
rtt_ms < 300e i sample_values mostrano il valore atteso. - Dashboard → Allarmi → Inbox: entro pochi minuti devi vedere eventi con
event_typeche corrisponde al nome del registro (reactor.temperature,motor.speed, ecc.). - Metriche (se Grafana collegato):
modbus_connection_state{source_id="<tuo-id>"} == 1emodbus_register_reads_totalche sale. - Circuit breaker sano: non devi vedere eventi
PLC_UNREACHABLEnell'inbox. Se li vedi, il tunnel o il firewall hanno un problema intermittente.
Template di dispositivi
Per PLC popolari (Schneider M340, Siemens S7-1500, Allen-Bradley MicroLogix, ecc.), abbiamo device template con la mappa di registri pre-configurata. All'alta di una fonte, clicca Usa template e scegli il tuo modello — i campi principali si auto-riempiono e devi solo aggiustare host, port, unit_id.
Se il tuo dispositivo non c'è, crea una fonte custom e — opzionalmente — converti la configurazione in un template per-tenant da riutilizzare su futuri impianti.
Limitazioni note
- Scritture: al momento solo lettura. Per scritture (setpoint, comandi) usa uno strumento di write Modbus dall'agente.
- Max registri per fonte: 64 registri simultanei. Se ne servono di più, dividi in fonti multiple.
- Precisione temporale: polling ogni 5s (configurabile a 1s minimo). Non è per controllo di ciclo — è condition monitoring.