Il progetto che vi voglio parlare in questo post riguarda la comunicazione tra Arduino e Android attraverso Modbus TCP.
Questo articolo appartiene ad un gruppo di post step-by-step che hanno come scopo fondamentale quello di comprendere:
- Come Arduino comunica con device Android (smartphone, tablet, multi touch devices) non solamente attraverso USB
- La semplicità con cui sia Arduino che Android possano utilizzare il protocollo modbus per comunicare tra di loro
- Come due implementazioni leggermente differenti del modbus, cioè il modbus TCP ed il modbus RTU, possono essere utilizzati per le applicazioni industriali e di domotica
Se sei interessato alla comunicazione modbus su rs
Il mio consiglio è di leggere l’articolo che vi spiega come far comunicare Android con qualche altro dispositivo che supporti modbus RTU (per es. PLC) attraverso RS485, se il vostro progetto richiede esplicitamente questa caratteristica.
Ritornando al tema principale, ho usato in questo progetto:
- Una board Arduino Ethernet
- Un pannello Android multi touch
connessi attraverso uno switch e cavi RJ45. L’arduino è lo slave nell’architettura di comunicazione, mentre il pannello Android è il master.
Vediamo prima di tutto come potrebbe essere lo sketch Arduino. Come prima cosa, il codice dovrà includere le librerie SPI e Ethernet (prime due linee) e la libreria Mudbus (per il modbus TCP).
Nella funzione setup(), dopo aver configurato i parametri Ethernet come per esempio il mac address, l’ip, il gateway ed il subnet mask, ho impostato i primi quattro registri modbus a zero. Mb.R è l’arrary che corrisponde ai registri interi del modbus TCP, mentre Mb.C si riferisce a registri binari (booleani).
Nella funzione loop(), leggo i valori analogici dai pin 0, 1, 2 e li memorizzo all’interno dei primi tre registri. Questo mi permetterà di visualizzare nel pannello Android tre valori di temperatura (se collego ai pin analogici tre sonde di temperatura come per esempio TMP36).
#include <SPI.h> #include <Ethernet.h> #include "Mudbus.h" //togli il commento se vuoi vedere le informazioni di debug //#define DEBUG Mudbus Mb; //Function codes 1(read coils), 3(read registers), 5(write coil), 6(write register) //signed int Mb.R[0 to 125] and bool Mb.C[0 to 128] MB_N_R MB_N_C void setup() { uint8_t mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x51, 0x06 }; uint8_t ip[] = { 192, 168, 1, 8 }; uint8_t gateway[] = { 192, 168, 1, 1 }; uint8_t subnet[] = { 255, 255, 255, 0 }; Ethernet.begin(mac, ip, gateway, subnet); delay(5000); //Time to open the terminal Mb.R[0] = 0; Mb.R[1] = 0; Mb.R[2] = 0; Mb.R[3] = 0; } void loop() { //modbus TCP aggiornamento Mb.Run(); //leggi i valori analogici dai pin 0,1,2 e memorizza la loro rappresentazione intera nei primi tre registri modbus Mb.R[0] = analogRead(0); Mb.R[1] = analogRead(1); Mb.R[2] = analogRead(2); /* Fai qualcosa con il registro di controllo Mb.R[3]. Per esempio: if (Mb.R[3]>10) digitalWrite(1,HIGH); else digitalWrite(1,LOW); */ delay(10); }
La porta Ethernet usata per le richieste modbus TCP è la 502. Per modificare questo valore impostato di default basta cambiare la variable MB_PORT definita all’interno del file Mudbus.h.
Vediamo ora il codice Java che è necessario al fine di leggere i tre valori delle sonde di temperatura collegate all’Arduino e scrivere un valore (di controllo) sul quarto registro.
Di sequito ho inserito il codice di una attività Android (MainActivity) che sarà eseguita non appena la app va in esecuzione. Il layout contiene due bottoni che attivano le chiamate di lettura/scrittura modbus su TCP. Il file è stato ommesso per chiarezza di esposizione. Potere crearne uno che contenga due bottoni con id btnRead e btnWrite.
public class MainActivity extends Activity implements OnClickListener{ TCPMasterConnection con = null; //the TCP connection ModbusTCPTransaction trans = null; //the Modbus transaction // Variabili per la memorizzazione dei parametri InetAddress addr = null; //the slave's address int port = Modbus.DEFAULT_PORT; Button btnRead, btnWrite; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // associa il layout all'attività setContentView(R.layout.activity_main); // Suppongo di avere un layout con due semplici bottoni btnRead = (Button) findViewById(R.id.btnRead); btnWrite = (Button) findViewById(R.id.btnWrite); btnRead.setOnClickListener(this); btnWrite.setOnClickListener(this); } @Override protected void onStop() { super.onStop(); //Close the TCP connection con.close(); } @Override protected void onResume() { super.onResume(); try { // specifica l'indirizzo IP dello slave addr = InetAddress.getByName("192.168.1.8"); // Apri la connessione con = new TCPMasterConnection(addr); con.setPort(port); con.connect(); } catch (Exception e) { Log.d("MODBUS","connection error"); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.activity_main, menu); return true; } @Override public void onClick(View v) { int startReg = 0; try { if (v.getId()==R.id.btnRead){ // lettura modbus }else if (v.getId()==R.id.btnWrite) { // scrittura modbus } } catch (Exception e) { Log.d("MODBUS", "Error in reading/writing"); } } }
Il codice che apre e chiude le connessioni TCP è localizzato all’interno di metodi onResume() e onStop() methods. OnResume imposta l’indirizzo IP dello slave a cui ci si collega, la porta, e crea la connessione TCP verso il device slave, mentre nel metodo onStop() la si chiude.
Il cuore del progetto si trova all’interno del metodo onClick() dove vengono gestite le letture/scritture modbus. Ho usato la libreria Jamod; i riferimenti e link in calce all’articolo.
Creare una richiesta modbus di lettura (o scrittura) si basa sui seguenti step:
- Dichiarare delle approppriate variabili di richiesta e risposta
- Creare una richiesta di lettura/scrittura
- Creare una transazione TCP e associare la richiesta di lettura/scrittura appena creata
- Eseguire la transazione
- Ricevere la risposta e gestire i dati eventualmente ricevuti
Il punto 1 consiste nel dichiarare delle variabili con un tipo specifico rispetto alla richiesta modbus eseguita. Per esempio, se si vuole usare la funzione modbus 3, si useranno i seguenti tipi:
- ReadMultipleRegistersRequest
- ReadMultipleRegistersResponse
Viceversa, se si vuole scrivere dei registri (codice funzione modbus 16), basterà usare i seguenti tipi:
- WriteMultipleRegistersRequest
- WriteMultipleRegistersResponse
public void onClick(View v) { // start register int startReg; try { if (v.getId()==R.id.btnRead){ startReg = 0; ReadMultipleRegistersRequest req = null; //the request ReadMultipleRegistersResponse res = null; //the response // Prepara la richiesta req = new ReadMultipleRegistersRequest(startReg, count); // Prepare la transazione trans = new ModbusTCPTransaction(con); trans.setRequest(req); // esegui la transazione trans.execute(); // ricevi la risposta res = (ReadMultipleRegistersResponse) trans.getResponse(); /* fai qualcosa con i registri ricevuti res.getRegister(k).getValue() dove k è l'indice dell'array che contiene i registri letti */ }else if (v.getId()==R.id.btnWrite) { startReg = 3; //writes the fourth register WriteMultipleRegistersRequest req = null; //the request WriteMultipleRegistersResponse res = null; //the response // Prepara la richiesta e crea un semplice registro di interi SimpleRegister[] hr = new SimpleRegister[1]; hr[0]=new SimpleRegister(65); req = new WriteMultipleRegistersRequest(startReg, hr); // Prepara la transazione trans = new ModbusTCPTransaction(con); trans.setRequest(req); //esegui la transazione trans.execute(); res = (WriteMultipleRegistersResponse) trans.getResponse(); } } catch (Exception e) { Log.d("MODBUS", "Error in reading/writing"); } }
Tutto qua! Che ne pensate? Se vi è piaciuto questo articolo, per favore condividetelo!
Riferimenti:
Hardware
[1] Arduino Ethernet Board
[2] Ltouch: pannello Android multi touch
Software
[3] Libreria Arduino Modbus TCP (slave)
[4] Libreria Jamod Modbus per Java