ModbusGateway, Modbus protocol gateway

List

Questa pagina fa parte del Manuale Programmazione IEC 61131-3. Vai all indice.

Questo blocco funzione da eseguire in task Back, opera come gateway tra due connessioni Modbus. Allo stream IFile può collegarsi un sistema master che esegue richieste Modbus con il protocollo definito in IType. Le richieste ricevute saranno riportate in uscita sullo stream OFile da collegarsi ai sistemi slaves, convertite con il tipo di protocollo definito in OType.

In Timeout occorre definire il tempo massimo di attesa delle risposte alle richieste Modbus ricevute in ingresso sullo stream IFile ed inviate in uscita sullo stream OFile. L’uscita Packets ritorna il conteggio dei pacchetti Modbus correttamente gestiti. In Exceptions è ritornato il conteggio delle eventuali risposte di eccezione ricevute dal sistema slave. In caso di errore viene attivata per un loop l’uscita Fault.

Modifica pacchetti da programma

Per abilitare la modifica dei pacchetti Modbus da programma occorre definire le variabili ITrigger ed OTrigger. Per abilitare questa funzione occorre richiedere il codice di protezione, vedi protezione funzioni e blocchi funzione. E’ comunque possibile testarne il funzionamento in modo test per 30 Min.

Per modificare i pacchetti Modbus ricevuti come comando dal server in ITrigger è possibile definire l’indirizzo di una variabile trigger (BOOL) che sarà attivata alla ricezione del pacchetto. Il programma potrà modificare il pacchetto in DFrame, resettando la variabile trigger il pacchetto sarà inviato in uscita su OFile.

Per modificare i pacchetti Modbus ricevuti come risposta dallo slave in OTrigger è possibile definire l’indirizzo di una variabile trigger (BOOL) che sarà attivata alla ricezione del pacchetto. Il programma potrà modificare il pacchetto in DFrame, resettando la variabile trigger il pacchetto sarà inviato in uscita su IFile.

Upgrade list

ModbusGateway_v1

Modificato parametri IType ed OType ora sono di tipo MODBUS_PROTOCOL. Il parametro Timeout ora è di tipo TIME.

Modbus gateway programmabile

Per connettersi a dispositivi seriali Modbus RTU via ethernet occorre utilizzare un Gateway Modbus TCP/RTU, esistono sul mercato prodotti stand-alone, come quelli da noi distribuiti nella gamma dei convertitori Ethernet-Seriale. Vediamo le possibilità offerte da questo FB modificando da programma i pacchetti Modbus.

  • Modificare l’indirizzo dei registri in richiesta dal sistema master verso il sistema slave.
  • Modificare l’indirizzo di nodo in richiesta dal sistema master verso il sistema slave.
  • Indirizzare la richiesta del sistema master su diversi streams di comunicazione e diversi tipi di protocollo in base all’indirizzo di nodo.
Programmare da LogicLab dispositivi su rete Modbus RS485

In molti casi si realizzano reti RS485 dove un sistema master è connesso in Modbus RTU con uno o più sistemi slaves, in questo caso l’utilizzo di questo FB sul sistema master permette di raggiungere tutti i sistemi slave connessi per la programmazione tramite LogicLab.

Nel programma ModbusRTUNetwork (Download) viene fornito il programma del sistema master ed il programma del sistema slave (Che può essere riportato su più slaves). Il sistema master tramite i FBs ModbusMaster ed ACModbus dialoga con i vari sistemi slaves. Quando si riceve una connessione TCP sulla porta 2000 viene disabilitata la comunicazione con i sistemi slaves ed abilitato il FB ModbusTCPGateway che converte la comunicazione Modbus TCP in Modbus RTU verso i dispositivi slaves.

Per programmare i vari slaves basterà in LogicLab definire una comunicazione Modbus TCP indicando lindirizzo IP del dispositivo master e porta 2000. Ogni slave sarà identificabile con il proprio indirizzo di nodo.

Information Circle

Blocco funzione

CODESYS: Non disponibile

LogicLab: eLLabModbusLib

Descrizione

Enable (BOOL) Comando di abilitazione blocco funzione.
SpyOn (BOOL) Se attivo permette di spiare il funzionamento della FB (Vedi articolo).
ITrigger (@BOOL) Definire indirizzo della variabile trigger che verrà attivata da FB su ricezione in DFrame pacchetto da IFile (Solo se si desidera modificare il pacchetto Modbus ricevuto dal master). Definire eNULL se non utilizzata.
OTrigger (@BOOL) Definire indirizzo della variabile trigger che verrà attivata da FB su ricezione in DFrame pacchetto da OFile (Solo se si desidera modificare il pacchetto Modbus ricevuto dallo slave). Definire eNULL se non utilizzata.
ADLgt (UINT) Definizione lunghezza frame di risposta da inviare al master su stream IFile. (Utilizzare solo se si modificano i pacchetti Modbus ricevuti dal master).
IType (MODBUS_PROTOCOL) Tipo di protocollo modbus gestito da stream IFile (Definizione).
IFile (eFILEP) Stream di comunicazione con sistema master.
OType (MODBUS_PROTOCOL) Tipo di protocollo modbus gestito da stream OFile (Definizione).
OFile (eFILEP) Stream di comunicazione con sistema slave.
Timeout (TIME) Tempo massimo gestione pacchetto.
Fault (BOOL) Attivo per un loop se errore esecuzione comando.
CDLgt (UINT) Ritorna la lunghezza del pacchetto Modbus ricevuto dallo stream IFile.
DFrame (PVOID) Indirizzo buffer con pacchetto Modbus.
Packets (UDINT) Contatore pacchetti Modbus correttamente gestiti.
PMTime (REAL) Tempo di gestione pacchetto da ricezione comando da server e trasmissione risposta (S).
Exceptions (UDINT) Contatore pacchetti Modbus con eccezione ricevuti dal sistema slave.
Errors (UDINT) Contatore errori di esecuzione.

Immagine FB ModbusGateway

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#00000001IRx: Ricezione frame comando modbus (Input)
16#00000002ITx: Trasmissione frame risposta modbus (Input)
16#00000004ORx: Ricezione frame risposta modbus (Output)
16#00000008OTx: Trasmissione frame comando modbus (Output)
16#40000000Er: Errore di esecuzione.

Esempi

Come utilizzare gli esempi.
ST_ModbusGateway: Viene gestito un semplice gateway tra Modbus TCP e Modbus RTU.

ST_ModbusGatewayPkModify: Viene gestito un gateway tra Modbus TCP e Modbus RTU se nodo Modbus diverso da 2. Nel caso il nodo Modbus sia 2 il gateway reindirizza il pacchetto al nodo 255 su Modbus TCP connesso in localhost. Viene anche eseguita la modifica dell’indirizzo Modbus richiesto su comando Read Holding register.

LogicLab (Ptp205, ST_ModbusGateway)
PROGRAM ST_ModbusGateway
VAR
    i : UDINT; (* Aux counter *)
    Sp : SysSerialPort; (* Serial port *)
    MdbGw : ModbusGateway_v1; (* Modbus ascii to RTU gateway *)
    TCPServer : SysTCPServer; (* TCPServer management *)
END_VAR

// *****************************************************************************
// PROGRAM "ST_ModbusGateway"
// *****************************************************************************
// Simple Modbus TCP to Modbus RTU gateway.
// -----------------------------------------------------------------------------

    // -------------------------------------------------------------------------
    // INITIALIZATION
    // -------------------------------------------------------------------------
    // Program initializations.

    IF (SysFirstLoop) THEN

        // TCP server settings.

        TCPServer.FilesArr:=ADR(MdbGw.IFile); //Files array
        TCPServer.LocalAdd:=ADR('0.0.0.0'); //Local address
        TCPServer.LocalPort:=2000; //Local port
        TCPServer.MaxConn:=1; //Accepted connections
        TCPServer.FlushTm:=50; //Flush time (mS)
        TCPServer.LifeTm:=60; //Life time (S)
        TCPServer.RxSize:=256; //Rx buffer size
        TCPServer.TxSize:=256; //Tx buffer size

        // Serial port settings.

        Sp.COM:=ADR('COM2'); //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

        // Modbus gateway settings.

        MdbGw.SpyOn:=TRUE; //Spy On
        MdbGw.IType:=MODBUS_PROTOCOL#MDB_TCP; //Modbus type (Input)
        MdbGw.OType:=MODBUS_PROTOCOL#MDB_RTU; //Modbus type (Output)
        MdbGw.ITrigger:=eNULL; //Trigger command (Input)
        MdbGw.OTrigger:=eNULL; //Trigger answer (Output)
        MdbGw.Timeout:=T#1s; //Timeout time
    END_IF;

    // -------------------------------------------------------------------------
    // GATEWAY MANAGEMENT
    // -------------------------------------------------------------------------
    // Gateway management.

    TCPServer(Enable:=TRUE); //TCPServer management
    Sp(Open:=TRUE); //Serial port management
    MdbGw(OFile:=Sp.File, Enable:=SysFIsOpen(MdbGw.IFile)); //Modbus gateway enable

// [End of file]
LogicLab (Ptp205, ST_ModbusGatewayPkModify)
PROGRAM ST_ModbusGatewayPkModify
VAR
    i : UDINT; (* Aux counter *)
    AwB : WORD; (* Auxiliary word buffer *)
    ITrigger : BOOL; (* Trigger command (Input) *)
    OTrigger : BOOL; (* Trigger answer (Output) *)
    MCommand : BYTE; (* Modbus command *)
    MCPoints : UINT; (* Modbus command points *)
    MRAddress : WORD; (* Modbus request address *)
    MCAddress : WORD; (* Modbus command address *)
    Sp : SysSerialPort; (* Serial port *)
    TCPClient : SysTCPClient; (* TCP client management *)
    TCPServer : SysTCPServer; (* TCPServer management *)
    MdbGw : ModbusGateway_v1; (* Modbus gateway *)
    IRegs : ARRAY[0..7] OF WORD; (* Internal registers *)
END_VAR

// *****************************************************************************
// PROGRAM "ST_ModbusGatewayPkModify"
// *****************************************************************************
// Modbus TCP to Modbus RTU and Modbus TCP gateway
// -----------------------------------------------------------------------------

    // -------------------------------------------------------------------------
    // INITIALIZATION
    // -------------------------------------------------------------------------
    // Program initializations.

    IF (SysFirstLoop) THEN

        // TCP server settings.

        TCPServer.FilesArr:=ADR(MdbGw.IFile); //Files array
        TCPServer.LocalAdd:=ADR('0.0.0.0'); //Local address
        TCPServer.LocalPort:=2000; //Local port
        TCPServer.MaxConn:=1; //Accepted connections
        TCPServer.FlushTm:=50; //Flush time (mS)
        TCPServer.LifeTm:=60; //Life time (S)
        TCPServer.RxSize:=256; //Rx buffer size
        TCPServer.TxSize:=256; //Tx buffer size

        // 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.

        TCPClient.PeerAdd:=ADR('127.0.0.1'); //Peer address
        TCPClient.PeerPort:=502; //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 gateway settings.

        MdbGw.SpyOn:=TRUE; //Spy On
        MdbGw.IType:=MODBUS_PROTOCOL#MDB_TCP; //Modbus type (Input)
        MdbGw.ITrigger:=ADR(ITrigger); //Trigger command (Input)
        MdbGw.OTrigger:=eNULL; //Trigger answer (Output)
        MdbGw.Timeout:=T#1s; //Timeout time
    END_IF;

    // -------------------------------------------------------------------------
    // GATEWAY MANAGEMENT
    // -------------------------------------------------------------------------
    // Gateway management.

    TCPServer(Enable:=TRUE); //TCPServer management
    Sp(Open:=TRUE); //Serial port management
    TCPClient(Connect:=TRUE); //TCPClient management
    MdbGw(Enable:=SysFIsOpen(MdbGw.IFile)); //Modbus gateway enable

    // =========================================================================
    // MODBUS PACKET MODIFY
    // =========================================================================
    // Below a program that shows the FB powerful features.

    // -------------------------------------------------------------------------
    // "IFILE" MODBUS RX PACKETS ANALYSING
    // -------------------------------------------------------------------------
    // At every modbus packet received from the "IFile" communication stream,
    // the packets is analized and modified by the program before to be sent to
    // slave system connected to "OFile" communication stream.
    // -------------------------------------------------------------------------
    // Just a remind on the Modbus commands,
    // 16#01 Read coil status
    // 16#02 Read input status
    // 16#03 Read holding registers
    // 16#04 Read input registers
    // 16#05 Force single coil
    // 16#06 Preset single register
    // 16#10 Preset multiple registers
    //
    //               +----+---+---+---+---...---
    // Command frame |Node|Cmd|Address| Data
    //               +----+---+---+---+---...---
    // -------------------------------------------------------------------------
    // The needs are to manage connection to three types of devices according
    // the received command address.

    IF (ITrigger) THEN 
        ITrigger:=FALSE; //Trigger command (Input)

        // Acquire the command and address from the received packet and manage it.

        MCommand:=eGetBYTE(MdbGw.DFrame+1); //Modbus command
        i:=ArrayVarTxfer(TRUE, WORD_TYPE, ADR(MCAddress), MdbGw.DFrame+2); //Modbus command address

        CASE (MCAddress) OF

            // -----------------------------------------------------------------
            // Addresses from 0 to 7 are managed by the program and act on 8
            // registers (Only 16#03 and 16#10 command are managed).

            0..7:
            MdbGw.OFile:=eNULL; //So command packet is not redirect to any slave
            CASE (MCommand) OF

                // -------------------------------------------------------------
                // Read holding registers, return the request value.
                //    +----+--+---+---+---+---+    +----+--+-----+--...--+
                // Rx |Node|03|Address|Points | Tx |Node|03|Bytes| Data  |
                //    +----+--+---+---+---+---+    +----+--+-----+--...--+

                16#03:
                i:=ArrayVarTxfer(TRUE, WORD_TYPE, ADR(MCPoints), MdbGw.DFrame+4); //Modbus command points
                i:=eSetBYTE(MdbGw.DFrame+2, TO_BYTE(MCPoints*2)); //Set bytes

                FOR AwB:=0 TO MCPoints-1 DO
                    i:=ArrayVarTxfer(TRUE, WORD_TYPE, MdbGw.DFrame+3+(AwB*2), ADR(IRegs)+(AwB*2));
                END_FOR;

                MdbGw.ADLgt:=3+(MCPoints*2); //Answer data length

                // -------------------------------------------------------------
                // Preset multiple registers, return the proper answer.
                //    +----+--+---+---+---+---+-----+--...--+    +----+--+---+---+---+---+
                // Rx |Node|10|Address|Points |Bytes| Data  | Tx |Node|10|Address|Points |
                //    +----+--+---+---+---+---+-----+--...--+    +----+--+---+---+---+---+

                16#10:
                i:=ArrayVarTxfer(TRUE, WORD_TYPE, ADR(MCPoints), MdbGw.DFrame+4); //Modbus command points
                
                FOR AwB:=0 TO MCPoints-1 DO
                    i:=ArrayVarTxfer(TRUE, WORD_TYPE, ADR(IRegs)+(AwB*2), MdbGw.DFrame+7+(AwB*2));
                END_FOR;

                MdbGw.ADLgt:=6; //Answer data length

                // -------------------------------------------------------------
                // If command is not managed an exception is sent.
                // +--+---+---------+
                // |Nd|Cmd|Exception|
                // +--+---+---------+

                ELSE
                i:=eSetBYTE(MdbGw.DFrame+1, eGetBYTE(MdbGw.DFrame+1) OR 16#80);
                i:=eSetBYTE(MdbGw.DFrame+2, 16#02); //Illegal data address
                MdbGw.ADLgt:=3; //Answer data length
            END_CASE;

            // -----------------------------------------------------------------
            // Address 100 is sent on a Modbus RTU slave, address is offsetted.

            100:
            MdbGw.OFile:=Sp.File; //Output stream
            MdbGw.OType:=MODBUS_PROTOCOL#MDB_RTU; //Modbus type
            MRAddress:=(MCAddress-100)+40000; //Modbus request address
            i:=ArrayVarTxfer(TRUE, WORD_TYPE, MdbGw.DFrame+2, ADR(MRAddress));

            // -----------------------------------------------------------------
            // Address 200 is sent on a Modbus TCP slave, address is offsetted.

            200:
            MdbGw.OFile:=TCPClient.File; //Output stream
            MdbGw.OType:=MODBUS_PROTOCOL#MDB_TCP; //Modbus type
            MRAddress:=(MCAddress-200)+40000; //Modbus request address
            i:=ArrayVarTxfer(TRUE, WORD_TYPE, MdbGw.DFrame+2, ADR(MRAddress));

            // -----------------------------------------------------------------
            // All other address are exceptions.
            // +--+---+---------+
            // |Nd|Cmd|Exception|
            // +--+---+---------+

            ELSE
            MdbGw.OFile:=eNULL; //So command packet is not redirect to any slave
            i:=eSetBYTE(MdbGw.DFrame+1, eGetBYTE(MdbGw.DFrame+1) OR 16#80);
            i:=eSetBYTE(MdbGw.DFrame+2, 16#02); //Illegal data address
            MdbGw.ADLgt:=3; //Answer data length
        END_CASE;
    END_IF;

    // -------------------------------------------------------------------------
    // "OFILE" MODBUS RX PACKETS ANALYSING
    // -------------------------------------------------------------------------
    // Just a remind on the Modbus answers,
    // 16#01 Read coil status
    // 16#02 Read input status
    // 16#03 Read holding registers
    // 16#04 Read input registers
    //
    //              +----+---+---
    // Answer frame |Node|Cmd|...
    //              +----+---+---
    //
    // 16#05 Force single coil
    // 16#06 Preset single register
    // 16#10 Preset multiple registers
    //
    //              +----+---+---+---+---
    // Answer frame |Node|Cmd|Address|...
    //              +----+---+---+---+---
    // -------------------------------------------------------------------------
    // Nelle risposte l'indirizzo di nodo da ritornare è sempre 16#01.

    IF (OTrigger) THEN 
        OTrigger:=FALSE; //Trigger command (Output)

        // Acquire the command and address from the received packet and manage it.

        MCommand:=eGetBYTE(MdbGw.DFrame+1); //Modbus command
        CASE (MCommand) OF

            // -----------------------------------------------------------------
            // On these commands no modification is required.

            16#01, 16#02,16#03, 16#04:

            // -----------------------------------------------------------------
            // On these commands the address must be replaced.

            16#05, 16#06,16#10:
            i:=ArrayVarTxfer(TRUE, WORD_TYPE, MdbGw.DFrame+2, ADR(MCAddress));
        END_CASE;
    END_IF;

// [End of file]
Was this article helpful?