Passa al contenuto principale

ModbusMaster, modbus master

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

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​

FB Modbus Master

NomeTipoDescrizione
EnableBOOLAbilita l'esecuzione del comando Modbus. Per rieseguire il comando, disabilitare e successivamente riabilitare questo ingresso.
SpyOnBOOLSe attivo, permette di monitorare il funzionamento della FB (vedi articolo).
AbsoluteBOOLPer compatibilità con la specifica Modbus, la FB sottrae 1 all'indirizzo Modbus. Se attivo, viene utilizzato l'indirizzamento assoluto e non viene eseguita la sottrazione.
Filee_FILEPFlusso dati (stream) da utilizzare per la comunicazione.
TypeMODBUS_PROTOCOLTipo di protocollo Modbus (definizione).
NodeUSINTNumero del nodo Modbus sul quale eseguire il comando. Intervallo: 0–255.
FCodeUSINTCodice funzione Modbus da eseguire nel comando (vedi tabella seguente).
AddressUINTIndirizzo di allocazione delle variabili sul sistema slave. In accordo con le specifiche Modbus, l'indirizzo trasmesso nel frame dati è Address - 1. Intervallo: 16#0001–16#FFFF.
PointsUSINTNumero di variabili consecutive sulle quali opera il comando.
BufferPVOIDIndirizzo del buffer contenente i dati letti o da scrivere.
TimeoutTIMETempo massimo di esecuzione del comando. Se il comando non termina entro il tempo definito, viene abortito e viene attivata l'uscita Fault.
DelayTIMETempo di pausa dopo l'esecuzione del comando Modbus.
DoneBOOLSi attiva al termine dell'esecuzione del comando e rimane attiva fino alla disabilitazione di Enable.
OkBOOLSi attiva per un ciclo di esecuzione se il comando è stato eseguito correttamente.
FaultBOOLSi attiva per un ciclo di esecuzione se si verifica un errore durante l'esecuzione del comando.
CTimeTIMETempo di esecuzione del comando, comprendente l'invio del comando e la ricezione della risposta.
PacketsUDINTNumero totale di comandi Modbus eseguiti. Viene incrementato a ogni comando; raggiunto il valore massimo, riparte da 0.
ErrorsUDINTNumero totale di errori. Viene incrementato a ogni errore; raggiunto il valore massimo, riparte da 0.

FCode, funzioni modbus supportate

16#01 Read coil status (Massimo 250 coils)
16#02 Read input status (Massimo 250 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

CodeNameDescription
16#01Illegal functionE' stato richiesto un codice funzione che il dispositivo non supporta.
16#02Illegal data addressViene generato in diverse situazioni: - Richiesto un registro non implementato (o un'area inesistente) - Richiesta la lettura di un numero di registri che va oltre l'area implementata (partendo dall'indirizzo richiesto) - Si è tentato di scrivere in un'area read‐only
16#03Illegal data valueIl dato definito nella richiesta non è accettato dal dispositivo.
16#04Slave device failureSi è verificato un errore irreversibile mentre lo slave stava tentando di eseguire l'azione richiesta.
16#05AcknowledgeLo 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#06Slave device busyLo slave è impegnato nell'elaborazione di un comando che richiede una lunga elaborazione.
16#07Negative acknowledgeLo slave non può eseguire la funzione di programma ricevuta nella query.
16#08Memory parity errorLo 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

TriggerDescrizione
16#00000001Tx: Invio frame comando modbus.
16#00000002Rx: Ricezione frame risposta modbus.
16#40000000Er: 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]