NORVI devices are cutting-edge technological solutions designed to enhance efficiency, connectivity, and control in various industries. Leveraging advanced IoT technology, NORVI devices offer seamless integration with existing infrastructure, providing real-time monitoring, analytics, and automation capabilities.
In this document, we will explore the process of NORVI acting as a MODBUS master. We’ll focus on the interaction between the NORVI device and MODBUS Slave software, examining how to read the values of the digital inputs, analog inputs, and relay/transistor outputs from the MODBUS Software and storing the values in the NORVI device.
MODBUS Protocol #
MODBUS is a serial communication protocol developed by Modicon and published by Modicon in 1979 for use with its programmable logic controllers (PLCs). In simple terms, it is a method used for transmitting information over serial lines between electronic devices. The device requesting the information is called the MODBUS Master(Client) and the devices supplying information are MODBUS Slave (Servers). A standard MODBUS network has one master and up to 247 Slaves, each with a unique Slave ID from 1 to 247. The master can also write information to the Slaves.
Several versions of the MODBUS protocol exist for the serial port and Ethernet and the most common are:
- MODBUS RTU
- MODBUS ASCII
- MODBUS TCP
- MODBUS Plus
How does it work? #
MODBUS is transmitted over serial lines between devices. The simplest setup would be a single serial cable connecting the serial ports on two devices, a Client and a Server. The data is sent as a series of ones and zeroes called bits. Each bit is sent as a voltage. Zeroes are sent as positive voltages and ones as negative. The bits are sent very quickly. A typical transmission speed is 9600 baud (bits per second).
Check this link to understand more about the MODBUS protocol.
RTU (RS485) Mode #
MODBUS RTU, specifically tailored for serial communication, utilizes interfaces such as RS-232 or RS-485. When controllers are set up to communicate on a MODBUS network using RTU (Remote Terminal Unit) mode, each 8–bit byte in a message contains two 4–bit hexadecimal characters. The main advantage of this mode is that its greater character density allows better data throughput than ASCII for the same baud rate. Each message must be transmitted in a continuous stream.
The format for each byte in RTU mode is:
Coding System
- 8–bit binary, hexadecimal 0–9, A–F
- Two hexadecimal characters are contained in each
- 8–bit field of the message
Bits per Byte
- 1 start bit
- 8 data bits, the least significant bit sent first
- 1 bit for even/odd parity; no bit for no parity
- 1 stop bit if parity is used; 2 bits if no parity
Error Check Field
- Cyclical Redundancy Check (CRC)
RS485 communication in the NORVI devices. #
MODBUS functionality is available across all NORVI devices supporting RS485 connectivity. NORVI devices are equipped with an integrated MAX485 driver, featuring RX, TX, and FC pins seamlessly linked to the corresponding GPIOs. Within these devices, the opposing terminals of the MAX485 driver are denoted as A&B.
For specific RS485 GPIO allocations of the NORVI devices, refer to the NORVI RS485 GPIO Allocation.
However direct use of serial print functions for communication is not possible for the NORVI-IIOT-AE01 and NORVI-IIOT-AE02 product ranges because RS485 TX and RX are shared with the USB RX and TX pins. As a solution for such situations, we can display the result in the OLED display as described in the troubleshooting section.
NORVI as a MODBUS RTU Master #
There are many versions of the MODBUS protocol and in this instructable MODBUS RTU will be used. Here we’ll be considering the NORVI ESP32 device as a master and “Modbus Slave” software will be the slave. with the NORVI device, we’ll be reading the data values of the digital inputs, analog inputs, and relay/transistor outputs from the MODBUS Software and storing the values in the device. Here we are using the NORVI-GSM-AE08-I-L device.
Requirements to get started NORVI device as a master, #
- The NORVI device
- Wires for connection
- USB cable.
- USB to RS-485 Converter module.
- MODBUS Slave device or simulator.
Circuit Connection #
Connect the RS485 A & B pins of the NORVI device with the A & B pins of the USB to the RS-485 converter module.
Power up the device and connect the USB cable.
Connect the USB to the RS-485 converter module to the PC.
Writing the Test Program. #
We need to write the program for reading the values of the digital inputs, analog inputs, and relay/transistor outputs from the MODBUS Software and storing the values in the NORVI device.
Download the example program from here.
Check the pin configuration of the program according to the NORVI device.
Compile and Upload the program.
Understanding the Test program #
Add the required library
#include <ModbusMaster.h>
Define the RX, TX, and FC of the device.
#define FC 22
#define RX_PIN 25
#define TX_PIN 26
Define the functions required for setting the state of the flow control pin and the object node for MODBUS master is named.
ModbusMaster node;
void preTransmission()
{
digitalWrite(FC, 1);
}
void postTransmission()
{
digitalWrite(FC, 0);
}
Setup function:
Initialize the Serial Monitor at a baud rate of 9600, RS-485 serial connection, and sets the Flow Control pin (FC) as an output pin. Then initializes the MODBUS master library with a slave ID of 1 and the software serial port (Serial1) for communication.
void setup()
{
Serial.begin(9600);
pinMode(FC, OUTPUT);
digitalWrite(FC, 0);
Serial1.begin(9600, SERIAL_8N1, RX_PIN, TX_PIN);
node.begin(1, Serial1); //Slave ID as 1
node.preTransmission(preTransmission);
node.postTransmission(postTransmission);
}
Loop function:
Declares a variable “value” of type “uint8_t”. Send the MODBUS read request for holding registers starting from address 0x40001 with a length of 3 registers and print the values of the response buffers to the Serial Monitor.
void loop()
{
uint8_t value;
value = node.readHoldingRegisters(0x40001, 3);
Serial.print("\n");
Serial.print(" ANIN1 : ");
Serial.print(node.getResponseBuffer(0x00));
Serial.print(" ANIN2 : ");
Serial.print(node.getResponseBuffer(0x01));
Serial.print(" ANIN3 : ");
Serial.print(node.getResponseBuffer(0x02));
Serial.print("\n");
delay(500);
Declares an array named result of type uint8_t with a size of 3 elements. Sends a MODBUS request to read 1 discrete input starting from address 0x10001 from the MODBUS slave device and prints the values of the response buffers to the Serial Monitor.
uint8_t result[3];
result[1] = node.readDiscreteInputs(0x10001, 1);
Serial.print(" INPUT1 VALUE : ");
Serial.print(node.getResponseBuffer(result[1]));
Serial.print("\n");
delay(500);
result[2] = node.readDiscreteInputs(0x10002, 1);
Serial.print(" INPUT2 VALUE : ");
Serial.print(node.getResponseBuffer(result[2]));
Serial.print("\n");
delay(500);
result[3] = node.readDiscreteInputs(0x10003, 1);
Serial.print(" INPUT3 VALUE : ");
Serial.print(node.getResponseBuffer(result[3]));
Serial.print("\n");
delay(500);
Result of the NORVI device as a MODBUS RTU Master. #
Enter the setup details to match the Slave and Master.
Then generate the response and it is displayed in the response table.
After the response is generated, it is written to the serial port.
Troubleshooting #
RS485 Check #
Before uploading the MODBUS code, RS485 two ways can be checked by setting the FC pin high and low. This test program sets up communication between two serial ports, the hardware serial port (Serial) and a software serial port (Serial1).
Download the RS485 example program from here and check the pin configuration of the program.
Here’s a breakdown of what each part of the code does,
Defined the FC (Flow Control) pin, RXD (Receive Data) pin, and the TXD (Transmit Data) pin.
#define RXD 33
#define TXD 2
#define FC 4
Setup function:
Initializes the hardware serial port (Serial) with a baud rate of 9600 bits per second. Initializes the software serial port (Serial1) with a baud rate of 9600 bits per second, 8 data bits, no parity bit, and 1 stop bit. It also specifies the RXD and TXD PINs defined earlier. Sets the Flow Control pin (FC) as an output pin.
void setup() {
Serial.begin(9600);
pinMode(FC, OUTPUT);
Serial1.begin(9600, SERIAL_8N1,RXD,TXD);
}
Loop function:
Sets the Flow Control pin (FC) to HIGH, indicating that the Arduino is ready to transmit data. Sends the message “RS485 01 SUCCESS” over the RS485 protocol using the software serial port (Serial1). Then set the Flow Control pin (FC) to LOW, indicating that the Arduino is ready to receive data.
void loop() {
digitalWrite(FC, HIGH); // Make FLOW CONTROL pin HIGH
Serial1.println(F("RS485 01 SUCCESS")); // Send RS485 SUCCESS serially
delay(500); // Wait for transmission of data
digitalWrite(FC, LOW) ; // Receiving mode ON
// Serial1.flush() ;
While loop checks if there is data available to read from the software serial port (Serial1). If there is data available: Reads a character from the software serial port and stores it in the variable n. Writes the character c to the hardware serial port.
while (Serial1.available()) { // Check if data is available
char c = Serial1.read(); // Read data from RS485
Serial.write(c); // Print data on serial monitor
Software serial window,
Once both serial windows are open, enter the message into the serial window and send it. From this RS485 can check for both sides.
Hardware serial window,
Sharing RX and TX with USB (NORVI IIOT AE01/NORVI IIOT AE02) #
Utilize the NORVI-IIOT-AE01 and NORVI-IIOT-AE02 product ranges RS485 communication where the TX (Transmit) and RX (Receive) pins are shared with the USB RX and TX pins. This means that the same physical pins are used for both serial communication with a computer via USB and RS485 communication.
As a result, direct use of serial print functions for communication is not possible when connected to such devices. An alternative solution would be to output the result to an OLED display, providing visual feedback on the data.
Before uploading the MODBUS code, RS485 two ways can be checked by setting the FC pin high and low. Download the RS485 example program from here and check the pin configuration of the program.
Download the example program from here.
This program sets up a NORVI-IIOT-AE02 device acting as a MODBUS master. It reads the digital inputs, analog inputs, and relay/transistor outputs from the slave and stores the values in the device.
For specific RS485 GPIO allocations for NORVI devices, refer to the NORVI RS485 GPIO Allocation.
we are using the NORVI-IIOT-AE02 device. It has common RX and TX pins.
- RS485_RX – 3
- RS485_TX – 1
- RS485_FC – 4
- USB_RX – 3
- USB_TX – 1
Add the required libraries.
- Modbus-esp8266
- Adafruit GFX
- Adafruit SSD1306
#include <Wire.h>
#include <ModbusRTU.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
Defines the screen width, height, and reset pin for the OLED display. Initializes the SSD1306 display object with these parameters. Defines the MODBUS slave ID, the first holding register address to read, the number of holding registers to read, and the function code.
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define SLAVE_ID 5
#define FIRST_HREG 2
#define HREG_COUNT 7
#define FC 4
Declares an instance of the MODBUS RTU class. Defines a callback function cb() which is not currently utilized.
ModbusRTU mb;
bool cb(Modbus::ResultCode event, uint16_t transactionId, void* data) {
if (event != Modbus::EX_SUCCESS) {
}
return true;
}
Setup function:
Initializes the serial communication, establishes MODBUS RTU communication with the specified function code, sets the Arduino pin corresponding to the function code to LOW, initializes MODBUS RTU as master, initiates I2C communication with the specified pins for the OLED display, and initializes the display.
void setup() {
Serial.begin(9600);
mb.begin(&Serial, FC);
digitalWrite(FC, LOW);
mb.master();
Wire.begin(16, 17);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.display();
}
Loop function:
Reads holding registers from the slave device, if the Master device is not acting as a slave itself. It then displays the readings on the OLED screen.
void loop() {
uint16_t res[HREG_COUNT];
if (!mb.slave()) {
mb.readHreg(SLAVE_ID, FIRST_HREG, res, HREG_COUNT, cb);
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(1);
display.setCursor(0, 0);
display.print("INP 1- "); display.print(res[0]);
display.setCursor(70, 0);
display.print("INP 2- "); display.print(res[1]);
display.setCursor(0, 14);
display.print("INP 3- "); display.print(res[2]);
display.setCursor(70, 14);
display.print("INP 4- "); display.print(res[3]);
display.setCursor(0, 30);
display.print("INP 5- "); display.print(res[4]);
display.setCursor(70, 30);
display.print("INP 6- "); display.print(res[5]);
display.setCursor(0, 48);
display.print("INP 7- "); display.print(res[6]);
display.setCursor(70, 48);
display.print("INP 8- "); display.print(res[7]);
display.display();
}