Blog

Turning an Arduino into a Modbus Device

Turning an Arduino into a Modbus Device

A situation arises where you have a system that includes a PLC, an HMI, and some peripheral devices. The HMI displays some information to a user and allows for some basic control and the PLC manages data collection and communication through an RS485 network using a Modbus RTU protocol.

Easy, right? Many PLCs already support RS485 and Modbus communication and only require a few functions blocks.

There is a twist, however. The devices with which the PLC is communicating are Arduinos. Lots of them.

So, how can you program an Arduino to act as a Modbus device?

Turns out the answer is easy.

In this post, I'll talk about adding an Arduino to an RS485 network and programming it to function as a Modbus slave device.

How an Arduino can Speak over an RS485

First, let’s talk about Arduino and RS485.

Although Arduino supports serial communication through its built-in UART (Universally Asynchronous Receiver/Transmitter), it uses TTL (Transistor-Transistor Logic), not RS485. Both signaling types use serial communication. Serial communication means data is sent one bit at a time at a specified BAUD rate (bits per second). 

However, TTL is single-ended, whereas RS485 relies on a differential signal (what's the difference?). This is far more useful in an industrial setting where signals can be influenced by electrical noise, and devices can be separated dozens or hundreds of meters.

To allow an Arduino to speak over an RS485 network, an additional device must be used to convert TTL to RS485. There are several devices on the market that do this, but I used this RS485 Transceiver Breakout Board.

Hardware Set-Up and Serial Ports

This picture shows my hardware setup.

Because I’m using an Arduino Mega, 4 serial ports available. If you are unsure how many ports you have, the Arduino website provides a description of serial ports on all board models.

Did you run out of Serial ports? Don’t forget about Software Serial!

Great! Your Arduino is now networked to the PLC (or another master device) and other non-master devices.

Installing Arduino IDE

Next is software. I’ll be using the Arduino IDE for software development.

Although Modbus was developed in 1979, it has stood the test of time and is proven to still a reliable industrial communication protocol. You may choose to write your own C++ library implementation of Modbus, but I opted to use a prewritten library.

A quick Google search yields several options. I chose this one.

Include ModbusRtu Library

The first step is to include the ModbusRtu library in your sketch.

The Modbus constructor takes three parameters:

  1. Slave Address – this is the Modbus address that the master device will use to make specific queries. In other words, it’s how other devices in the network know who is talking. The address can be any number between 1 and 247; address 0 is reserved for the master. It’s important to ensure there are no duplicate addresses on the network.
  1. Serial Port – this tells the library which Serial port to use. Here is the list of options, depending on the number of ports available on your Arduino model:

    0 = Serial

    1 = Serial1

    2 = Serial2

    3 = Serial3

    4 = SoftwareSerial

  2. TX Enable Pin – because this is a 2-wire RS485 network, it is only half duplex. Half duplex means that a device can only receive or transmit at a time. By default, the Arduino is in receive mode. Setting the TX enable pin high allows the Arduino to transmit data.

Modbus_array Example

Below is example code that shows how we can use our newly created RS485 network and Modbus library using a very simple (and probably unrealistic) scenario.

In this scenario, our Arduino is hooked up to a toggle switch and an LED. We have an operator who wants to know the state of the switch (on or off) and the ability to remotely toggle the LED. What should we do?

First, look at how the Modbus library function that allows for reading and writing:

Modbus::poll( uint16_t *regs, uint8_t u8size )

*regs is a register table used for communication exchange, and u8size is the size of the register table.

This method checks if there is an incoming query in its serial buffer. If there is, the library will validate the message (check the device address, data length, and CRC) and subsequently perform the correct function.

In other words, to use this function, we must pass it an unsigned 16-bit integer array and its length. The array will contain the data that the master device is reading or writing over.

For this simple example, I created an array called modbus_array that contains two elements.

  1. The first is a status bit for the LED. If the value of the first element is 0, the LED will turn off; otherwise, it will be on.
  2. The second element will be the state of the switch.

Now, let’s look at this from the perspective of the master device.

Toggling the LED

  1. To toggle the LED, it must send a Modbus message to slave address 1 that writes a single holding register (Modbus function #6) to the first element with a value of 0 or 1.
  2. To read the toggle switch, it must send a message that reads holding registers (Modbus function #3) starting at the second element with a length of 1. The Arduino will respond with the second element.
// Includes
#include <ModbusRtu.h>

// Defines
#define LED_PIN     5
#define SWITCH_PIN  6

// Variables
Modbus modbus_port;

// modbus_array = { LED control, toggle switch state }
uint16_t modbus_array[] = {0, 0};

void setup()
{  
  // Initiate a MODBUS object
  modbus_port = Modbus(1, 1, 7);

  // Start the serial port with a specified baud rate
  modbus_port.begin(9600);

  pinMode(LED_PIN, OUTPUT);
  pinMode(SWITCH_PIN, INPUT);
}

void loop()
{
  // Allow the master to access the registry table
  modbus_port.poll(modbus_array, sizeof(modbus_array)/sizeof(modbus_array[0]));

  // If the master device sets the first element in the array to 0, turn off the LED.
  // Any other number will make the LED go high.
  if (modbus_array[0] == 0) {
    digitalWrite(LED_PIN, LOW);
  } else {
    digitalWrite(LED_PIN, HIGH);
  }

  // Set the second element in the array to the state of the switch
  modbus_array[1] = digitalRead(SWITCH_PIN);
}

Conclusion

Arduinos are usually confined to hobbyist and educational markets because of their low barrier to entry, but this also makes them ideal candidates for prototyping, even in the industrial automation space.

Do you have a low fidelity project that could use some low-cost microcontrollers? Check out our Embedded Development and Programming service area to find out if DMC could be good fit for you! Or, Contact Us to get started on a solution today.

Comments

There are currently no comments, be the first to post one.

Post a comment

Name (required)

Email (required)

CAPTCHA image
Enter the code shown above: