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-Duino, 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
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
How to connect an Arduino industrial PLC using Modbus TCP/IP