Comunicazione Android Arduino via RS485 e Modbus

In questo post vi parlo di uno dei progetti a cui sto lavorando in questo periodo. Consiste nell’utilizzare un pannello multi touch Android che comunica attraverso rs485 e modbus con uno (o più) Arduino board. In particolare, l’ambito principale del progetto riguarda la domotica (accensione/spegnimento/status luci, regolazione temperatura, ecc) sebbene molte delle tecniche e concetti che vedremo possono essere applicati con successo in altri campi.


La cosa carina del touch screen che ho utilizzato è l’ampia disponibilità di porte, ampia connettività in generale: Ethernet, USB, rs232, RS485 ed anche I²C. Sebbene disponga di porte molto veloci, ho espressamente scelto di utilzzare RS485 perchè i device Arduino (Uno per esempio) per supportare per esempio l’interfaccia Ethernet hanno bisogno di elettronica aggiuntiva che fa lievitare ulteriormente il prezzo (anche se dal punto di vista della fattibilità esistono diversi progetti in rete).
RS485 è uno standard molto conosciuto nell’ambito dell’automazione industriale e nella domotica. E’ half-duplex, utilizza due fili, tollera molto bene i disturbi, si riescono a raggiungere buone velocità e connettere device remoti fino anche a 1200 metri.

Inoltre, utilizzando il protocollo serial modbus, anche questo sviluppato principalmente nell’ambito dell’automazione industriale, è open, semplice e robusto da sviluppare e manutenere. Ho scelto di utilizzare la variante del modbus denominata RTU, sebbene ne esistano di altre.

Le sezioni dell’articolo sono le seguenti:

Parti elettroniche

Gli ingredienti chiave che ho utilizzato in questo progetto sono i seguenti:

  • Un pannello multi touch Android già rooted
  • Uno (o più) board Arduino (non necessariamente Mega)
  • Un chip Max485 Maxim (o eventualmente uno più economico della TI, l’sn75176a)
  • Led
  • Un sensore di temperatura (come per esempio TMP36)
  • Interruttori
  • Un potenziomentro
  • Una resistenza da 120 Ohm (sebbene per progetti come questo dove la distanza tra master e slave è limitata)
  • Fili ed un pò di esperienza di saldatura 🙂

Diagramma del circuito

Android Arduino communication via modbus rs485
La figura a destra rappresenta schematicamente il diagramma del circuito inpiegato in questo progetto (alcune figure sono state fatte con Fritzing). Anche se non è stato raffigurato, è possibile collegare al bus principale piu’ board Arduino.

Arduino sketch

Lo sketch Arduino usa essenzialmente la libreria SimpleSlaveModbus (come installare una nuova libreria Arduino). Anche se è relativamente semplice da utilizzare, vi suggerisco ugualmente di leggere la documentazione, in modo tale da capire più in dettaglio il suo funzionamente ma soprattutto per capire quali siano le limitazioni.

Dopo aver incluso la libreria (slave) del modbus in testa al modulo, ho dichiarato un array intero (denominato holdingRegs) che, come richiama il nome, memorizza i registri modbus. La funzione 16 avrà il combito di modificare mentre la funzione 3 di leggere i registri.
I primi due registri sono riservati ai valori analogici che sono letti attraverso la funzione analogRead dei pin A0 e A1. I registri da INDIG0 a INDIG4, sono riservati per gli input digitali, OUTD0 a OUTD4 per gli output digitali e AOUT0 per il PWM.
Nella funzione setup dello sketch, richiamo la funzione modbus_configure per configurare il modbus fornendo i parametri come il baud rate, l’ID dello slave (l’indirizzo), il pin di abilitazione alla trasmissione ed il numero di registri del modbus. Successivamente configuro, attraverso la funzione pinMode, i pin digitali della board.

#include <SimpleModbusSlave.h>

#define pinDig 3
#define pinDigOut 6

//////////////// registers of the slave ///////////////////
enum
{
<%%KEEPWHITESPACE%%>  // The first register starts at address 0
<%%KEEPWHITESPACE%%>  ADC0,
<%%KEEPWHITESPACE%%>  ADC1,
<%%KEEPWHITESPACE%%>  INDIG0,
<%%KEEPWHITESPACE%%>  INDIG1,
<%%KEEPWHITESPACE%%>  INDIG2,
<%%KEEPWHITESPACE%%>  INDIG3,
<%%KEEPWHITESPACE%%>  OUTD0,
<%%KEEPWHITESPACE%%>  OUTD1,
<%%KEEPWHITESPACE%%>  OUTD2,
<%%KEEPWHITESPACE%%>  OUTD3,
<%%KEEPWHITESPACE%%>  AOUT0,
<%%KEEPWHITESPACE%%>  TOTAL_ERRORS,
<%%KEEPWHITESPACE%%>  TOTAL_REGS_SIZE
<%%KEEPWHITESPACE%%>    // total number of registers for function 3 and 16 share the same register array
};

unsigned int holdingRegs[TOTAL_REGS_SIZE]; // function 3 and 16 register array

void setup()
{

<%%KEEPWHITESPACE%%>  modbus_configure(38400, 1, 2, TOTAL_REGS_SIZE);

<%%KEEPWHITESPACE%%>  //digital input configuration
<%%KEEPWHITESPACE%%>  for(int i=10; i&lt;14; i++){
<%%KEEPWHITESPACE%%>    pinMode(i, INPUT_PULLUP);
<%%KEEPWHITESPACE%%>  }

<%%KEEPWHITESPACE%%>  //digital output
<%%KEEPWHITESPACE%%>  pinMode(3, OUTPUT);
<%%KEEPWHITESPACE%%>  pinMode(4, OUTPUT);
<%%KEEPWHITESPACE%%>  pinMode(5, OUTPUT);
<%%KEEPWHITESPACE%%>  pinMode(6, OUTPUT);

<%%KEEPWHITESPACE%%>  //modbus led
<%%KEEPWHITESPACE%%>  pinMode(8, OUTPUT);

<%%KEEPWHITESPACE%%>  //PWM
<%%KEEPWHITESPACE%%>  pinMode(9, OUTPUT);

<%%KEEPWHITESPACE%%>  //modbus register initialization
<%%KEEPWHITESPACE%%>  for (int i=0; i &lt; 10; i++){
<%%KEEPWHITESPACE%%>  	holdingRegs[i] = 0;
<%%KEEPWHITESPACE%%>  }
}

Nella funzione loop dello sketch, ho inserito una chiamata alla funzione modbus_update, specificando come primo parametro un riferimento all’array che contiene i registri modbus. Sarà questa funzione che si prenderà carico di gestire i comandi modbus ricevuti dalla seriale, modificando il valore dei registri nel caso di scritture oppure rispondendo con il valore dei registri nel caso di letture.
La parte rimanente dello sketch legge i valori dei pin analogici A0 e A1, legge i valori degli ingressi digitali (i pin digitali in output sono stati settati come INPUT_PULLUP) ed infine scrive i valori degli interuttori.

Per avere un’idea sullo stato del canale di comunicazione, ho utilizzato un led rosso che indica quando un messaggio modbus viene ricevuto e processato. Tutto ciò si può fare modificando i sorgenti della libreria modbus, aggiungendo una coppia di digitalWrites (settate ad HIGH all’inizio e a LOW alla fine) su un certo pin (7 nel nostro caso).

void loop()
{
<%%KEEPWHITESPACE%%>  // modbus_update() is the only method used in loop(). It returns the total error
<%%KEEPWHITESPACE%%>  // count since the slave started. You don't have to use it but it's useful
<%%KEEPWHITESPACE%%>  // for fault finding by the modbus master.

<%%KEEPWHITESPACE%%>  holdingRegs[TOTAL_ERRORS] = modbus_update(holdingRegs);

<%%KEEPWHITESPACE%%>  holdingRegs[0]=analogRead(0);
<%%KEEPWHITESPACE%%>  holdingRegs[1]=analogRead(1);

<%%KEEPWHITESPACE%%>   //digital input
<%%KEEPWHITESPACE%%>   for (int i=0; i &lt; 4; i++){
<%%KEEPWHITESPACE%%>	   holdingRegs[i+2]=digitalRead(10+i);
<%%KEEPWHITESPACE%%>   }

<%%KEEPWHITESPACE%%>   for(int i=0; i &lt; 4; i++){
<%%KEEPWHITESPACE%%>     digitalWrite(pinDig+i,holdingRegs[pinDigOut+i]);
<%%KEEPWHITESPACE%%>   }

<%%KEEPWHITESPACE%%>   //PWM
<%%KEEPWHITESPACE%%>   analogWrite(12, holdingRegs[9]);

}

Nel post denominato Progetto Domotica con Android, parlo di come utilizzare una libreria modbus nativa per comunicare con slave Arduino.

Se avete a disposizione una rete RJ45, potere anche pensare di utilizzare il modbus TCP per la .

Che ne pensate? Se vi è piaciuto questo articolo, per favore condividetelo!

Update: Per coloro di voi che utilizzano una versione aggiornata della libreria SimpleModbusSlave, il codice Arduino presentato sopra dovrà essere leggermente modificato. Utilizzando per esempio la versione V7, bisogna modificare la chiamata al metodo modbus_configure. Infatti, rispetto alle versioni precedenti, ora accetta i seguenti parametri:

  • Un puntatore all’interfaccia seriale (hardware)
  • Il baudrate della connessione modbus
  • Il modbus byte format. I valori concessi sono i seguenti
    • SERIAL_8N2: 1 start bit, 8 data bits, 2 stop bits
    • SERIAL_8E1: 1 start bit, 8 data bits, 1 Even parity bit, 1 stop bit
    • SERIAL_8O1: 1 start bit, 8 data bits, 1 Odd parity bit, 1 stop bit
  • L’id del nodo
  • Il pin di abilitazione del transceiver MAX485 (o similari)
  • La dimensione dell’array che contiene i registri modbus
  • Un puntatore all’array che contiene i registri modbus

In sostanza, la nuova chiamata al metodo è la seguente:

modbus_configure(&amp;Serial, 38400, SERIAL_8N2, 1, 2, HOLDING_REGS_SIZE, holdingRegs);

Dato che il riferimento all’array che contiene i registri modbus è stato passato alla libreria con la chiamata di configurazione modbus, non è più necessario specificare questo riferimento nel richiamo del metodo modbus_update.