Browse our Blog. You will find multiple applications, solutions, code examples. Navigate using the tag cloud or search using specific criteria

How to use Modbus TCP Slave library with Arduino based industrial controller


Modbus is a standard serial communication protocol that has been used for PLC's since 1979. Modbus allow the communication between devices in the same network. In this case it is showed how to use a Modbus TCP Industrial Shields library.  Modbus TCP/IP is a variant of Modbus that is used for communications over TCP/IP networks and over the port 502. 

Odoo text and image block

Modbus object types:

Object typeAccessSize
Discrete inputRead1 bit
Input registerRead16 bits
Holding registersRead-write 16 bits

Normally coils a are used to write digital values to an outputs. Discrete inputs are used to read digital inputs. Registers are use to communicate data between the devices and also usually used for analog I/Os. 

In this case we will see how to use the slave library of Tools40.


Modbus TCP slave functions

The ModbusTCPSlave module implements the Modbus TCP Slave capabilities.

#include <ModbusTCPSlave.h>

ModbusTCPSlave slave;

The default TCP port is the 502 but you can change it with:

// Set the TCP listening port to 510 instead of 502
ModbusTCPSlave slave(510);

To map the coils, discrete inputs, holding registers and input registers addresses with the desired variables values, the module uses four variables arrays:

bool coils[NUM_COILS];
bool discreteInputs[NUM_DISCRETE_INPUTS];
uint16_t holdingRegistesr[NUM_HOLDING_REGISTERS];
uint16_t inputRegisters[NUM_INPUT_REGISTERS];

The lengths of these arrays depend on the application and the registers usages. Obviously, the names of the arrays also depend on your preferences.

To associate the registers arrays with the library it is possible to use these functions in the setup:

slave.setCoils(coils, NUM_COILS);
slave.setDiscreteInputs(discreteInputs, NUM_DISCRETE_INPUTS);
slave.setHoldingRegisters(holdingRegisters, NUM_HOLDING_REGISTERS);
slave.setInputRegisters(inputRegisters, NUM_INPUT_REGISTERS);

It is not required to have all kind of registers mapping to work, only the used by the application.

To start the ModbusTCP server, call the begin function after the registers mapping. It is also possible to call the beginfunction before the registers mapping. Remember to begin the Ethernet before the ModbusTCPSlave object in the setup.

// Init the Ethernet
Ethernet.begin(mac, ip);

// Init the ModbusTCPSlave object

At this time the ModbusTCP server is running and the only important thing to do is to update the ModbusTCPSlave object often in the loop function, and tret the registers mapping values to update variables, inputs and outputs.

// Update discrete inputs and input registers values
discreteInputs[0] = digitalRead(I0_7);
inputRegisters[0] = analogRead(I0_0);
// ...

// Update the ModbusTCPSlave object

// Update coils and holding registers
digitalWrite(Q0_0, coils[0]);
// ...

Example software for Arduino based automation controller

   Copyright (c) 2018 Boot&Work Corp., S.L. All rights reserved
   This program is free software: you can redistribute it and/or modify
   it 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
   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 <>.
#include <ModbusTCPSlave.h>
#include <Ethernet.h>

// Ethernet configuration values
uint8_t mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEE };
IPAddress ip(10, 10, 10, 4);
int port = 502;
// Modbus registers mapping
// This example uses the M-Duino21+ mapping
int digitalOutputsPins[] = {
#if defined(PIN_Q0_4)
  Q0_0, Q0_1, Q0_2, Q0_3, Q0_4,
int digitalInputsPins[] = {
#if defined(PIN_I0_6)
  I0_0, I0_1, I0_2, I0_3, I0_4, I0_5, I0_6,
int analogOutputsPins[] = {
#if defined(PIN_A0_7)
  A0_5, A0_6, A0_7,
int analogInputsPins[] = {
#if defined(PIN_I0_12)
  I0_7, I0_8, I0_9, I0_10, I0_11, I0_12,
#define numDigitalOutputs int(sizeof(digitalOutputsPins) / sizeof(int))
#define numDigitalInputs int(sizeof(digitalInputsPins) / sizeof(int))
#define numAnalogOutputs int(sizeof(analogOutputsPins) / sizeof(int))
#define numAnalogInputs int(sizeof(analogInputsPins) / sizeof(int))
bool digitalOutputs[numDigitalOutputs];
bool digitalInputs[numDigitalInputs];
uint16_t analogOutputs[numAnalogOutputs];
uint16_t analogInputs[numAnalogInputs];
// Define the ModbusTCPSlave object
ModbusTCPSlave modbus(port);
void setup() {
  // Init variables, inputs and outputs
  for (int i = 0; i < numDigitalOutputs; ++i) {
    digitalOutputs[i] = false;
    digitalWrite(digitalOutputsPins[i], digitalOutputs[i]);
  for (int i = 0; i < numDigitalInputs; ++i) {
    digitalInputs[i] = digitalRead(digitalInputsPins[i]);
  for (int i = 0; i < numAnalogOutputs; ++i) {
    analogOutputs[i] = 0;
    analogWrite(analogOutputsPins[i], analogOutputs[i]);
  for (int i = 0; i < numAnalogInputs; ++i) {
    analogInputs[i] = analogRead(analogInputsPins[i]);
  // Init Ethernet
  Ethernet.begin(mac, ip);
  // Init ModbusTCPSlave object
  modbus.setCoils(digitalOutputs, numDigitalOutputs);
  modbus.setDiscreteInputs(digitalInputs, numDigitalInputs);
  modbus.setHoldingRegisters(analogOutputs, numAnalogOutputs);
  modbus.setInputRegisters(analogInputs, numAnalogInputs);
void loop() {
  // Update inputs
  for (int i = 0; i < numDigitalInputs; ++i) {
    digitalInputs[i] = digitalRead(digitalInputsPins[i]);
  for (int i = 0; i < numAnalogInputs; ++i) {
    analogInputs[i] = analogRead(analogInputsPins[i]);
  // Process modbus requests
  // Update outputs
  for (int i = 0; i < numDigitalOutputs; ++i) {
    digitalWrite(digitalOutputsPins[i], digitalOutputs[i]);
  for (int i = 0; i < numAnalogOutputs; ++i) {
    analogWrite(analogOutputsPins[i], analogOutputs[i]);

Related Posts: 

M-Duino controller family 

Tools40 library installed 

Do you want more information?

Open Source technology allows you to develop your installations.

Just fill the form and we will contact you as soon as we can.