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:
- How Arduino communicates to Android devices not only by means of USB
- How easily is to use the lightweight modbus protocol in both Arduino and Android
- 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:
- An Arduino Ethernet board
- An Android multi touch panel
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:
- Declare appropriate request and response variables
- Create the read/write request
- Create a modbus TCP transaction and associate a read/write request to it
- Execute the transaction
- 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