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 explore integrating Norvi IIoT devices with Modbus to MQTT, collecting data from Modbus-enabled devices, publishing data and decoding the data by MQTT broker, and visualization in a DATA cake.
MQTT PROTOCOL #
MQTT (Message Queuing Telemetry Transport) is a lightweight messaging protocol designed for low-bandwidth, high-latency, or unreliable networks. It’s widely used in the Internet of Things (IoT) and machine-to-machine (M2M) communication due to its simplicity and efficiency.
Check this link to understand more about the MQTT PROTOCOL.
Here are the components of MQTT,
1. Publisher-Subscriber Model
- MQTT follows a publisher-subscriber model. In this model, there are two main entities, publishers and subscribers.
- Publishers are devices or applications that publish messages on a specific topic.
- Subscribers are devices or applications that receive messages by subscribing to specific topics.
2. Broker
- MQTT communication is facilitated by a broker. The broker is a server responsible for receiving, routing, and delivering messages.
- Publishers and subscribers connect to the broker to send and receive messages.
- The broker is responsible for managing topics, subscribers, and message delivery.
3. Topics
- Messages in MQTT are published to topics. Topics are hierarchical and can be thought of as addresses.
- Subscribers can subscribe to entire topics or specific subtopics using wildcards.
- For example, a topic could be sensors/temperature where sensors are the main topic and temperature is a subtopic.
4. Retained Messages
MQTT allows for retained messages, which are special messages that are stored by the broker and sent to new subscribers immediately upon subscription.
This feature is useful for sending status updates or configuration information to new subscribers.
5. Quality of Service (QoS)
- MQTT supports different levels of Quality of Service (QoS) for message delivery:
- QoS 0 (At most once): Messages are delivered at most once, without confirmation.
- QoS 1 (At least once): Messages are guaranteed to be delivered at least once, but duplicates may occur.
- QoS 2 (Exactly once): Messages are guaranteed to be delivered exactly once.
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 Simulator software will be the MODBUS 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 MQTT broker.
Check this link to understand more about the NORVI device as a MODBUS RTU master.
NORVI IIOT MODBUS to MQTT #
Here we’ll be considering the NORVI ESP32 device as a Publisher and “MQTT.FX” software as the Subscriber, and also the NORVI ESP32 device as a master and MODBUS simulator software as the MODBUS-enabled device. 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 MQTT Broker and the data visualization platform.
Prerequisites #
- Norvi IIoT device
- Modbus-enabled devices (sensors, PLCs, etc.)
- MQTT broker
- Subscriber
- Wi-Fi network
- Data visualization platform (e.g., DATACAKE)
Understanding the Test program #
This code establishes a connection to the Wi-Fi network using the provided credentials. It then initializes the Modbus communication with the sensors connected to the NORVI device. The code continuously reads data from the sensors and publishes it to the specified MQTT topic on the IoT platform. The data is formatted as a JSON payload containing values. This allows users to visualize and analyze the data on the IoT platform.
Download the example program.
Check the pin configuration of the program according to the NORVI device. Compile and Upload the program.
Include Libraries: The code includes the necessary libraries for WiFi connectivity, MQTT communication, and Modbus protocol. Find the necessary libraries from here.
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <ModbusMaster.h>
WiFi Credentials: Defines the WiFi network name (SSID) and password.
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
MQTT Broker Configuration: Sets up the MQTT broker details including server address, port, topic to publish, and authentication credentials.
const char* mqtt_server = "YOUR_MQTT_BROKER_ADDRESS";
const int mqtt_port = YOUR_MQTT_PORT;
const char* mqtt_topic = "YOUR_MQTT_TOPIC";
const char* mqtt_username = "YOUR_MQTT_USERNAME";
const char* mqtt_password = "YOUR_MQTT_PASSWORD";
Modbus Configuration: Defines Modbus function code and pins for RX/TX communication.
#define FC 22 //GPIO of the RS485 Flow control
#define RX_PIN 25 //GPIO of the RS485 RX
#define TX_PIN 26 //GPIO of the RS485 TX
ModbusMaster Initialization: Initializes a ModbusMaster object named “node”.
ModbusMaster node;
WiFi Client and MQTT Client Initialization: Initializes WiFi and MQTT clients using the configured WiFi client and MQTT client objects.
WiFiClient espClient;
PubSubClient client(espClient);
Pre and Post-Transmission Functions: These functions are callbacks used by the Modbus library to set the control pin high before transmission and low after transmission.
void preTransmission()
{
digitalWrite(FC, 1);
}
void postTransmission()
{
digitalWrite(FC, 0);
}
WiFi Connection Setup: Connects to the WiFi network using the provided credentials.
void setup_wifi() {
delay(10);
// Connect to WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
MQTT Reconnect Function: Tries to reconnect to the MQTT broker if the connection is lost.
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.println("");
Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect("ESP32Client", mqtt_username, mqtt_password)) {
Serial.println("connected");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
Setup Function: Initializes serial communication, pins, Modbus, WiFi connection, and MQTT client.
void setup()
{
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, mqtt_port);
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: Reconnects to MQTT if disconnected, then reads Modbus data and publishes it to the MQTT broker.
void loop()
{
uint8_t value;
value = node.readHoldingRegisters(0x40001, 3);
int ANIN1 = node.getResponseBuffer(0x00);
int ANIN2 = node.getResponseBuffer(0x01);
int ANIN3 = node.getResponseBuffer(0x02);
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);
uint8_t result[4];
result[1] = node.readDiscreteInputs(0x10001, 1);
int digitalInput1 = node.getResponseBuffer(result[1]);
Serial.print("\n DIGI IN1 : ");
Serial.print(node.getResponseBuffer(result[1]));
result[2] = node.readDiscreteInputs(0x10002, 1);
int digitalInput2 = node.getResponseBuffer(result[2]);
Serial.print(" \n DIGI IN2 : ");
Serial.print(node.getResponseBuffer(result[2]));
result[3] = node.readDiscreteInputs(0x10003, 1);
int digitalInput3 = node.getResponseBuffer(result[3]);
Serial.print(" \n DIGI IN3 : ");
Serial.print(node.getResponseBuffer(result[3]));
result[4] = node.readDiscreteInputs(0x10004, 1);
int digitalInput4 = node.getResponseBuffer(result[4]);
Serial.print(" \n DIGI IN4 : ");
Serial.print(node.getResponseBuffer(result[4]));
if (!client.connected()) {
reconnect();
}
client.loop();
// Read digital inputs from GPIO pins
DynamicJsonDocument doc(256);
doc["AN1"] = ANIN1;
doc["AN2"] = ANIN2;
doc["AN3"] = ANIN3;
doc["input1"] = digitalInput1;
doc["input2"] = digitalInput2;
doc["input3"] = digitalInput3;
doc["input4"] = digitalInput4;
String payload;
serializeJson(doc, payload);
// Publish payload to MQTT topic
client.publish(mqtt_topic, payload.c_str());
delay(10000); // Adjust the delay according to your needs
}
Software Configurations #
Check this link for detailed instructions on how to configure the MQTT broker and the Subscriber.
Results from MODBUS to MQTT conversion using the NORVI device. #
JSON Encoding #
JSON (JavaScript Object Notation) is a lightweight data-interchange format that is easy for humans to read and write and easy for machines to parse and generate. In the provided code, JSON is used to structure the sensor data (both analog and digital) before publishing it to an MQTT broker.
Steps of JSON Encoding in the Code,
- The ArduinoJson library is used to handle JSON encoding. It allows us to create a JSON document and serialize it into a string format.
#include <ArduinoJson.h>
- A DynamicJsonDocument object named doc is created with a capacity of 256 bytes. This object will hold the JSON structure.
DynamicJsonDocument doc(256);
- Each piece of sensor data is added to the doc object as a key-value pair. The keys (e.g., “input1”, “AN1”) are strings, and the values (e.g., digitalInput1, ANIN1) are the sensor readings.
doc["AN1"] = ANIN1;
doc["AN2"] = ANIN2;
doc["AN3"] = ANIN3;
doc["input1"] = digitalInput1;
doc["input2"] = digitalInput2;
doc["input3"] = digitalInput3;
doc["input4"] = digitalInput4;
- The serializeJson function converts the JSON document (doc) into a string (payload). This string can then be used to transmit the data over MQTT.
String payload;
serializeJson(doc, payload);
- The payload.c_str() function call converts the String object to a C-style string (a null-terminated array of characters). This is required by the publish method of the MQTT client. The JSON string is then published to the specified MQTT topic.
client.publish(mqtt_topic, payload.c_str());
Integration with Data Visualization Platform #
The NORVI Devices can be connected to an IoT platform, allowing users to visualize data collected by the NORVI devices. The platform used in this case is DATACAKE. Here’s how to integrate the NORVI devices with DATACAKE.
1. Access the DATACAKE from this link and navigate the Datacake dashboard. Select the “Add Devices”.
2. For the MQTT integration select the API. This will add the device to the Datacake account.
3. Click on Configuration Scroll down a bit and go to the new panel “MQTT Configuration”. Press on “Add New MQTT Broker”. Fill in the server details and add.
4. Then link the MQTT device on the broker to the device in the DATACAKE by providing the MQTT upline decoder. Subscribe to the topic on the MQTT broker and write a payload decoder.
This decoder function is designed to decode incoming messages from a device and format them into a structure that can be sent to the Datacake API for display or storage.
JSON Parsing: This function starts by converting the incoming payload, presumably in JSON format, into a JavaScript object. It uses JSON.parse() to accomplish this.
function Decoder(topic, payload) {
// Transform incoming payload to JSON
payload = JSON.parse(payload);
Data Extraction: It then extracts analog (ANIN1, ANIN2, ANIN3) and digital (INPUT1, INPUT2, INPUT3, INPUT4) inputs from the payload. Each value is converted to an integer using parseInt(). This assumes that the payload contains these fields as strings representing integer values.
// Extract analog and digital inputs from payload
var ANIN1 = parseInt(payload.AN1);
var ANIN2 = parseInt(payload.AN2);
var ANIN3 = parseInt(payload.AN3);
var INPUT1 = parseInt(payload.input1);
var INPUT2 = parseInt(payload.input2);
var INPUT3 = parseInt(payload.input3);
var INPUT4 = parseInt(payload.input4);
Data Forwarding: Finally, it constructs an array of objects where each object represents a data point to be sent to the Datacake Device API.
// Forward Data to Datacake Device API using Serial, Field-Identifier
return [
{
device: "Serial Number", // Serial Number or Device ID
field: "ANIN1",
value: ANIN1
},
{
device: "Serial Number", // Serial Number or Device ID
field: "ANIN2",
value: ANIN2
},
{
device: "Serial Number", // Serial Number or Device ID
field: "ANIN3",
value: ANIN3
},
{
device: "Serial Number", // Serial Number or Device ID
field: "INPUT1",
value: INPUT1
},
{
device: "Serial Number", // Serial Number or Device ID
field: "INPUT2",
value: INPUT2
},
{
device: "Serial Number", // Serial Number or Device ID
field: "INPUT3",
value: INPUT3
},
{
device: "Serial Number", // Serial Number or Device ID
field: "INPUT4",
value: INPUT4
}
];
}
“Try Decoder Function” allows the decoder function to be tested to verify its functionality before deploying it. By entering sample data and observing the output, you can verify that the decoder is correctly parsing incoming payloads and formatting the data in a way that can be sent to the Datacake API.
5. To create a first database field, click on the “Add Field”. This will open another modal asking for some required input for the fields.
When typing the name for that field, the identifier auto-fills. This identifier is unique and cannot be changed. it’s required for accessing the field from within the payload decoder or API.
6. If the device is already sending data, an indication of incoming data can be seen in the “Last data” column.
7. After the settings in the configuration section, the dashboard can be created. Use the Tab Bar on the Device to navigate into the ”Dashboard” view.