ESP32 Device As a RS-485 Modbus Slave | Norvi IIOT
by NORVI Controllers in Circuits > Electronics
18823 Views, 6 Favorites, 0 Comments
ESP32 Device As a RS-485 Modbus Slave | Norvi IIOT
RS-485 is a serial data transmission standard widely used in industrial implementations. The Modbus protocol is commonly used when implementing RS-485 communication. It is a communication protocol for transmitting information over serial lines between electronic devices. Here, a single serial cable linking the serial ports on two devices will be the simplest configuration. The device requesting the information is called the Modbus Master and the devices supplying information are Modbus Slave. In a standard Modbus network, there is one Master and up to 247 Slaves, each with a unique Slave address from 1 to 247. The Master can also write information to the Slaves.
There are many versions of the Modbus protocol and in this instructable Modbus RTU will be used. Here we'll be considering the simplest configuration, where the Norvi AE04-V / Norvi AE01-R ESP32 device will be the slave and "Simply Modbus" software will be the master. With Norvi AE04-V ,we'll be reading the digital inputs and storing the analog output values in the Modbus Software and with Norvi AE01-R device, digital inputs are read and also the relay outputs are controlled from the Master software. (Each scenario is discussed separately.)
Check this link to understand more about the Modbus protocol - Simply Modbus
So what do you need to get started?
For Norvi IIOT-AE01-R device as a slave,
- The Norvi IIOT-AE01-R device Check more about the product
- Wires for connection
- USB cable (Type A to Type B mini)
- USB to RS-485 Converter module.
- PC/Laptop
-
The Simply Modbus Software
Check this tutorial to know more about how to work with Norvi IIOT-AE01-R
For Norvi IIOT-AE04-V device as slave,
- The Norvi IIOT-AE04-V device Check more about the product
- Wires for connection
- USB cable (Type A to micro B)
- PC/Laptop
- The Simply Modbus Software
-
USB to RS-485 Converter module
Check this tutorial to know more about the working of Norvi AE04-V.(Norvi AE04-V has extra features like expansion slot for micro SD when compared with Norvi AE02-V, apart from this the working of both the devices will be the same.)
Understanding More About the RS-485 Connection
In Norvi IIOT-AE01-R,
- The Device has a built-in MAX485 driver with RX connected with GPIO3, TX connected with GPIO1 and RE & DE are connected with the GPIO4 (for flow control) of the Norvi ESP32 Device.
In Norvi IIOT-AE04-V,
- The Device has a built-in MAX485 driver with RX connected with GPIO2, TX connected with GPIO33 and RE & DE are connected with the GPIO4 (for flow control) of the Norvi ESP32 Device.
**For Norvi IIOT-AE04-V, in the UART library, the default RX2 & TX2 pins should be swapped with 33 and 2 respectively, before uploading the sketch to the device. Check this video to know how to implement this change.
**In both the devices, the other end of the MAX485 driver is indicated as A&B.
GPIOs of the Devices.
Norvi IIOT-AE01-R,
- Check steps 4 and 5 in this instructable.
Norvi IIOT-AE04-V,
- Digital Inputs
I.0 = 39 I.1 = 34 I.2 = 35 I.3 = 13 I.4 = 21 I.5 = 22 I.6 = 15
- There are 6 analog input pins starting from A0-A5 which are capable of measuring 0-10V industrial level voltages. The first four inputs (A0-A3) are connected to the four channels of the first ADS1115 module of address 0x48 and the final two inputs (A5 & A6) are connected to the first two channels of the second ADS1115 module.
- The GPIO pins of the transistors are as follows,
T.0 = 26 T.1 = 27 PWM compatible (36VDC max with 360mW collector power dissipation)
Check step 2 of this instructable to understand more about Analog Inputs.
Writing the Program
- Install the Adafruit SSD1306, Adafruit GFX and Adafruit ADS1015, Modbus RTU libraries in your Arduino IDE and upload the below sketch.(For installing the ESP32 board and booting up the device, check the steps 1 and 2 of our previous instructable - Getting Started With Norvi Devices)
For Norvi IIOT-AE01-R,
#include <Wire.h> #include <SPI.h> #include <Adafruit_SSD1306.h> #include <Adafruit_GFX.h> #include <ModbusRTU.h> #define SLAVE_ID 1 #define INPUT1 18 #define INPUT2 39 #define INPUT3 34 #define INPUT4 35 #define INPUT5 19 #define INPUT6 21 #define INPUT7 22 #define INPUT8 23 #define OUTPUT1 14 #define OUTPUT2 12 #define OUTPUT3 13 #define OUTPUT4 15 #define OUTPUT5 2 #define OUTPUT6 33 ModbusRTU mb; #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 64 // OLED display height, in pixels Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1); // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) void setup() { Serial.begin(9600, SERIAL_8N1); #if defined(ESP32) || defined(ESP8266) mb.begin(&Serial,4); #else mb.begin(&Serial,4); //mb.begin(&Serial, RXTX_PIN); //or use RX/TX direction control pin (if required) mb.setBaudrate(9600); #endif Wire.begin(16, 17); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); //starting the display display.display(); delay(2000); display.clearDisplay(); pinMode(INPUT1,INPUT); pinMode(INPUT2,INPUT); pinMode(INPUT3,INPUT); pinMode(INPUT4,INPUT); pinMode(INPUT5,INPUT); pinMode(INPUT6,INPUT); pinMode(INPUT7,INPUT); pinMode(INPUT8,INPUT); pinMode(OUTPUT1,OUTPUT); pinMode(OUTPUT2,OUTPUT); pinMode(OUTPUT3,OUTPUT); pinMode(OUTPUT4,OUTPUT); pinMode(OUTPUT5,OUTPUT); pinMode(OUTPUT6,OUTPUT); } void loop() { Serial.print("INPUTS\n"); Serial.print(" "); Serial.println(digitalRead(INPUT1)); Serial.print(" "); Serial.println(digitalRead(INPUT2)); Serial.print(" "); Serial.println(digitalRead(INPUT3)); Serial.print(" "); Serial.println(digitalRead(INPUT4)); Serial.print(" "); Serial.println(digitalRead(INPUT5)); Serial.print(" "); Serial.println(digitalRead(INPUT6)); Serial.print(" "); Serial.println(digitalRead(INPUT7)); Serial.print("OUTPUTS\n"); Serial.print(" "); Serial.println(digitalRead(OUTPUT1)); Serial.print(" "); Serial.println(digitalRead(OUTPUT2)); Serial.print(" "); Serial.println(digitalRead(OUTPUT3)); Serial.print(" "); Serial.println(digitalRead(OUTPUT4)); Serial.print(" "); Serial.println(digitalRead(OUTPUT5)); Serial.print(" "); Serial.println(digitalRead(OUTPUT6)); delay(1000); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0, 0); display.println("Outputs"); display.setCursor(90, 0); display.println("Inputs"); display.setCursor(5, 8); display.println(digitalRead(OUTPUT1)); display.setCursor(5, 16); display.println(digitalRead(OUTPUT2)); display.setCursor(5, 24); display.println(digitalRead(OUTPUT3)); display.setCursor(5, 32); display.println(digitalRead(OUTPUT4)); display.setCursor(5, 40); display.println(digitalRead(OUTPUT5)); display.setCursor(5, 48); display.println(digitalRead(OUTPUT6)); display.setCursor(100, 8); display.println(digitalRead(INPUT1)); display.setCursor(100, 16); display.println(digitalRead(INPUT2)); display.setCursor(100, 24); display.println(digitalRead(INPUT3)); display.setCursor(100, 32); display.println(digitalRead(INPUT4)); display.setCursor(100, 40); display.println(digitalRead(INPUT5)); display.setCursor(100, 48); display.println(digitalRead(INPUT6)); display.setCursor(100, 54); display.println(digitalRead(INPUT7)); display.display(); delay(1000); display.clearDisplay(); mb.slave(SLAVE_ID); mb.addCoil(1); mb.Coil(1); mb.addCoil(2); mb.Coil(2); mb.addCoil(3); mb.Coil(3); mb.addCoil(4); mb.Coil(4); mb.addCoil(5); mb.Coil(5); mb.addCoil(6); mb.Coil(6); if(mb.Coil(1)==1) { digitalWrite(14,HIGH); } else {digitalWrite(14,LOW); } if(mb.Coil(2)==1) { digitalWrite(12,HIGH); } else {digitalWrite(12,LOW); } if(mb.Coil(3)==1) { digitalWrite(13,HIGH); } else {digitalWrite(13,LOW); } if(mb.Coil(4)==1) { digitalWrite(15,HIGH); } else {digitalWrite(15,LOW); } if(mb.Coil(5)==1) { digitalWrite(2,HIGH); } else {digitalWrite(2,LOW); } if(mb.Coil(6)==1) { digitalWrite(33,HIGH); } else {digitalWrite(33,LOW); } 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)); mb.addIsts(5); mb.Ists(5,digitalRead(INPUT5)); mb.addIsts(6); mb.Ists(6,digitalRead(INPUT6)); mb.addIsts(7); mb.Ists(7,digitalRead(INPUT7)); mb.task(); yield(); }
For Norvi IIOT-AE04-V,
#include <Wire.h> #include <SPI.h> #include <Adafruit_ADS1015.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include <ModbusRTU.h> #define SLAVE_ID 1 #define INPUT1 39 #define INPUT2 34 #define INPUT3 35 #define INPUT4 13 #define INPUT5 21 #define INPUT6 22 #define INPUT7 15 ModbusRTU mb; #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 64 // OLED display height, in pixels Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1); // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) Adafruit_ADS1115 ads1(0x48); Adafruit_ADS1115 ads2(0x49); float Voltage1 = 0.0; float Voltage2 = 0.0; float Voltage3 = 0.0; float Voltage4 = 0.0; float Voltage5 = 0.0; float Voltage6 = 0.0; void setup() { Serial.begin(9600, SERIAL_8N1); Serial2.begin(9600, SERIAL_8N1); #if defined(ESP32) || defined(ESP8266) mb.begin(&Serial2,4); #else mb.begin(&Serial2,4); //mb.begin(&Serial, RXTX_PIN); //or use RX/TX direction control pin (if required) mb.setBaudrate(9600); #endif Wire.begin(16, 17); ads1.begin(); ads2.begin(); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); //starting the display display.display(); delay(2000); display.clearDisplay(); pinMode(INPUT1,INPUT); pinMode(INPUT2,INPUT); pinMode(INPUT3,INPUT); pinMode(INPUT4,INPUT); pinMode(INPUT5,INPUT); pinMode(INPUT6,INPUT); pinMode(INPUT7,INPUT); } void loop() { float adc0 = ads1.readADC_SingleEnded(0); float adc1 = ads1.readADC_SingleEnded(1); float adc2 = ads1.readADC_SingleEnded(2); float adc3 = ads1.readADC_SingleEnded(3); float adc4 = ads2.readADC_SingleEnded(0); float adc5 = ads2.readADC_SingleEnded(1); Voltage1 = (adc0 * 0.1875) / 1000; Voltage2 = (adc1 * 0.1875) / 1000; Voltage3 = (adc2 * 0.1875) / 1000; Voltage4 = (adc3 * 0.1875) / 1000; Voltage5 = (adc4 * 0.1875) / 1000; Voltage6 = (adc5 * 0.1875) / 1000; Serial.print("Voltages\n"); Serial.print(" "); Serial.println(Voltage1 * 2.5, 3); Serial.print(" "); Serial.println(Voltage2 * 2.5, 3); Serial.print(" "); Serial.println(Voltage3 * 2.5, 3); Serial.print(" "); Serial.println(Voltage4 * 2.5, 3); Serial.print(" "); Serial.println(Voltage5 * 2.5, 3); Serial.print(" "); Serial.println(Voltage6 * 2.5, 3); Serial.print("INPUTS\n"); Serial.print(" "); Serial.println(digitalRead(INPUT1)); Serial.print(" "); Serial.println(digitalRead(INPUT2)); Serial.print(" "); Serial.println(digitalRead(INPUT3)); Serial.print(" "); Serial.println(digitalRead(INPUT4)); Serial.print(" "); Serial.println(digitalRead(INPUT5)); Serial.print(" "); Serial.println(digitalRead(INPUT6)); Serial.print(" "); Serial.println(digitalRead(INPUT7)); delay(1000); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0, 0); display.println("Voltage"); display.setCursor(90, 0); display.println("Inputs"); display.setCursor(5, 8); display.println(Voltage1 * 2.5); display.setCursor(5, 16); display.println(Voltage2 * 2.5); display.setCursor(5, 24); display.println(Voltage3 * 2.5); display.setCursor(5, 32); display.println(Voltage4 * 2.5); display.setCursor(5, 40); display.println(Voltage5 * 2.5); display.setCursor(5, 48); display.println(Voltage6 * 2.5); display.display(); display.setCursor(100, 8); display.println(digitalRead(INPUT1)); display.setCursor(100, 16); display.println(digitalRead(INPUT2)); display.setCursor(100, 24); display.println(digitalRead(INPUT3)); display.setCursor(100, 32); display.println(digitalRead(INPUT4)); display.setCursor(100, 40); display.println(digitalRead(INPUT5)); display.setCursor(100, 48); display.println(digitalRead(INPUT6)); display.setCursor(100, 54); display.println(digitalRead(INPUT7)); display.display(); delay(1000); display.clearDisplay(); mb.slave(SLAVE_ID); mb.addHreg(1); mb.Hreg(1,100); mb.addHreg(2); mb.Hreg(2,Voltage1*2.5); mb.addHreg(3); mb.Hreg(3,Voltage2*2.5); mb.addHreg(4); mb.Hreg(4,Voltage3*2.5); mb.addHreg(5); mb.Hreg(5,Voltage4*2.5); mb.addHreg(6); mb.Hreg(6,Voltage5*2.5); mb.addHreg(7); mb.Hreg(7,Voltage6*2.5); 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)); mb.addIsts(5); mb.Ists(5,digitalRead(INPUT5)); mb.addIsts(6); mb.Ists(6,digitalRead(INPUT6)); mb.addIsts(7); mb.Ists(7,digitalRead(INPUT7)); mb.task(); yield(); }
Circuit Connection
- Connect the MAX 485 driver's A & B pins with the A & B pins of USB to the RS-485 module.
Understanding the Code
For Norvi IIOT-AE01-R,
- We begin by adding the required libraries.
#include <Wire.h> #include <SPI.h> #include <Adafruit_SSD1306.h> #include <Adafruit_GFX.h> #include <ModbusRTU.h><br>
- Next, we define the Slave ID, inputs and outputs of the device.
#define SLAVE_ID 1 #define INPUT1 18 #define INPUT2 39 #define INPUT3 34 #define INPUT4 35 #define INPUT5 19 #define INPUT6 21 #define INPUT7 22 #define INPUT8 23 #define OUTPUT1 14 #define OUTPUT2 12 #define OUTPUT3 13 #define OUTPUT4 15 #define OUTPUT5 2 #define OUTPUT6 33 ModbusRTU mb;<br>
-
Then, the width and height of our OLED display are defined.
#define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 64 // OLED display height, in pixels<br>
-
Next, with I2C communication protocol (&Wire), a display object with the width and height defined earlier is initialized.
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);<br>
-
Next, in the setup(), we initialize the Serial Monitor at a baud rate of 115200.
Serial.begin(9600, SERIAL_8N1);<br>
- Then the RS-485 serial connection is initiated.
#if defined(ESP32) || defined(ESP8266) mb.begin(&Serial,4); #else mb.begin(&Serial,4); //mb.begin(&Serial, RXTX_PIN); //or use RX/TX direction control pin (if required) mb.setBaudrate(9600); #endif<br>
-
In the following line, you initialize I2C by setting the sda and scl GPIOs to communicate with the display.
Wire.begin(16, 17);<br>
-
Next, the display is initialized using its address.
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); //starting the display<br>
-
Using the below lines, we can ensure whether our display is working correctly. Here the display check is done for 2s (2000ms) and this can be varied.
display.display(); delay(2000); display.clearDisplay();<br>
- Then the pin mode of each input and output is defined.
pinMode(INPUT1,INPUT); pinMode(INPUT2,INPUT); pinMode(INPUT3,INPUT); pinMode(INPUT4,INPUT); pinMode(INPUT5,INPUT); pinMode(INPUT6,INPUT); pinMode(INPUT7,INPUT); pinMode(INPUT8,INPUT); pinMode(OUTPUT1,OUTPUT); pinMode(OUTPUT2,OUTPUT); pinMode(OUTPUT3,OUTPUT); pinMode(OUTPUT4,OUTPUT); pinMode(OUTPUT5,OUTPUT); pinMode(OUTPUT6,OUTPUT);<br>
- These lines will print the status of the digital inputs and the relay outputs in the serial monitor.
Serial.print("INPUTS\n"); Serial.print(" "); Serial.println(digitalRead(INPUT1)); Serial.print(" "); Serial.println(digitalRead(INPUT2)); Serial.print(" "); Serial.println(digitalRead(INPUT3)); Serial.print(" "); Serial.println(digitalRead(INPUT4)); Serial.print(" "); Serial.println(digitalRead(INPUT5)); Serial.print(" "); Serial.println(digitalRead(INPUT6)); Serial.print(" "); Serial.println(digitalRead(INPUT7)); Serial.print("OUTPUTS\n"); Serial.print(" "); Serial.println(digitalRead(OUTPUT1)); Serial.print(" "); Serial.println(digitalRead(OUTPUT2)); Serial.print(" "); Serial.println(digitalRead(OUTPUT3)); Serial.print(" "); Serial.println(digitalRead(OUTPUT4)); Serial.print(" "); Serial.println(digitalRead(OUTPUT5)); Serial.print(" "); Serial.println(digitalRead(OUTPUT6));<br>
-
The text size, text color and text position are defined and the values read and are displayed on the OLED screen.
display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0, 0); display.println("Outputs"); display.setCursor(90, 0); display.println("Inputs"); display.setCursor(5, 8); display.println(digitalRead(OUTPUT1)); display.setCursor(5, 16); display.println(digitalRead(OUTPUT2)); display.setCursor(5, 24); display.println(digitalRead(OUTPUT3)); display.setCursor(5, 32); display.println(digitalRead(OUTPUT4)); display.setCursor(5, 40); display.println(digitalRead(OUTPUT5)); display.setCursor(5, 48); display.println(digitalRead(OUTPUT6)); display.setCursor(100, 8); display.println(digitalRead(INPUT1)); display.setCursor(100, 16); display.println(digitalRead(INPUT2)); display.setCursor(100, 24); display.println(digitalRead(INPUT3)); display.setCursor(100, 32); display.println(digitalRead(INPUT4)); display.setCursor(100, 40); display.println(digitalRead(INPUT5)); display.setCursor(100, 48); display.println(digitalRead(INPUT6)); display.setCursor(100, 54); display.println(digitalRead(INPUT7)); display.display(); delay(1000); display.clearDisplay();<br>
- Discrete output coils are added to read the relay output status and control them.
mb.slave(SLAVE_ID); mb.addCoil(1); mb.Coil(1); mb.addCoil(2); mb.Coil(2); mb.addCoil(3); mb.Coil(3); mb.addCoil(4); mb.Coil(4); mb.addCoil(5); mb.Coil(5); mb.addCoil(6); mb.Coil(6);<br>
- Using the below lines the relay outputs are controlled.
if(mb.Coil(1)==1) { digitalWrite(14,HIGH); } else {digitalWrite(14,LOW); } if(mb.Coil(2)==1) { digitalWrite(12,HIGH); } else {digitalWrite(12,LOW); } if(mb.Coil(3)==1) { digitalWrite(13,HIGH); } else {digitalWrite(13,LOW); } if(mb.Coil(4)==1) { digitalWrite(15,HIGH); } else {digitalWrite(15,LOW); } if(mb.Coil(5)==1) { digitalWrite(2,HIGH); } else {digitalWrite(2,LOW); } if(mb.Coil(6)==1) { digitalWrite(33,HIGH); } else {digitalWrite(33,LOW); }<br>
- Discrete input contacts are defined to store the digital input status.
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)); mb.addIsts(5); mb.Ists(5,digitalRead(INPUT5)); mb.addIsts(6); mb.Ists(6,digitalRead(INPUT6)); mb.addIsts(7); mb.Ists(7,digitalRead(INPUT7));<br>
- The below lines will initiate the Modbus connection between the Modbus software and the device.
mb.task(); yield();
**For IIOT-AE04-V, in the code, Holding registers are used to save and read analog output voltages.
mb.slave(SLAVE_ID); mb.addHreg(1); mb.Hreg(1,100); mb.addHreg(2); mb.Hreg(2,Voltage1*2.5); mb.addHreg(3); mb.Hreg(3,Voltage2*2.5); mb.addHreg(4); mb.Hreg(4,Voltage3*2.5); mb.addHreg(5); mb.Hreg(5,Voltage4*2.5); mb.addHreg(6); mb.Hreg(6,Voltage5*2.5); mb.addHreg(7); mb.Hreg(7,Voltage6*2.5);<br>
Understanding the Operation of the Simply Modbus Sofwtare
To check more about the Norvi lineup- - www.norvi.lk