How to connect an Arduino industrial PLC using Modbus TCP/IP

Master/Slave configuration with Modbus TCP/IP
March 18, 2019 by
How to connect an Arduino industrial PLC using Modbus TCP/IP
Bernat Garcia

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:

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


Looking for some examples of industrial automation?

Check our Case Studies about Arduino automation, control and monitoring.


​Search in our Blog

How to connect an Arduino industrial PLC using Modbus TCP/IP
Bernat Garcia March 18, 2019
Share this post

Looking for your ideal Programmable Logic Controller?

Take a look at this product comparison with other industrial controllers Arduino-based. 

We are comparing inputs, outputs, communications and other features with the ones of the relevant brands.


Industrial PLC comparison >>>