Introduction
The NORVI-GSM, powered by the ESP32-WROOM32 microcontroller and equipped with a Quectel EC25 GSM module, connects to the ThingsBoard IoT platform using cellular data via the MQTT protocol. It reads modbus readings(via RS485) and securely transmits this data to ThingsBoard, allowing users to monitor real-time values through customizable dashboards. This setup enables reliable remote access to sensor data, ensuring continuous monitoring and management without requiring physical interaction with the device.
System Architecture
The system architecture of the NORVI-GSM device integrated with ThingsBoard is designed to provide continuous, real-time monitoring of Modbus readings using secure cellular communication. The core components and data flow are as follows:
- NORVI-GSM Device
At the core of the system is the NORVI-GSM device, equipped with the ESP32-WROOM32 microcontroller and the Quectel EC25 GSM module. The device continuously reads Modbus readings from connected sensors or inputs. - Cellular Connectivity
The EC25 GSM module establishes a mobile data connection through the cellular network, providing internet access without the need for Wi-Fi, making it ideal for remote locations. - MQTT Communication Protocol
Using the lightweight MQTT protocol, the NORVI-GSM device publishes the modbus data as telemetry messages to the ThingsBoard MQTT broker. MQTT ensures reliable, low-latency, and bandwidth-efficient data transfer over the cellular network. - ThingsBoard IoT Platform
ThingsBoard acts as the cloud-based platform for device management, data ingestion, processing, and visualization. It receives MQTT messages, stores telemetry data, and displays modbus readings on customizable dashboards. - User Interface
End users can access the ThingsBoard web interface or mobile app to monitor live sensor data, configure alerts, and review historical measurements from anywhere with internet connectivity.
ThingsBoard Setup
To successfully visualize Modbus readings from the NORVI-GSM device, the ThingsBoard platform must be properly configured. This section outlines the key steps to set up ThingsBoard for device management and data visualization.
1. Create a ThingsBoard Account and Log In
- Register for a ThingsBoard cloud account at https://thingsboard.io or install a local ThingsBoard server.
- Log in to your ThingsBoard dashboard using your credentials.
2. Add a New Device
- Navigate to Devices and click + Add new device.
- Provide a meaningful name for your NORVI-GSM device (e.g., “NORVI GSM – Modbus RS485”).
- Save the device; this generates a unique Access Token used for MQTT authentication.

3. Configure Device Credentials
- Open the newly created device and go to the Manage credentials tab.
- Copy the Access Token; this token will be used in the ESP32 MQTT client to authenticate the device.

4. Set Up Telemetry Data
- ThingsBoard automatically accepts telemetry data sent via MQTT.
- Your device will publish modbus readings as telemetry using JSON format (e.g., {“A0”: 3.3}).
- No additional configuration is needed for telemetry reception.

5. Create a Dashboard for Visualization
- Go to the Dashboards section and click + Add new dashboard.
- Design a dashboard by adding widgets such as charts, gauges, or numeric displays.
- Link widgets to the NORVI-GSM device telemetry keys (e.g., A0) to visualize real-time Modbus readings.



6. Optional: Set Alerts and Rules
- Configure rules to trigger alerts or actions based on input thresholds or device status.
- Use ThingsBoard’s rule engine to automate notifications or device commands.
With ThingsBoard configured, the NORVI-GSM device can securely send Modbus data over MQTT, enabling real-time monitoring and analysis through intuitive dashboards.
Software Setup
A. Install the ESP32 board in Arduino IDE:
- Go to File > Preferences.
- Go to Tools > Boards > Boards Manager, search for “ESP32”, and install.
- NORVI-with-ThingsBoard
B. Install Required Libraries (Arduino):
Go to Sketch > Include Library > Manage Libraries and install:
- ModbusMaster.h
C. Configure the code:
- Open the code provided in your Arduino IDE.
- Replace the GPRS credentials (apn) with your SIM card provider details.
- Replace device token of your ThingsBoard device.
Code Explanation
1. Library Inclusions and Definitions
#include <Arduino.h>
#include <ModbusMaster.h>
#define SerialMon Serial
#define SerialAT Serial1
#define MODEM_TX 32
#define MODEM_RX 33
#define GSM_RESET 21
#define UART_BAUD 115200
#define RXD 25
#define TXD 26
#define FC 22
· ModbusMaster.h – Reads data from Modbus RTU devices via RS485.
· SerialMon – USB serial for debugging.
· SerialAT – UART connected to Quectel EC25 (AT command interface).
· MODEM_TX / MODEM_RX – ESP32 pins wired to EC25 UART.
· GSM_RESET – hardware reset control for the modem.
· RXD / TXD – Serial pins for RS485 communication.
· FC – Flow control pin for enabling/disabling RS485 driver.
2. GPRS and ThingsBoard Credentials
const char apn[] = "dialogbb";
const char* mqttServer = "mqtt.thingsboard.cloud";
const int mqttPort = 1883;
const char* accessToken = "EEqkBEnWAU9o41FFW3Mb";
· apn
must match the SIM operator.
· ThingsBoard server details, port (default MQTT port 1883), and the device token authenticate the device on ThingsBoard.
3. Modbus RS485 Setup
ModbusMaster node;
void preTransmission() { digitalWrite(FC, 1); }
void postTransmission() { digitalWrite(FC, 0); }
· ModbusMaster node – Modbus RTU communication object.
· preTransmission/postTransmission – Control RS485 transceiver direction (send/receive).
4. Initilizing the modem and connect to network
void InitModem() {
delay(3000);
gsm_send_serial("AT+CFUN=1", 2000);
gsm_send_serial("AT+CPIN?", 1000);
gsm_send_serial("AT+CSQ", 1000);
gsm_send_serial("AT+CREG?", 1000);
gsm_send_serial("AT+CGATT?", 1000);
gsm_send_serial("AT+COPS?", 1000);
gsm_send_serial("AT+CPSI?", 1000);
gsm_send_serial("AT+QIFGCNT=0", 500);
gsm_send_serial("AT+QICSGP=1,1,\"" + String(apn) + "\",\"\",\"\",1", 1000);
}
void connectToGPRS() {
gsm_send_serial("AT+QIDEACT=1", 1000);
gsm_send_serial("AT+QIACT=1", 2000);
gsm_send_serial("AT+QIACT?", 1000);
}
void connectToMQTT() {
gsm_send_serial("AT+QMTCFG=\"recv/mode\",0,0,1", 1000);
gsm_send_serial("AT+QMTOPEN=0,\"" + String(mqttServer) + "\"," + String(mqttPort), 3000);
delay(3000); // Wait for +QMTOPEN: 0,0
gsm_send_serial("AT+QMTCONN=0,\"esp32client\",\"" + String(accessToken) + "\"", 3000);
}
· InitModem() issues basic AT checks and sets PDP settings (AT+QICSGP) with the APN.
· connectToGPRS() deactivates/activates data session (AT+QIDEACT, AT+QIACT) and checks status.
· connectToMQTT() configures MQTT receive mode, opens connection (AT+QMTOPEN) and then connects (AT+QMTCONN) using the client ID and ThingsBoard access token.
5. MQTT Publish Function
void publishMQTTMessage(String topic, String message) {
int msgLen = message.length();
String cmd = "AT+QMTPUBEX=0,0,0,0,\"" + topic + "\"," + String(msgLen);
gsm_send_serial(cmd, 1000);
gsm_send_serial(message + "\x1A", 3000); // End with Ctrl+Z
}
· Uses Quectel’s AT+QMTPUBEX to publish. The code sends the command with the message length, then the message terminated by Ctrl+Z (0x1A) to indicate end-of-data.
6. Sending Telemetry Data
void sendTelemetry() {
uint8_t value;
uint8_t IN1, IN2, IN3, IN4;
value = node.readHoldingRegisters(0x40001, 3);
IN1 = node.getResponseBuffer(0x00);
IN2 = node.getResponseBuffer(0x01);
IN3 = node.getResponseBuffer(0x02);
Serial.print("\n");
Serial.print(" ANIN1 : ");
Serial.print(IN1);
Serial.print(" ANIN2 : ");
Serial.print(IN2);
Serial.print(" ANIN3 : ");
Serial.print(IN3);
Serial.print("\n");
delay(500);
String payload = "{\"ANIN1\":";
payload += IN1;
payload += ", \"ANIN2\":";
payload += IN2;
payload += ", \"ANIN3\":";
payload += IN3;
payload += "}";
Serial.print("Publishing: ");
Serial.println(payload);
publishMQTTMessage("v1/devices/me/telemetry", payload);
}
· Reads 3 holding registers starting at address 0x40001.
· Extracts each register value (IN1, IN2, IN3)
· Constructs a JSON string payload with modbus readings (ANIN1 to ANIN3).
· Publishes telemetry data to ThingsBoard MQTT topic “v1/devices/me/telemetry”.
7. Setup Function
void setup() {
SerialMon.begin(115200);
delay(1000);
SerialAT.begin(UART_BAUD, SERIAL_8N1, MODEM_RX, MODEM_TX);
pinMode(GSM_RESET, OUTPUT);
digitalWrite(GSM_RESET, HIGH);
pinMode(FC, OUTPUT);
digitalWrite(FC, 0);
Serial2.begin(115200, SERIAL_8N1, RXD, TXD);
node.begin(1, Serial2); //Slave ID as 1
node.preTransmission(preTransmission);
delay(10);
node.postTransmission(postTransmission);
InitModem();
connectToGPRS();
connectToMQTT();
}
· Initializes serial communication for debug output, and modem UARTs, sets reset pin.
· Calls sequential functions to initialize the modem, bring up GPRS, then connect MQTT (via AT commands).
· Initializes RS485 pins, and Modbus communication.
8. Main Loop
void loop() {
if (millis() - lastSend > 10000) {
lastSend = millis();
sendTelemetry();
}
}
· Sends telemetry every 10 seconds.
Testing Setup
1.Upload the Firmware
Compile and upload the firmware to the ESP32 and check the serial monitor output and verify that device is connected to the GPRS and ThingsBoard MQTT.
2. Expected Outputs
- ESP32 logs network connection and update status.
- Check ThingsBoard dashboard to confirm it displays correct Modbus readings.


Common Issues & Fixes
Issue | Possible Cause | Solution |
No data appearing in ThingsBoard | APN is incorrect or not set | Verify apn[] in code matches your SIM provider’s APN |
GPRS session not active | Check AT response to AT+QIACT? — should show an active PDP context , Check SIM card inserted properly. | |
MQTT connection failed | Confirm mqttServer, mqttPort, and accessToken are correct | |
GPRS connects but MQTT fails | Invalid payload length | Ensure message length in AT+QMTPUBEX matches the JSON string size |
No Modbus readings on dashboard | Widget keys don’t match telemetry keys | Ensure ThingsBoard widget keys match JSON keys (ANIN1 , etc.) sent by device |
Data updates delayed or missing | Long send interval; GPRS signal dropouts | Reduce telemetry interval in code; check GPRS signal strength |
No Modbus data received | Wrong slave ID or register address in code | Verify the Modbus slave ID and register addresses with your device’s documentation and update node.begin() and readHoldingRegisters() accordingly. |
All values show 0 | RS485 wiring reversed (A/B swapped) | Swap the A/B lines between NORVI-GSM and Modbus device. |