Skip to Content

Modbus on Arduino and ESP32 PLCs with the Arduino IDE

Implementation for Arduino / ESP32 based PLCs (Arduino IDE)

Installation

1. Arduino IDE: For the implementation of Arduino and ESP32 based PLCs it is commonly used the Arduino IDE. Download it here.

2. Industrial Shields boards: To be able to work with Industrial Shields PLC, install the industrial shields boards package following this step-by-step guide.

3. Modbus Library: Download the Modbus library that will be used for this lesson. Install it from here.

Slave

The following examples are Modbus slave implementations with both RTU and TCP. These codes can also be found in the Arduino IDE on File>Examples>Tools40>Modbus:


Modbus RTU Slave


Modbus TCP Slave

Implementation Guide

The differences between RTU and TCP implementation are the initial configurations, as RTU works over RS-485 and TCP works over ethernet:

  • For Modbus RTU (using RS-485):
    • Include the libraries <RS485.h> and <ModbusRTUSlave.h>
    • Instantiate the modbus object with: ModbusRTUSlave modbus(RS485, slaveID);
    • Initialize the RS-485 port: RS485.begin(baudrate, HALFDUPLEX, SERIAL_8E1);
    • Initialize the modbus object: modbus.begin(RS485_BAUDRATE);
  • For Modbus TCP:
    • Include the libraries <Ethernet.h> and <ModbusTCPSlave.h>
    • Instantiate the modbus object with: ModbusTCPSlave modbus(port);
    • Initialize the Ethernet with: Ethernet.begin(mac, ip);
    • Initialize the modbus object: modbus.begin();

1. Modbus registers mapping: According to your PLC model, define the inputs and outputs to be mapped as the corresponding Modbus objects. The example codes are implemented for  M-Duino 21+, you must modify the inputs and outputs of code according to your version.

int digitalOutputsPins[] = { /* Define your digital output pins here */ };
int digitalInputsPins[] = { /* Define your digital input pins here */ };
int analogOutputsPins[] = { /* Define your analog output pins here */ };
int analogInputsPins[] = { /* Define your analog input pins here */ };

#define numDigitalOutputs (sizeof(digitalOutputsPins) / sizeof(digitalOutputsPins[0]))
#define numDigitalInputs (sizeof(digitalInputsPins) / sizeof(digitalInputsPins[0]))
#define numAnalogOutputs (sizeof(analogOutputsPins) / sizeof(analogOutputsPins[0]))
#define numAnalogInputs (sizeof(analogInputsPins) / sizeof(analogInputsPins[0]))

bool digitalOutputs[numDigitalOutputs];
bool digitalInputs[numDigitalInputs];
uint16_t analogOutputs[numAnalogOutputs];
uint16_t analogInputs[numAnalogInputs];

2. Setup: In the setup function, the input and output pins of the PLC are initialized and the communication protocol is configured, whether it's Modbus TCP or Modbus RTU. Additionally, the Modbus Slave object is initialized, and the associated coils and registers are linked to it.

void setup() {
  // Init variables, inputs and outputs

  // Init Ethernet (for TCP) or RS-485 (for RTU)

  // Init ModbusSlave object (pass RS485 baudrate for RTU)

  modbus.setCoils(digitalOutputs, numDigitalOutputs);
  modbus.setDiscreteInputs(digitalInputs, numDigitalInputs);
  modbus.setHoldingRegisters(analogOutputs, numAnalogOutputs);
  modbus.setInputRegisters(analogInputs, numAnalogInputs);
}

3. Loop: In the loop function, the PLC continuously updates its inputs and outputs and processes Modbus requests. The digital and analog inputs are read and updated to reflect the current state. The Modbus object processes any incoming requests, and the outputs are updated based on the current state of the coils and registers.

void loop() {
  // Update Inputs
  modbus.update();
  // Update Outputs
}

Master

The following examples are Modbus master implementations with both RTU and TCP. These codes can also be found in the Arduino IDE on File>Examples>Tools40>Modbus along with more examples:

Modbus RTU Master Read Coils

Modbus RTU Master Write Coils


Modbus TCP Master Read Coils

Modbus TCP Master Write Coil

Implementation Guide

The differences between RTU and TCP implementation are the initial configurations, as RTU works over RS-485 and TCP works over ethernet:

  • For Modbus RTU (using RS-485):
    • Include the libraries <RS485.h> and <ModbusRTUMaster.h>
    • Instantiate the modbus object with: ModbusRTUMaster modbus(RS485);​
    • Initialize the RS-485 port: RS485.begin(baudrate, HALFDUPLEX, SERIAL_8E1);
    • Initialize the modbus object: modbus.begin(RS485_BAUDRATE);
  • For Modbus TCP:
    • Include the libraries <Ethernet.h> and <ModbusTCPMaster.h>
    • Configure the Ethernet values: MAC address, master IP and slave IP
    • Instantiate the master object with: ModbusTCPMaster modbus;​
    • Initialize the Ethernet with: Ethernet.begin(mac, ip);

API Reference

As seen in the first lesson of this course, Modbus provides multiple function codes (FC) to read and write the different coils and registers. Our library provides some functions to send messages using the different function codes. These are the functions available:

Description:

Reads the contents of a specified number of coils from a slave device (FC=01).

Syntax:

readCoils(slave, addr, quantity);

Parameters:

  • slave: The address of the slave device.
  • addr: The starting address of the coils.
  • quantity: The number of coils to read.

Returns:

Returns true if the operation was successful, false otherwise.

Description:

Reads the contents of a specified number of discrete inputs from a slave device (FC=02).

Syntax:

readDiscreteInputs(slave, addr, quantity);

Parameters:

  • slave: The address of the slave device.
  • addr: The starting address of the discrete inputs.
  • quantity: The number of discrete inputs to read.

Returns:

Returns true if the operation was successful, false otherwise.

Description:

Reads the contents of a specified number of holding registers from a slave device (FC=03).

Syntax:

readHoldingRegisters(slave, addr, quantity);

Parameters:

  • slave: The address of the slave device.
  • addr: The starting address of the holding registers.
  • quantity: The number of holding registers to read.

Returns:

Returns true if the operation was successful, false otherwise.

Description:

Reads the contents of a specified number of input registers from a slave device (FC=04).

Syntax:

readInputRegisters(slave, addr, quantity);

Parameters:

  • slave: The address of the slave device.
  • addr: The starting address of the input registers.
  • quantity: The number of input registers to read.

Returns:

Returns true if the operation was successful, false otherwise.

Description:

Writes a single coil to either 0 or 1 in a slave device (FC=05).

Syntax:

writeSingleCoil(slave, addr, value);

Parameters:

  • slave: The address of the slave device.
  • addr: The address of the coil.
  • value: The value to write to the coil.

Returns:

Returns true if the operation was successful, false otherwise.

Description:

Writes a value to a single holding register in a slave device (FC=06).

Syntax:

writeSingleRegister(slave, addr, value);

Parameters:

  • slave: The address of the slave device.
  • addr: The address of the holding register.
  • value: The value to write to the holding register.

Returns:

Returns true if the operation was successful, false otherwise.

Description:

Writes values to a sequence of coils in a slave device (FC=15).

Syntax:

writeMultipleCoils(slave, addr, values, quantity);

Parameters:

  • slave: The address of the slave device.
  • addr: The starting address of the coils.
  • values: The values to write to the coils.
  • quantity: The number of coils to write.

Returns:

Returns true if the operation was successful, false otherwise.

Description:

Writes values to a sequence of holding registers in a slave device (FC=16).

Syntax:

writeMultipleRegisters(slave, addr, values, quantity);

Parameters:

  • slave: The address of the slave device.
  • addr: The starting address of the holding registers.
  • values: The values to write to the holding registers.
  • quantity: The number of holding registers to write.

Returns:

Returns true if the operation was successful, false otherwise.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.