/ModbusTCPSlave

This is an Arduino library that implements the slave/server logic of the Modbus TCP protocol.

Primary LanguageC++MIT LicenseMIT

ModbusTCPSlave

Modbus is an industrial communication protocol. The TCP variant communicates over Ethernet or Wi-Fi. The full details of the Modbus protocol can be found at modbus.org. A good summary can also be found on Wikipedia.

This is an Arduino library that implements the slave/server logic of the Modbus TCP protocol. It enables an Arduino, or arduino compatible, board to respond to Modbus TCP requests from a Modbus master/client.
This library is able to service the following function codes:

  • 1 (Read Coils)
  • 2 (Read Discrete Inputs)
  • 3 (Read Holding Registers)
  • 4 (Read Input Registers)
  • 5 (Write Single Coil)
  • 6 (Write Single Holding Register)
  • 15 (Write Multiple Coils)
  • 16 (Write Multiple Holding Registers).

This library will work with any Client object.

This library updates coil, descrete input, holding register, and input register arrays based on Modbus requests. It does not give indication of what has changed, but it does give indication if a valid Modbus request has been received.

This library depends on ModbusADU, ModbusTCPComm, and ModbusSlaveLogic.

Note

At present this library should be considered to be in Beta. It may contain bugs and documentation may be incomplete. If you encounter issues, please report them here. This will help me know what to focus on in improving and developing this library.

Thanks,

C. M. Bulliner

Examples

Methods

ModbusTCPSlave

Description

Creates a ModbusTCPSlave object.

Syntax

  • ModbusTCPSlave

Example

# include <ModbusTCPSlave.h>

ModbusTCPSlave modbus;
configureCoils()

Description

Tells the library where coil data is stored and the number of coils. If this function is not run, the library will assume there are no coils.

Syntax

modbus.configureCoils(coils, numCoils)

Parameters

  • modbus: a ModbusRTUSlave object.

  • coils: an array of coil values. Allowed data types: array of bool.

  • numCoils: the number of coils. This value must not be larger than the size of the array. Allowed data types: uint16_t.

configureDiscreteInputs()

Description

Tells the library where to read discrete input data and the number of discrete inputs. If this function is not run, the library will assume there are no discrete inputs.

Syntax

modbus.configureDiscreteInputs(discreteInputs, numDiscreteInputs)

Parameters

  • modbus: a ModbusRTUSlave object.

  • discreteInputs: an array of discrete input values. Allowed data types: array of bool.

  • numDiscreteInputs: the number of discrete inputs. This value must not be larger than the size of the array. Allowed data types: uint16_t.

configureHoldingRegisters()

Description

Tells the library where holding register data is stored and the number of holding registers. If this function is not run, the library will assume there are no holding registers.

Syntax

modbus.configureHoldingRegisters(holdingRegisters, numHoldingRegisters)

Parameters

  • modbus: a ModbusRTUSlave object.

  • holdingRegisters: an array of holding register values. Allowed data types: array of uint16_t.

  • numHoldingRegisters: the number of holding registers. This value must not be larger than the size of the array. Allowed data types: uint16_t.

configureInputRegisters()

Description

Tells the library where to read input register data and the number of input registers. If this function is not run, the library will assume there are no input registers.

Syntax

modbus.configureInputRegisters(inputRegisters, numInputRegisters)

Parameters

  • modbus: a ModbusRTUSlave object.

  • inputRegisters: an array of input register values. Allowed data types: array of uint16_t.

  • numInputRegisters: the number of input registers. This value must not be larger than the size of the array. Allowed data types: uint16_t.

poll()

Description

Checks if any Modbus requests are available from a particular client. If a valid write request has been received, it will update the appropriate data array, and send an acknowledgment response. If a valid read request has been received, it will send a response with the requested data. If an invalid request has been received, it will either respond with an exception response or not at all, as per the Modbus specification. This function should be called frequently.

Syntax

modbus.poll(client)

Parameters

  • modbus: a ModbusTCPSlave object.
  • client: a Client object

Returns

  • true if a valid Modbus request was received and processed.
  • false if no valid Modbus request was received.

It is not essential that this value be read.

Example

#include <Ethernet.h>
# include <ModbusTCPSlave.h>

uint8_t mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};

const uint8_t coilPins[2] = {4, 5};
const uint8_t discreteInputPins[2] = {2, 3};

EthernetServer server(MODBUS_TCP_SLAVE_DEFAULT_PORT);
ModbusRTUSlave modbus;

bool coils[2];
bool discreteInputs[2];

void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 2000);

  pinMode(coilPins[0], OUTPUT);
  pinMode(coilPins[1], OUTPUT);
  pinMode(discreteInputPins[0], INPUT);
  pinMode(discreteInputPins[1], INPUT);

  modbus.configureCoils(coils, 2);
  modbus.configureDiscreteInputs(discreteInputs, 2);

  Serial.println("Connecting");
  if (!Ethernet.begin(mac)) {
    Serial.println("Failed to configure Ethernet using DHCP");
    if (Ethernet.hardwareStatus() == EthernetNoHardware) Serial.println("Ethernet shield not found");
    else if (Ethernet.linkStatus() == LinkOFF) Serial.println("Ethernet cable not connected");
    while (true);
  }
  Serial.print("IP address: ");
  Serial.println(Ethernet.localIP());

  server.begin();
}

void loop() {
  EthernetClient client = server.available();
  while (client.connected()) {
    discreteInputs[0] = digitalRead(discreteInputPins[0]);
    discreteInputs[1] = digitalRead(discreteInputPins[1]);

    modbus.poll();

    digitalWrite(coilPins[0], coils[0]);
    digitalWrite(coilPins[1], coils[1]);
  }
}