Introduction
Modbus is a very useful standard that allows us to communicate with several devices from different manufacturers in the same network. Besides Modbus TCP/IP is transferred through Ethernet which is one of the most reliable protocols of the market.
In this case, we will show how to configure two industrial Arduino based PLCs M-Duinos, one used as a Master and the other as a Slave.
Industrial control
Requirements for the connection between and Arduino industrial programmable logic controller with Modbus TCP/IP
Industrial PLC Arduino automation based controller Modbus TCP/IP Master/Slave examples:
Follow the previous post to see the Slave code that we have used in this example:
How to use Modbus TCP Slave library with a PLC controller Arduino automation Read the post >>>
Also, take a look at the Master post to see how to use the Modbus Library, but for the Master, we have changed the code a little in order to make it more interactive with the user:
Modbus TCP Master with Industrial Arduino based PLCs Read the post >>>
Application Architecture of the connection between an Arduino PLC and Modbus TCP/IP
M-Duino Master has an interactive serial menu that allows the user to control the application. The menu has 6 options. The first four options are to control two outputs of the slave, the fifth option is to get the Analog inputs of registers from the slave and the last option, sixth, is to get the digital inputs or discrete inputs from the slave.
We use writeSingleCoil(), readInputRegisters() and readDiscreteInputs() functions to communicate to the slave. Then just executing a short for loop depending on which message we have sent, we read the values using response.getRegister() or response.isDiscreteInputSet().
The rest of the code is just Ethernet configurations and Serial commands to debug and make the application more interactive
Modbus TCP IP
Software
/*
Copyright © 2018 Boot&Work Corp., S.L. All rights reserved
This program is free software: you can redistribute it and/or modify
it is under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ModbusTCPMaster.h>
#include <Ethernet.h>
// Ethernet configuration values
uint8_t mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(10, 10, 10, 3);
IPAddress slaveIp(10, 10, 10, 4);
uint16_t slavePort = 502;
// Define the ModbusTCPMaster object
ModbusTCPMaster modbus;
//
bool registerSet = 0;
bool discreteSet = 0;
// Ethernet client object used to connect to the slave
EthernetClient slave;
uint32_t lastSentTime = 0UL;
uint32_t lastSentTimeReadInputs = 0UL;
////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(9600UL);
// Begin Ethernet
Ethernet.begin(mac, ip);
Serial.println(Ethernet.localIP());
// NOTE: it is not necessary to start the modbus master object
mainUI();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void loop() {
// Connect to slave if not connected
// The ethernet connection is managed by the application, not by the library
// In this case the connection is opened once
if (!slave.connected()) {
Serial.println("Slave not connected");
slave.stop();
slave.connect(slaveIp, slavePort);
if (slave.connected()) {
Serial.println("Reconnected");
}
}
// Send a request every 1000ms if connected to slave
if (slave.connected()) {
//Serial.println("Slave connected");
if (Serial.available()) {
byte chosenOption= Serial.read();
bool value;
byte address;
switch(chosenOption){
case '1': //set Q0_0 to high
value = 1;
address = 0;
if (!(modbus.writeSingleCoil(slave, 0, address, value))) {
// Failure treatment
Serial.println("Request fail");
}
Serial.println("Q0_0 set to HIGH");
break;
case '2': //set Q0_0 to low
value = 0;
address = 0;
if (!(modbus.writeSingleCoil(slave, 0, address, value))) {
// Failure treatment
Serial.println("Request fail");
}
Serial.println("Q0_0 set to LOW");
break;
case '3': //set Q0_1 to high
value = 1;
address = 1;
if (!(modbus.writeSingleCoil(slave, 0, address, value))) {
// Failure treatment
Serial.println("Request fail");
}
Serial.println("Q0_1 set to HIGH");
break;
case '4':
value = 0;
address= 1;
if (!(modbus.writeSingleCoil(slave, 0, address, value))) {
// Failure treatment
Serial.println("Request fail");
}
Serial.println("Q0_1 set to LOW");
break;
case '5':
if (!modbus.readInputRegisters(slave, 0, 0, 6)) {
// Failure treatment
Serial.println("Error requesting registers");
}else{registerSet = true;}
break;
case '6':
if (!modbus.readDiscreteInputs(slave, 0, 0, 7)) {
// Failure treatment
Serial.println("Error requesting discrete input");
}else{discreteSet = true;}
break;
}
mainUI();
}
if (modbus.isWaitingResponse()) {
ModbusResponse response = modbus.available();
if (response) {
if (response.hasError()) {
// Response failure treatment. You can use response.getErrorCode()
// to get the error code.
}else if (registerSet){
// Get the input registers values from the response
Serial.print("Input registers values: ");
for (int i = 0; i < 6; ++i) {
Serial.print(response.getRegister(i));
Serial.print(',');
}
registerSet = false;
} else if(discreteSet) {
// Get the input registers values from the response
Serial.print("Input discrete values: [");
for (int i = 0; i < 7; ++i) {
Serial.print(response.isDiscreteInputSet(i));
Serial.print(',');
}
Serial.println(']');
discreteSet = false;
}
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void mainUI(){
Serial.println("********************Modbus Test*********************");
Serial.println("Chose an option:");
Serial.println("1. Set Q0_0 to HIGH");
Serial.println("2. Set Q0_0 to LOW");
Serial.println("3. Set Q0_1 to HIGH");
Serial.println("4. Set Q0_1 to LOW");
Serial.println("5. Print slave input analog values");
Serial.println("6. Print slave input digital values");
Serial.println("****************************************************");
}
* IMPORTANT: when connecting the PLCs, a crossover Ethernet cable must be used