Comunicazione Android Arduino via Modbus TCP

Arduino ethernet modbus tcp

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:

  1. Come Arduino comunica con device Android (smartphone, tablet, multi touch devices) non solamente attraverso USB
  2. La semplicità con cui sia Arduino che Android possano utilizzare il protocollo modbus per comunicare tra di loro
  3. 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:

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:

  1. Dichiarare delle approppriate variabili di richiesta e risposta
  2. Creare una richiesta di lettura/scrittura
  3. Creare una transazione TCP e associare la richiesta di lettura/scrittura appena creata
  4. Eseguire la transazione
  5. 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