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 slave. We’ll focus on the interaction between the NORVI device and MODBUS Master software, examining how to read the digital inputs, and analog input and storing the values in the Modbus Software, and the relay/transistor outputs are controlled from the Modbus software.
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 SLAVE #
There are many versions of the MODBUS protocol and in this instructable MODBUS RTU will be used. Here we’ll consider the simplest configuration, where the NORVI ESP32 device will be the slave and “MODBUS Master” software will be the master. With the NORVI device, we’ll be reading the digital inputs, and analog input and storing the values in the MODBUS Software. The relay/transistor outputs are controlled by the MODBUS software.
Here we are using the NORVI-IIOT-AE04-V device.
Requirements to get started NORVI device as a slave, #
- The NORVI device
- Wires for connection
- USB cable.
- USB to RS-485 Converter module.
- MODBUS master 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 digital inputs, and analog input and storing the values in the MODBUS master Software. The relay/transistor outputs are controlled by the MODBUS master software.
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 libraries.
- Modbus-esp8266
- Adafruit GFX
- Adafruit ADS1x15
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_ADS1X15.h>
#include <ModbusRTU.h>
Define the Slave ID, RX, TX, and FC and the Inputs and Outputs of the device
#define SLAVE_ID 5
#define RXD 33
#define TXD 2
#define FC 4
#define INPUT1 18
#define INPUT2 34
#define INPUT3 35
#define INPUT4 19
#define OUTPUT1 26
#define OUTPUT2 27
Create Objects for the libraries.
ModbusRTU mb;
Adafruit_ADS1115 ads1;
Setup Function:
Initialize the Serial Monitor at a baud rate of 9600 and RS-485 serial connection. Here Serial1 is the RS485 serial connection. Initialize I2C by setting the SDA and SCL GPIOs to communicate with the ADC. Call the Slave ID. Defined the pin mode of each input and output.
void setup() {
Serial.begin(9600);
Serial1.begin(9600, SERIAL_8N1, RXD, TXD);
mb.begin(&Serial1, FC); // Use Serial for USB communication
mb.setBaudrate(9600);
Serial.println("Modbus Initialized");
Wire.begin(16, 17);
ads1.begin();
Serial.println("ADS Initialized");
pinMode(INPUT1, INPUT);
pinMode(INPUT2, INPUT);
pinMode(INPUT3, INPUT);
pinMode(INPUT4, INPUT);
pinMode(OUTPUT1, OUTPUT);
pinMode(OUTPUT2, OUTPUT);
}
Loop Function:
Holding registers are used to store and read analog input status .
void loop() {
int16_t adc0, adc1, adc2, adc3;
mb.addHreg(1);
mb.Hreg(1, adc0);
mb.addHreg(2);
mb.Hreg(2, adc1);
mb.addHreg(3);
mb.Hreg(3, adc2);
mb.addHreg(4);
mb.Hreg(4, adc3);
Discrete input coils are used to store and read the digital input status.
mb.slave(SLAVE_ID);
mb.addIsts(1);
mb.Ists(1, digitalRead(INPUT1));
mb.addIsts(2);
mb.Ists(2, digitalRead(INPUT2));
mb.addIsts(3);
mb.Ists(3, digitalRead(INPUT3));
mb.addIsts(4);
mb.Ists(4, digitalRead(INPUT4));
Discrete output coils are used to control the status of the transistor/relay outputs.
mb.slave(SLAVE_ID);
mb.addCoil(1);
mb.Coil(1);
mb.addCoil(2);
mb.Coil(2);
if (mb.Coil(1) == 1)
{
digitalWrite(26, HIGH);
}
else
{ digitalWrite(26, LOW);
}
if (mb.Coil(2) == 1)
{
digitalWrite(27, HIGH);
}
else
{ digitalWrite(27, LOW);
}
These lines will print the status of the digital inputs, status of the transistor/relay outputs, and status of the analog inputs.
Serial.println("\nInputs:");
Serial.print("Input 1: ");
Serial.println(digitalRead(INPUT1));
Serial.print("Input 2: ");
Serial.println(digitalRead(INPUT2));
Serial.print("Input 3: ");
Serial.println(digitalRead(INPUT3));
Serial.print("Input 4: ");
Serial.println(digitalRead(INPUT4));
Serial.print("\nTransistor outs\n");
Serial.print(" ");
Serial.println(digitalRead(OUTPUT1));
Serial.print(" ");
Serial.println(digitalRead(OUTPUT2));
Serial.print("\nVoltages\n");
Serial.print(" ");
Serial.println(adc0);
Serial.print(" ");
Serial.println(adc1);
Serial.print(" ");
Serial.println(adc2);
Serial.print(" ");
Serial.println(adc3);
Initiate the MODBUS connection between the MODBUS software and the device.
mb.task();
yield();
Result of the NORVI device as a MODBUS RTU SLAVE. #
- Read Analog Input
Send the request after changing the serial settings and building a request string.
Then the response is displayed in the response table. The initial values are shown below.
Change the slave values. The changing values are displayed in the response table.
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 sets 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 open the 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) #
NORVI-IIOT-AE01 and NORVI-IIOT-AE02 product ranges utilize 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 slave. It reads holding registers from the slave and displays the values on an OLED display.
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
Defines the screen width, height, and reset pin for the OLED display. Initializes the SSD1306 display object with these parameters.
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
#include <ModbusRTU.h>
Defines the screen width, height, and reset pin for the OLED display. Initializes the SSD1306 display object with these parameters. Then defines the MODBUS slave ID, RX, TX, and the function code and the Inputs and Outputs of the device.
#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 RXD 3
#define TXD 1
#define FC 4
//
#define INPUT1 18
#define INPUT2 39
#define INPUT3 34
#define INPUT4 35
Declares an instance of the MODBUS RTU class.
ModbusRTU mb;
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. Defined the pin mode of each input.
void setup() {
//Serial.begin(9600);
Serial.begin(9600, SERIAL_8N1,RXD,TXD);
mb.begin(&Serial1, FC); // Use Serial for USB communication
mb.setBaudrate(9600);
Serial.println("Modbus Initialized");
//Initialize Digital Inputs
pinMode(INPUT1, INPUT);
pinMode(INPUT2, INPUT);
pinMode(INPUT3, INPUT);
pinMode(INPUT4, INPUT);
Wire.begin(16, 17);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.display();
}
Loop function:
Reads Discreat Input Coils from the slave device stored in the master device.
mb.slave(SLAVE_ID);
mb.addIsts(1);
mb.Ists(1,digitalRead(INPUT1));
mb.addIsts(2);
mb.Ists(2,digitalRead(INPUT2));
mb.addIsts(3);
mb.Ists(3,digitalRead(INPUT3));
mb.addIsts(4);
mb.Ists(4,digitalRead(INPUT4));
Then display the readings on the OLED screen of the slave device.
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(1);
display.setCursor(0, 0);
display.print("INP 1- "); display.print(digitalRead(INPUT1));
display.setCursor(70, 0);
display.print("INP 2- "); display.println(digitalRead(INPUT2));
display.setCursor(0, 14);
display.print("INP 3- "); display.print(digitalRead(INPUT3));
display.setCursor(70, 14);
display.print("INP 4- "); display.println(digitalRead(INPUT4));