Arduino Android communication through Modbus TCP

Arduino ethernet modbus tcp

The project I want to share with you today is how to communicate between Arduino and Android through Modbus TCP.

This article belongs to a set of walkthrough articles that are based on understanding:

  1. How Arduino communicates to Android devices not only by means of USB
  2. How easily is to use the lightweight modbus protocol in both Arduino and Android
  3. How the two slightly different implementations of modbus TCP and RTU, can be used in industrial and home applications

Take a look at how to communicate between Arduino and Android through RS485 and modbus RTU article if you are interested in knowing the “serial” version of the current article.

So, back to the main topic, the items that I’ve used in this project are the following:

both connected by a switch and RJ45 cables. I supposed that the Arduino is the slave device and the Android panel is the master in the communication paradigm.

I start first by presenting the Arduino sketch. It uses the SPI and Ethernet Arduino libraries (first two lines) and the Mudbus libraries (for the modbus TCP).
In the setup() function, apart from setting Ethernet parameters like mac, ip, gateway and subnet mask, I set to zero the first four registers of the modbus. Mb.R is an array that corresponds to the (signed) modbus TCP integer registers whereas Mb.C refers to boolean-based registers.

In the loop() function, I read the analog values of pins 0, 1, 2 and stored them into the first three modbus registers. This allowed me to display in the Android panel three temperature values (for instance three temperature probes such as TMP36 connected to the analog pins).

#include
#include
#include "Mudbus.h"

//uncomment if you want to see debug info
//#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);
  //Avoid pins 4,10,11,12,13 when using ethernet shield

  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 update
  Mb.Run();

  //read analog values and store the integer representation in the first three modbus registers
  Mb.R[0] = analogRead(0);
  Mb.R[1] = analogRead(1);
  Mb.R[2] = analogRead(2);

  /*
  Do some work with the control register Mb.R[3]. For instance:

  	if (Mb.R[3]>10)
  		digitalWrite(1,HIGH);
  	else
  		digitalWrite(1,LOW);

  */

  delay(10);
}

The Ethernet port used for the modbus TCP requests is 502. To modifying the default port, just change the MB_PORT variable defined inside the Mudbus.h header file.

We are now ready to analyze the Java code that is necessary to create an Android app that requests slave’s first three registers values and write (a control) integer value to the fourth.

The following is the Android Activity for this demonstrative project. The layout simply consists of two buttons that trigger modbus TCP reads and writes. I skipped the code for the layout but you can just create a layout with two buttons named btnRead and btnWrite.


public class MainActivity extends Activity implements OnClickListener{

	// The important instances of the classes mentioned before
	TCPMasterConnection con = null; 	//the TCP connection
	ModbusTCPTransaction trans = null; 	//the Modbus transaction

	// Variables for storing the parameters
	InetAddress addr = null; 		//the slave's address
	int port = Modbus.DEFAULT_PORT;

	Button btnRead, btnWrite;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// associate the layout to the activity
		setContentView(R.layout.activity_main);

		// I suppose of having a layout with two simple buttons
		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 {
			// specify the slave IP address
			addr = InetAddress.getByName("192.168.1.8");

			// Open the connection
			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){

				  // modbus reads

			}else if (v.getId()==R.id.btnWrite) {

				// modbus writes

			}
		} catch (Exception e) {
			Log.d("MODBUS", "Error in reading/writing");
		}
	}

}

The code that I want to highlight is the one that opens and closes the TCP connection and is located inside onResume() and onStop() methods.
Basically, onResume sets the slave IP address, the port and creates the tcp connection to slave devices whereas onStop() closes it.

The core part of the project is located inside the onClick() method where modbus reads/writes requests are created and executed. I used the Jamod library; references are at the bottom of the article.
Creating a modbus read(or write) request takes some simple steps:

  1. Declare appropriate request and response variables
  2. Create the read/write request
  3. Create a modbus TCP transaction and associate a read/write request to it
  4. Execute the transaction
  5. Get the transaction response

Point 1 needs to use variable types that refer specifically to the type of read/write you intend to use. For instance, if you want to use the modbus function 3, use the types:

  • ReadMultipleRegistersRequest
  • ReadMultipleRegistersResponse

Conversely, if you want to write multiple registers (function code 16), just use the following types:

  • 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

			// Prepare the request
			req = new ReadMultipleRegistersRequest(startReg, count);

			// Prepare the transaction
			trans = new ModbusTCPTransaction(con);
			trans.setRequest(req);

			// execute the transaction
			trans.execute();
			// get the response
			res = (ReadMultipleRegistersResponse) trans.getResponse();

			/*

			 do something with
			 res.getRegister(k).getValue()
			 where k is the index of the response array registers

			 */

		}else if (v.getId()==R.id.btnWrite) {
			startReg = 3;				//writes the fourth register

			WriteMultipleRegistersRequest req = null; //the request
			WriteMultipleRegistersResponse res = null; //the response

			// Prepare the request and create a simple integer register
			SimpleRegister[] hr = new SimpleRegister[1];
			hr[0]=new SimpleRegister(65);

			req = new WriteMultipleRegistersRequest(startReg, hr);

			// Prepare the transaction
			trans = new ModbusTCPTransaction(con);
			trans.setRequest(req);

			//execute the transaction
			trans.execute();
			res = (WriteMultipleRegistersResponse) trans.getResponse();

		}
	} catch (Exception e) {
		Log.d("MODBUS", "Error in reading/writing");
	}
}

That’s it! What do you think? If you liked this article, please share it!

References:

Hardware
[1] Arduino Ethernet Board
[2] Ltouch Android multi touch panel

Software
[3] Arduino Modbus TCP library (slave)
[4] Jamod Modbus for Java