Questo blocco funzione da eseguire in task Back utilizzabile con modello a cascata, esegue la gestione del protocollo modbus master, per la codifica/decodifica dei frame Modbus viene utilizzato il FB _MModbusFrame. Con Type è possibile selezionare il tipo di protocollo RTU, Ascii ed over IP. Con File è possibile definire il terminale di I/O su cui effettuare la comunicazione.
Attivando Enable sul terminale di I/O viene inviato un frame per eseguire la funzione modbus definita in FCode sul nodo definito in Node. Terminata l’esecuzione del comando viene attivata l’uscita Done. Se l’esecuzione comando ha esito positivo si attiva per un loop di programma l’uscita Ok. Disattivando Enable si azzera Done e l’eventuale Fault, per eseguire nuovamente il comando occorre riabilitare l’ingresso Enable. L’ingresso SpyOn se attivo permette di spiare il funzionamento della FB.
Se FCode è una funzione di lettura, il valore delle variabili a partire dall’indirizzo definito in Address per il numero di variabili definito da Points, viene letto dal sistema slave e trasferito nelle variabili indirizzate da Buffer. Se FCode è una funzione di scrittura, il valore delle variabili presenti nel buffer di memoria indirizzato da Buffer per il numero di variabili definito da Points, è inviato al dispositivo slave che lo trasferirà nelle sue variabili a partire dall’indirizzo definito in Address. Sono supportati i seguenti codici modbus.
Ad ogni esecuzione corretta del comando viene attivata per un loop di programma l’uscita Done ed incrementato il valore di Packets. In caso di errore esecuzione o tempo di esecuzione comando superiore al tempo definito in Timeout, viene attivata per un loop di programma l’uscita Fault ed incrementato il valore in Errors.
Utilizzandolo con i FB dedicati alla gestione dei dispositivi Modbus si semplifica notevolmente lo sviluppo delle applicazioni.
Approfondimenti
- Vedi eventuali note di rilascio raccolta librerie.
- Questo topic tratta l’utilizzo dell’utilty Modbus Master del programma Toolly per testare la connessione Modbus su di un dispositivo.
- In questo topic viene illustato come utilizzare più FB ModBusMaster in cascata per comunicare con più dispositivi.
- In questo topic un semplice programma di comunicazione Modbus.
- In questo topic suggerimenti su comunicazione con più dispositivi Modbus TCP.
- In questo topic informazioni sulla comunicazione Modbus in TCP.
- In questo topic un esempio di rete Modbus RTU tra PLC e dispositivi con accesso ai dispositivi da connessione TCP.
Upgrade list
- Se l’oggetto aggiornato non è nell’ultima versione del package, vedi capitolo “Aggiornamento librerie” in questo articolo.
- Gli oggetti obsoleti sono inseriti nella libreria eLLabObsoleteLib fare riferimento al relativo manuale ed al manuale programmazione in formato pdf.
ModbusMaster_v1
Implementa una gestione ottimizzata della acquisizione dei frames modbus di risposta evitando la dichiarazione del parametro IFTime. Tutti gli altri parametri in ingresso rimangono invariati, i parametri Timeout e Delay ora sono definiti di tipo REAL ed occorre impostarne il valore in secondi.
ModbusMaster_v2
Aggiunta gestione ingresso Absolute, se attivo considera indirizzamento assoluto e non viene sottratto 1 all’indirizzo Modbus inviato nel frame di richiesta. Visto che è stato aggiunto il solo parametro di ingresso Absolute mantenendo identico il funzionamento può sostituire la ModbusMaster_v1 nei progetti esistenti.
ModbusMaster_v3
Ingresso Type di definizione tipo di protocollo ora è di tipo MODBUS_PROTOCOL. I parametri in I/O Timeout, Delay e CTime ora sono di tipo TIME.
Descrizione
Enable (BOOL) Comando abilitazione esecuzione comando modbus. Per rieseguire il comando disabilitare e poi riabilitare questo ingresso.
SpyOn (BOOL) Se attivo permette di spiare il funzionamento della FB (Vedi articolo).
Absolute (BOOL) Per compatibilità con la specifica Modbus il FB sottrae 1 all’indirizzo Modbus. Se bit attivo si considera indirizzamento assoluto e non si esegue la sottrazione.
File (eFILEP) Flusso dati stream da utilizzare per la comunicazione.
Type (MODBUS_PROTOCOL) Tipo di protocollo modbus (Definizione).
Node (USINT) Numero di nodo modbus su cui effettuare il comando (Range da 0 a 255).
FCode (USINT) Codice funzione modbus da eseguire nel comando (Vedi tabella sotto).
Address (UINT) Indirizzo di allocazione variabili su sistema slave. In accordo alle specifiche modbus l’indirizzo inviato nel frame dati è (Address-1) (Range da 16#0001 a 16#FFFF).
Points (USINT) Numero di variabili consecutive su cui opera il comando.
Buffer (PVOID) Indirizzo buffer dati letti o da scrivere.
Timeout (TIME) Tempo massimo esecuzione comando. Se il comando non termina nel tempo definito viene abortito ed attivata l’uscita Fault.
Delay (TIME) Tempo di pausa dopo l’esecuzione del comando modbus.
Done (BOOL) Si attiva al termine della esecuzione comando e rimane attiva fino alla disabilitazione di Enable.
Ok (BOOL) Attivo per un loop se esecuzione comando corretta.
Fault (BOOL) Attivo per un loop se errore esecuzione comando.
CTime (TIME) Tempo di esecuzione comando, invio comando e ricezione risposta.
Packets (UDINT) Numero di comandi modbus eseguiti, incrementato ad ogni comando, raggiunto massimo riparte da 0.
Errors (UDINT) Numero di errori, incrementato ad ogni errore, raggiunto massimo riparte da 0.

FCode, funzioni modbus supportate
16#01 Read coil status (Massimo 250 coils)
16#02 Read input status (Massimo 125 inputs)
16#03 Read holding registers (Massimo 125 registri)
16#04 Read input registers (Massimo 125 registri)
+----+-----------+---+---+---+---+ +----+-----------+-----+--...--+
Query |Node|01/02/03/04|Address|Points | Response |Node|01/02/03/04|Bytes| Data |
+----+-----------+---+---+---+---+ +----+-----------+-----+--...--+
16#05 Force single coil
16#06 Preset single register
+----+-----+---+---+--+--+ +----+-----+---+---+--+--+
Query |Node|05/06|Address|Data | Response |Node|05/06|Address|Data |
+----+-----+---+---+--+--+ +----+-----+---+---+--+--+
16#0F Force Multiple Coils (Massimo 250 coils)
16#10 Preset multiple registers (Massimo 125 registri)
+----+-----+---+---+---+---+-----+--...--+ +----+-----+---+---+---+---+
Query |Node|0F/10|Address|Points |Bytes| Data | Response |Node|0F/10|Address|Points |
+----+-----+---+---+---+---+-----+--...--+ +----+-----+---+---+---+---+
16#41 Read memory bytes (Funzione custom, massimo 250 bytes)
+----+--+---+---+---+---+ +----+--+-------+--...--+
Query |Node|41|Address|Points | Response |Node|41|Points | Data |
+----+--+---+---+---+---+ +----+--+-------+--...--+
16#42 Write memory bytes (Funzione custom, massimo 250 bytes)
+----+--+---+---+---+---+--...--+ +----+--+-------+
Query |Node|42|Address|Points | Data | Response |Node|42|Points |
+----+--+---+---+---+---+--...--+ +----+--+-------+
I codici 16#41 e 16#42 sono stati implementati per compatibilità con i vecchi sistemi programmabili con Remoter.
Messaggi di errore (Eccezione)
Il dispositivo, se non è in grado di eseguire l’operazione richiesta dal comando ricevuto, risponde con un messaggio di errore che prevede il seguente formato:
+-------------+---------------+----------------+---+---+ | Modbus node | Function code | Exception code | CRC | +-------------+---------------+----------------+---+---+
- Modbus node: Indirizzo del dispositivo slave che risponde.
- Function code: Codice funzione con MSB=1 (per indicare l’eccezione); esempio 16#83 (per la lettura 16#03 ) o 16#86 (per la scrittura 16#06).
- Exception code: Codice eccezione.
Codici eccezione
| Code | Name | Description |
|---|---|---|
| 16#01 | Illegal function | E’ stato richiesto un codice funzione che il dispositivo non supporta. |
| 16#02 | Illegal data address | Viene generato in diverse situazioni:
|
| 16#03 | Illegal data value | Il dato definito nella richiesta non è accettato dal dispositivo. |
| 16#04 | Slave device failure | Si è verificato un errore irreversibile mentre lo slave stava tentando di eseguire l’azione richiesta. |
| 16#05 | Acknowledge | Lo slave ha accettato la richiesta e la sta elaborando, ma per farlo sarà necessario un lungo periodo di tempo. Questa risposta viene restituita per evitare che si verifichi un errore di timeout nel master. |
| 16#06 | Slave device busy | Lo slave è impegnato nell’elaborazione di un comando che richiede una lunga elaborazione. |
| 16#07 | Negative acknowledge | Lo slave non può eseguire la funzione di programma ricevuta nella query. |
| 16#08 | Memory parity error | Lo slave ha tentato di leggere la memoria ma ha rilevato un errore di parità. |
Trigger di spy
Se SpyOn attivo è possibile utilizzare la console di spionaggio per verificare il funzionamento della FB. Sono previsti vari livelli di triggers.
Livelli di trigger
| Trigger | Descrizione |
|---|---|
| 16#00000001 | Tx: Invio frame comando modbus. |
| 16#00000002 | Rx: Ricezione frame risposta modbus. |
| 16#40000000 | Er: Errore di esecuzione. |
Esempi
Come utilizzare gli esempi
ST_ModbusMaster: Vengono eseguiti comandi Modbus di lettura/scrittura sia di coils che di registri. E’ possibile utilizzare l’esempio su di un unico sistema SlimLine se usato in seriale collegando tra di loro le porte seriali COM0 e COM1 di un modulo CPU (Usare un cavo cross). Se usato in TCP definendo come indirizzo IP dello slave il localhost ADR(‘127.0.0.1′). E’ possibile utilizzare l’esempio insieme a quello del FB ModbusSlave.
ST_ModbusExchange: Gestita comunicazione Modbus RTU su COM0, attivo server TCP su porta 2000. Alla connessione di un client TCP la comunicazione Modbus si arresta ed i dati ricevuti da socket TCP sono inoltrati sulla porta seriale COM0 permettendo comunicazione Modbus RTU verso i dispositivi connessi in seriale. Il programma si può testare su un modulo MPS054 connettendo tra di loro le porte COM0 e COM1.
LogicLab (Ptp205, ST_ModbusMaster)
PROGRAM ST_ModbusMaster
VAR
i : UDINT; (* Aux counter *)
SerialOrTCP : BOOL := FALSE; (* FALSE:Serial, TRUE:TCP communication *)
CaseNr : USINT; (* Program case *)
Sp : SysSerialPort; (* Serial port *)
TCPClient : SysTCPClient; (* TCP client management *)
MMdb : ModbusMaster_v3; (* Modbus master FB *)
RCoils : ARRAY[0..15] OF BOOL; (* Coil status (Read) *)
RHRegs : ARRAY[0..15] OF WORD; (* Holding registers (Read) *)
WCoils : ARRAY[0..15] OF BOOL; (* Coil status (Write) *)
WHRegs : ARRAY[0..15] OF WORD; (* Holding registers (Write) *)
END_VAR
// *****************************************************************************
// PROGRAM "ST_ModbusMaster"
// *****************************************************************************
// Are executed all possible Modbus commands with a connection over another
// SlimLine. It's possible to test all the three possible Modbus types (Serial
// RTU, serial ascii, TCP).
// -----------------------------------------------------------------------------
// -------------------------------------------------------------------------
// INITIALIZATION
// -------------------------------------------------------------------------
// Program initializations.
IF (SysFirstLoop) THEN
// Serial port settings.
Sp.COM:=ADR('COM0'); //COM port definition
Sp.Baudrate:=115200; //Baudrate
Sp.Parity:='E'; //Parity
Sp.DataBits:=8; //Data bits
Sp.StopBits:=1; //Stop bits
Sp.DTRManagement:=DTR_AUTO_WO_TIMES; //DTR management
Sp.DTRComplement:=FALSE; //DTR complement
Sp.EchoFlush:=FALSE; //Received echo flush
Sp.DTROnTime:=0; //DTR On time delay (mS)
Sp.DTROffTime:=0; //DTR Off time delay (mS)
Sp.FlushTm:=0; //Flush time (mS)
Sp.RxSize:=0; //Rx buffer size
Sp.TxSize:=0; //Tx buffer size
// TCP client settings.
// Port 502 to connect to embed Modbus slave.
// Port 3000 to connect to ModbusSlave FB example.
TCPClient.PeerAdd:=ADR('127.0.0.1'); //Peer address
TCPClient.PeerPort:=3000; //Peer port
TCPClient.LocalAdd:=ADR('0.0.0.0'); //Local address
TCPClient.LocalPort:=0; //Local port
TCPClient.FlushTm:=50; //Flush time (mS)
TCPClient.LifeTm:=20; //Life time (S)
TCPClient.RxSize:=128; //Rx buffer size
TCPClient.TxSize:=128; //Tx buffer size
// Modbus master settings.
MMdb.Absolute:=FALSE; //Absolute addressing
MMdb.Node:=1; //Node number
MMdb.Timeout:=T#1s; //Timeout time
MMdb.Delay:=T#100ms; //Delay time
END_IF;
// -------------------------------------------------------------------------
// MODBUS MANAGEMENT
// -------------------------------------------------------------------------
// Eseguo selezione canale comunicazione.
IF NOT(SerialOrTCP) THEN
// Serial port management.
Sp(Open:=TRUE); //Serial port management
MMdb.File:=Sp.File; //File pointer
MMdb.Type:=MODBUS_PROTOCOL#MDB_RTU; //Modbus protocol type
// MMdb.Type:=MODBUS_PROTOCOL#MDB_ASCII; //Modbus protocol type
ELSE
// TCP client management.
TCPClient(Connect:=TRUE); //TCPClient management
MMdb.File:=TCPClient.File; //File pointer
MMdb.Type:=MODBUS_PROTOCOL#MDB_TCP; //Modbus protocol type
END_IF;
// Manage the modbus master communication.
MMdb(); //Modbus master
IF NOT(SysFIsOpen(MMdb.File)) THEN MMdb.Enable:=FALSE; CaseNr:=0; RETURN; END_IF;
// -------------------------------------------------------------------------
// PROGRAM CASES
// -------------------------------------------------------------------------
// Manage the program cases.
CASE (CaseNr) OF
// ---------------------------------------------------------------------
// Execute a Force multiple coils.
0:
MMdb.Enable:=TRUE; //Modbus enable
MMdb.FCode:=16#0F; //Modbus function code
MMdb.Address:=40000; //Modbus register address
MMdb.Points:=SIZEOF(WCoils); //Modbus register points
MMdb.Buffer:=ADR(WCoils); //Memory buffer address
IF NOT(MMdb.Done) THEN RETURN; END_IF;
MMdb.Enable:=FALSE; //Modbus enable
CaseNr:=CaseNr+1; //Program case
// ---------------------------------------------------------------------
// Execute a Force single coil.
1:
MMdb.Enable:=TRUE; //Modbus enable
MMdb.FCode:=16#05; //Modbus function code
MMdb.Address:=40000+0; //Modbus register address
MMdb.Points:=0; //Modbus register points
MMdb.Buffer:=ADR(WCoils); //Memory buffer address
IF NOT(MMdb.Done) THEN RETURN; END_IF;
MMdb.Enable:=FALSE; //Modbus enable
CaseNr:=CaseNr+1; //Program case
// ---------------------------------------------------------------------
// Execute a Read coil status.
2:
MMdb.Enable:=TRUE; //Modbus enable
MMdb.FCode:=16#01; //Modbus function code
MMdb.Address:=40000+0; //Modbus register address
MMdb.Points:=SIZEOF(RCoils); //Modbus register points
MMdb.Buffer:=ADR(RCoils); //Memory buffer address
IF NOT(MMdb.Done) THEN RETURN; END_IF;
MMdb.Enable:=FALSE; //Modbus enable
CaseNr:=CaseNr+1; //Program case
// ---------------------------------------------------------------------
// Execute a Preset multiple registers.
3:
MMdb.Enable:=TRUE; //Modbus enable
MMdb.FCode:=16#10; //Modbus function code
MMdb.Address:=40000+(16/2); //Modbus register address
MMdb.Points:=SIZEOF(WHRegs)/2; //Modbus register points
MMdb.Buffer:=ADR(WHRegs); //Memory buffer address
IF NOT(MMdb.Done) THEN RETURN; END_IF;
MMdb.Enable:=FALSE; //Modbus enable
CaseNr:=CaseNr+1; //Program case
// ---------------------------------------------------------------------
// Execute a Preset single register.
4:
MMdb.Enable:=TRUE; //Modbus enable
MMdb.FCode:=16#06; //Modbus function code
MMdb.Address:=40000+(16/2); //Modbus register address
MMdb.Points:=0; //Modbus register points
MMdb.Buffer:=ADR(WHRegs[0]); //Memory buffer address
IF NOT(MMdb.Done) THEN RETURN; END_IF;
MMdb.Enable:=FALSE; //Modbus enable
CaseNr:=CaseNr+1; //Program case
// ---------------------------------------------------------------------
// Execute a Read holding registers.
5:
MMdb.Enable:=TRUE; //Modbus enable
MMdb.FCode:=16#03; //Modbus function code
MMdb.Address:=40000+(16/2); //Modbus register address
MMdb.Points:=SIZEOF(RHRegs)/2; //Modbus register points
MMdb.Buffer:=ADR(RHRegs); //Memory buffer address
IF NOT(MMdb.Done) THEN RETURN; END_IF;
MMdb.Enable:=FALSE; //Modbus enable
CaseNr:=0; //Program case
END_CASE;
// [End of file]
LogicLab (Ptp205, ST_ModbusExchange)
PROGRAM ST_ModbusExchange
VAR
CaseNr : USINT; (* Program case *)
MdbRd : ARRAY[0..1] OF WORD; (* Modbus read registers *)
MdbWr : ARRAY[0..1] OF WORD; (* Modbus write registers *)
Fp : eFILEP; (* File pointer *)
Sp : SysSerialPort; (* Porta seriale *)
Sk : SysTCPServer; (* TCP server *)
DExch : DataStreamExch_v1; (* Data stream exchange *)
MMdb : ModbusMaster_v3; (* Modbus master communication *)
END_VAR
// *****************************************************************************
// PROGRAM "ST_ModbusExchange"
// *****************************************************************************
// Gestione comunicazione Modbus con scambio dati tra TCP e seriale.
// -----------------------------------------------------------------------------
// -------------------------------------------------------------------------
// INIZIALIZZAZIONI
// -------------------------------------------------------------------------
// Eseguo inizializzazioni.
IF (SysFirstLoop) THEN
// Apro ed inizializzo porta seriale.
Sp.COM:=ADR('COM0'); // COM port definition
Sp.Baudrate:=115200; // Baudrate
Sp.Parity:='E'; // Parity
Sp.DataBits:=8; // Data bits
Sp.StopBits:=1; // Stop bits
Sp.DTRManagement:=DTR_AUTO_WO_TIMES; // DTR management
Sp.DTRComplement:=FALSE; // DTR complement
Sp.EchoFlush:=FALSE; // Received echo flush
Sp.DTROnTime:=0; // DTR On time delay (mS)
Sp.DTROffTime:=0; // DTR Off time delay (mS)
Sp.FlushTm:=0; // Flush time (mS)
Sp.RxSize:=0; // Rx buffer size
Sp.TxSize:=0; // Tx buffer size
// Eseguo apertura socket.
Sk.FilesArr:=ADR(Fp); // Files array
Sk.LocalAdd:=ADR('0.0.0.0'); // Local address
Sk.LocalPort:=2000; // Local port
Sk.MaxConn:=1; // Accepted connections
Sk.FlushTm:=50; // Flush time (mS)
Sk.LifeTm:=30; // Life time (S)
Sk.RxSize:=256; // Rx buffer size
Sk.TxSize:=256; // Tx buffer size
// Modbus master settings.
// La comunicazione con PLC caldaia richiede 30mS di delay.
MMdb.Type:=MODBUS_PROTOCOL#MDB_RTU; //Modbus protocol type
MMdb.Absolute:=FALSE; //Absolute addressing
MMdb.Timeout:=T#100ms; //Timeout time
MMdb.Delay:=T#50ms; //Delay time
// Inizializzo FB scambio dati tra socket e seriale.
DExch.DBMax:=260; //Data buffer maximum size
DExch.DBLife:=30.0; //Data buffer life (S)
END_IF;
// -------------------------------------------------------------------------
// GESTIONE PORTA SERIALE E SOCKET
// -------------------------------------------------------------------------
// Forzo gestione porta seriale e socket.
Sp(Open:=TRUE); //Gestione porta seriale
Sk(Enable:=TRUE); //TCPServer management
// -------------------------------------------------------------------------
// GESTIONE SCAMBIO DATI TRA SOCKET TCP E SERIALE
// -------------------------------------------------------------------------
// Eseguo controllo se client è connesso al socket.
DExch(FpA:=Fp, FpB:=Sp.File); //Data exchange
MMdb(File:=Sp.File); //Modbus master
// Se client si connette disabilito Modbus master ed abilito scambio dati.
DExch.Enable:=FALSE; //Data exchange ebnable
IF (Sk.ConnPeers <> 0) THEN
MMdb.Enable:=FALSE; //Modbus enable
DExch.Enable:=TRUE; //Data exchange ebnable
CaseNr:=0; //Program case
RETURN;
END_IF;
// -------------------------------------------------------------------------
// PROGRAM CASES
// -------------------------------------------------------------------------
// Manage the program cases.
CASE (CaseNr) OF
// ---------------------------------------------------------------------
// LETTURA REGISTRI MODBUS
// ---------------------------------------------------------------------
// Eseguo lettura registri Modbus.
0:
MMdb.Enable:=TRUE; //Modbus enable
MMdb.Node:=1; //Node number
MMdb.FCode:=16#04; //Modbus function code
MMdb.Address:=40000; //Modbus register address
MMdb.Points:=2; //Modbus register points
MMdb.Buffer:=ADR(MdbRd); //Memory buffer address
IF NOT(MMdb.Done) THEN RETURN; END_IF;
MMdb.Enable:=FALSE; //Modbus enable
CaseNr:=CaseNr+1; //Program case
// ---------------------------------------------------------------------
// SCRITTURA REGISTRI MODBUS
// ---------------------------------------------------------------------
// Eseguo scrittura registri Modbus.
1:
MMdb.Enable:=TRUE; //Modbus enable
MMdb.Node:=1; //Node number
MMdb.FCode:=16#10; //Modbus function code
MMdb.Address:=40000; //Modbus register address
MMdb.Points:=2; //Modbus register points
MMdb.Buffer:=ADR(MdbWr); //Memory buffer address
IF NOT(MMdb.Done) THEN RETURN; END_IF;
MMdb.Enable:=FALSE; //Modbus enable
CaseNr:=0; //Program case
END_CASE;
// [End of file]