1. Modbus overview

Le informazioni trattate in questa pagina sono derivate dalla documentazione originale che si può trovare sul sito The Modbus Organization, in particolare dai documenti MODBUS Protocol Specification e MODBUS TCP/IP.

Modbus è un protocollo applicativo che si basa sul paradigma client/server, in cui il server è generalmente chiamato SLAVE ed il client MASTER, per lo scambio di semplici messaggi di tipo request/reply.
Lo SLAVE offre servizi specificati da codici funzione (Function Code) definiti inviati dal MASTER nella richiesta.
Diverse sono le implementazioni del protocollo in termini di trasporto utilizzato e nei progetti illustrati in questo sito tratteremo principalmente le due più importanti:

  • Modbus over TCP/IP su Ethernet
  • Modbus over Serial RS485 e RS232

01-layer

Ovviamente nella realtà potrebbero esistere architetture miste in cui vi sono oggetti che hanno la funzione di gateway, cioè trasformano, ad esempio, il colloquio su rete Ethernet in un colloquio asincrono su seriale (vedremo come ultimo esempio anche un’implementazione di questo tipo di oggetto).

02-hybrid

Descrizione del protocollo

Modbus definisce una semplice struttura del messaggio che viene scambiato dal MASTER verso lo SLAVE e viceversa. Tale struttura è chiamata Protocol Data Unit (PDU) ed è indipendente dal mezzo di trasmissione. Il frame che ogni implementazione del protocollo utilizza (ed al cui interno è presente il PDU) viene invece chiamato Application Data Unit (ADU). Nella figura seguente si può vedere lo schema di PDU e il suo utilizzo nel caso di ADU per trasmissione seriale asincrona e di ADU per trasmissione Ethernet.

03-pdu-adu

Il campo FUNC (Function Code) del PDU è grande 1 byte. Codici validi per questo campo sono numeri da 1 a 255, di cui i numeri da 1 a 127 sono riservati a codici funzione mentre quelli da 128 a 255 sono riservati ed utilizzati in caso di eccezione nella risposta, inoltre il codice 0 non è valido. Vedremo in seguito come verranno codificate le funzioni e quali saranno i codici delle funzioni standard e quali quelli definiti dall’utente. Alcune funzioni prevedono sub-codici, da inserire comunque nel campo DATA.
Nel campo DATA si trovano anche i parametri della funzione, come gli indirizzi dei registri da leggere o scrivere. Se la richiesta può essere servita dallo SLAVE, allora la risposta conterrà i valori richiesti.
Definendo i seguenti frame di richiesta, risposta ed eventuale risposta con eccezione, in figura si può vedere la schematizzazione del colloquio tra client (MASTER) e server (SLAVE):

mb_req_pdu = FUNC_CODE(1 byte), DATA(n bytes)
 
mb_rsp_pdu = FUNC_CODE(1 byte), DATA(m bytes)
 
mb_excep_rsp_pdu = EXC_FUNC_CODE(FUNC_CODE+0x80), EXCEP_CODE(1 byte)

05-mimic.001

Originariamente il protocollo nacque per una connettività seriale asincrona (RS485 o RS232 ad esempio), in cui fu imposto che tutto il messaggio ADU poteva essere grande al massimo 256 byte. Come conseguenza di questo limite il PDU del messaggio è grande al massimo 253 bytes, cioè 1 byte per il campo FUNCTION e 252 byte per il campo DATA (dai 256 byte massimi totali abbiamo sottratto il byte iniziale, che rappresenta l’indirizzo dello SLAVE, e i 2 byte finali del CRC, come definito nell’ADU del protocollo nel caso di seriale asincrono).
Nella sua implementazione TCP/IP il protocollo ha mantenuto il limite massimo di 253 byte per la grandezza del PDU, ma l’ADU, in questo caso, può diventare grande al massimo 260 byte, 253 byte al massimo per la parte PDU + 7 byte di header TCP/IP chiamato MBAP – ModBus Application Protocol.

MBAP Header

Item Size Description Request Response
Transaction Identifier 2 bytes Identification of a MODBUS transaction Initialize by MASTER Recopied by SLAVE
Protocol Identifier 2 bytes Always 0x0000 Initialize by MASTER Recopied by SLAVE
Lenght 2 bytes Number of following bytes Initialize by MASTER Initialize by SLAVE
Unit ID 1 byte Identification of a remote SLAVE connected on a serial Initialize by MASTER Recopied by INPUTSLAVE

La porta standard TCP riservata su cui lo SLAVE risponde al MASTER è la 502.

Data Model

I tipi di oggetti referenziati all’interno del protocollo possono essere:

Object Size Type
DISCRETE INPUTS Single bit RO
COILS Single bit RW
INPUT REGISTERS 16bits word RO
HOLDING REGISTERS 16bits word RW

Gli oggetti con dimensione 16 bit hanno un encoding di tipo BIG-ENDIAN, cioè viene trasmesso prima il byte più significativo della word e poi il byde meno significativo. Così se la word da trasmettere è 0x1234, verrà trasmesso prima il byte 0x12 e poi 0x34.
Ogni oggetto Modbus, può essere referenziato con un indirizzo a 16 bit, da 0 a 65535 (0x0000 – 0xFFFF). Diversi tipi di oggetto potrebbero far riferimento al medesimo valore (overlapping: ad esempio un DISCRETE INPUT da 16 bit potrebbe essere letto anche come una successione di COILS).
Il protocollo quindi effettua un mapping logico tra le risorse dell’hardware (ad esempio valori di grandezze fisiche come temperatura, umidità, tensioni, correnti, etc…) e gli indirizzi logici che arbitrariamente il protocollo assegna a tali risorse per poterle leggere e/o gestire.

04-blocks

Negli esempi si utilizzeranno le schede Arduino, in particolare si inizierà con la scheda Arduino UNO, scheda su cui si implementerà il protocollo nella sua versione SLAVE.
Il mapping di Arduino UNO (o anche Arduino Leonardo e Arduino Duemilanove) che verrà utilizzato in seguito è il seguente:

MB-Map2    arduino-uno

Utilizzando tutti i PIN disponibili della scheda Arduino UNO abbiamo effettuato le seguenti scelte per il mapping sul protocollo Modbus:

  • I PIN con input analogici verranno mappati come Input Register a 16 bit. Tali register avranno indirizzamento da 0x0000 a 0x0005 e conterranno il valore in mV della tensione letta sul relativo PIN.
  • I PIN digitali numerati sulla scheda Arduino con 4, 5, 6, 7 e 8 verranno mappati come COIL con indirizzamento da 0x0020 a 0x0024. Sono quindi oggetti da utilizzare sia in lettura che in scrittura secondo la configurazione impostata sull’Holding Register chiamato COILS_SET, che ha indirizzo Modbus 0x00F0.
  • I PIN 2 e 3 sono PIN che sulla scheda arduino possono essere gestiti con IRQ, e quindi verranno utilizzati come counter. Il conteggio potrà avvenire sui fronti positivi (switch da 0 a 1 logici dell’ingresso digitale), su quelli negativi (da 0 a 1 logici) o su entrambi secondo la configurazione impostata sull’Holding Register chiamato COUNTERS_SET, che ha indirizzo Modbus 0x00E0. L’hardware permette di mantenere questo conteggio in una variabile di 32 bit, per cui ogni counter avrà associati 2 Holding Register da 16 bit ognuno, uno per la parte più significativa della variabile a 32 bit ed uno per la parte meno significativa. Il tipo di registro Modbus è in lettura e scrittura così che è possibile impostare un valore iniziale al conteggio.
  • I rimanenti PIN serviranno per la comunicazione seriale asincrona (PIN 0, 1 e 9) o per la comunicazione attraverso lo shield Ethernet aggiuntivo (PIN 10, 11, 12 e 13).
  • Nell’elenco, peraltro già mensionati, vi sono anche altri due registri che non corrispondono direttamente a letture o scritture di PIN della scheda, ma servono solamente per impostare i vari ingressi ed uscite nella maniera più opportuna (COILS_SET e COUNTERS_SET).

Modbus Transaction

Nella implementazione del protocollo seguiremo le linee guida che si trovano nella documentazione ufficiale sul sito The Modbus Organization, in particolare seguiremo, per ogni transazione, il seguente schema:

mb-transaction

Lo SLAVE (Arduino) si porrà in attesa di un messaggio. Se il messaggio è ad esso indirizzato (ad esempio lo SLAVE ID coincide, in caso di trasmissione seriale, oppure è stata aperta una connessione TCP) l’algoritmo, per costruire la risposta, effettuerà alcuni controlli che potrebbero portare ad una risposta di eccezione nel caso che le alcuni controlli diano esito negativo. I controlli che verranno effettuati sono nell’ordine:

  1. Validazione del codice di funzione: verrà generato un messaggio di errore nel caso il codice funzione non sia stato implementato dallo SLAVE.
  2. Validazione degli indirizzi dei registri: verrà generato un messaggio di errore nel caso gli indirizzi dei registri siano fuori range rispetto i registri validi per la funzione specificata.
  3. Validazione dei dati ricevuti: verrà generato un messaggio di errore se i dati non sono validi per i registri indirizzati, ad esempio se un registro supporta un valore da 0 a 1000 non può essere infasato con un valore maggiore di 1000.
  4. Validazione della esecuzione della funzione: verrà generato un messaggio di eccezione, che nel nostro caso si limiterà ad un errore di indisponibilità della lettura su Arduino.

Se tutte le verifiche hanno esito negativo, allora verrà costruito il messaggio da inviare al MASTER che ha effettuato la richiesta.

Function Code

Il Function Code ha la grandezza di un byte e, come abbiamo già riportato, non tutti i valori da 0 a 255 sono validi. In particolare il valore 0 non è valido, i valori da 1 a 127 sono codici funzione validi, mentre i valori da 128 a 255 sono invece valori riservati per le eccezioni. In pratica se c’è un’eccezione il messaggio di risposta conterrà il codice funzione per l’eccezione composto dal codice funzione della richiesta più 128 (ad esempio se ho un’eccezione per la funzione 5 la risposta conterrà come codice funzione 5+128, cioè 133).
Dei codici funzione da 1 a 127, alcuni sono definiti dallo standard Modbus, altri sono User Defined, cioè a discrezione di chi li implementa e possono essere utilizzati per funzioni dedicate non previste dallo standard.

mb-func mb-func-code

 Alcuni dei codici funzione standard sono riportati nella tabella precedente, e sono divisi in 4 grandi categorie:

  • Codici funzione per la gestione dei registri di un bit.
  • Codici funzione per la gestione dei registri di 16 bit.
  • Codici funzione per la lettura e scrittura di streaming di dati.
  • Codici funzione per diagnostica.

Nei nostri progetti implementeremo solo alcuni dei codici funzione standard e qualcuno dei codici funzione definiti dall’utente.

Claudio.

Print Friendly

Lascia una risposta

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati *

È possibile utilizzare questi tag ed attributi XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Elettronica, programmazione embedded (Arduino, Raspberry PI) ed altro