Introduction: MQTT With QUECTEL EC25 Guide on NORVI GSM Series

About: ESP32 & STM32 based Industrial Controllers

NORVI devices are industrial controllers optimized for IoT applications. When paired with the Quectel EC25 LTE module, they can securely connect to cloud platforms using the MQTT protocol. This guide illustrates how to set up NORVI devices with the EC25 module to connect to an MQTT broker via AT commands. The focus is on the setup process, code details, and key AT commands necessary for implementation.

Click here to view the web guide

NORVI GSM Series

Supplies

Key features of NORVI devices with the Quectel EC25 module

  1. Industrial-grade design: Suitable for harsh environments.
  2. Versatile I/O options: Digital and analog inputs/outputs for various sensors and actuators.
  3. Cellular connectivity: LTE support via the Quectel EC25 module for remote monitoring and control.


Prerequisites

Before you begin, ensure you have the following,

  1. NORVI Device with an integrated Quectel EC25 module
  2. Registered Sim card
  3. MQTT broker details (hostname, port, username, password)
  4. MQTT.fx software for testing and monitoring
  5. Arduino IDE and necessary libraries for programming the NORVI device.
  6. Data visualization platform (e.g., DATACAKE)


Required Libraries

  1. Arduino.h
  2. Wire.h
  3. WiFi.h
  4. ArduinoJson.h


Sensitive Information Handling

  1. Secret.h: This file should contain the MQTT username and password, which should be kept secure.


Hardware Setup

1. Pin Connections

  1. MODEM_TX (TX Pin)
  2. MODEM_RX (RX Pin)
  3. GSM_RESET (Reset Pin)
  4. Digital Input Pins (D0, D1, D2, D3)
  5. Relay Output Pin (R0)

Step 1: Setup NORVI GSM Device and Quectel EC25

  1. Connect the sim card to EC25 module securely for the NORVI GSM device.
  2. 2. Power on your device.
  3. 3. Make sure you have the GPRS credentials from your provider:
  4. APN, User, and Password (for GPRS connection).


Step 2: : Clone the Example Firmware Repository

  1. Clone the repository that contains the firmware for EC25 and MQTT communication: click here to get the code
  2. Open the project in your preferred IDE (e.g., Arduino IDE).

Step 3: Configure the Code for Your Setup

This Arduino code establishes a connection between a NORVI device and an MQTT broker using a Quectel EC25 LTE module. It initializes the GSM modem, connects to GPRS, and subscribes to an MQTT topic. The device sends digital input states as JSON payloads to the broker and handles incoming messages, decoding them as necessary. The code also includes functions for sending AT commands to the modem, managing MQTT connections, and monitoring network status to ensure reliable communication.



gistfile EC25 MQTT.txt

This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Show hidden characters
#include
#include
#include // Include the WiFi library for MAC address
#include
#include "Secrets.h" // Include the file to get the username and password of MQTT server
String gsm_send_serial(String command, int delay);
//#define TINY_GSM_MODEM_SIM7600
#define SerialMon Serial
#define SerialAT Serial1
#define GSM_PIN ""
// Your GPRS credentials, if any
const char apn[] = "dialogbb";
const char gprsUser[] = "";
const char gprsPass[] = "";
// MQTT details
String broker = "mqtt.sensoper.net";
String MQTTport = "1881";
#define uS_TO_S_FACTOR 1000000ULL // Conversion factor for micro seconds to seconds
#define TIME_TO_SLEEP 60 // Time ESP32 will go to sleep (in seconds)
#define UART_BAUD 115200
#define MODEM_TX 32
#define MODEM_RX 33
#define GSM_RESET 21
#define D0 39
#define D1 34
#define D2 35
#define D3 14
#define R0 12
#define MAC_ADDRESS_SIZE 18 // Assuming MAC address is in format "XX:XX:XX:XX:XX:XX"
byte mac[6];
String str_macAddress;
unsigned long prevMillis = 0;
const unsigned long interval = 60000; // Interval for sending messages
// Device-specific details
const char* deviceSerial = "3CE90E6C8F88"; // Replace with your device serial
void mqttCallback(char* topic, byte* payload, unsigned int len) {
SerialMon.print("Message arrived [");
SerialMon.print(topic);
SerialMon.print("]: ");
SerialMon.write(payload, len);
SerialMon.println();
// Extract serial number from the topic
String topicStr = String(topic);
int firstSlash = topicStr.indexOf('/');
int lastSlash = topicStr.lastIndexOf('/');
String MAC_ID = topicStr.substring(firstSlash + 1, lastSlash);
SerialMon.print("MAC ID: ");
SerialMon.println(MAC_ID);
if (MAC_ID == deviceSerial) {
// Decode the received message
StaticJsonDocument<200> doc;
DeserializationError error = deserializeJson(doc, payload, len);
if (error) {
SerialMon.print("deserializeJson() failed: ");
SerialMon.println(error.c_str());
return;
}
// Extract the payload
bool state = doc["state"];
SerialMon.println(response);
// Check if the response contains "+QMTRECV:"
int startPos = response.indexOf("+QMTRECV:");
if (startPos != -1) {
// Extract the part of the response containing the message
String messagePart = response.substring(startPos);
// Print the extracted message part for debugging
SerialMon.print("Extracted Message Part: ");
SerialMon.println(messagePart);
// Remove any extraneous text before "+QMTRECV:"
messagePart.trim();
// Check if the response is in the expected format
if (messagePart.startsWith("+QMTRECV:")) {
// Extract the part after "+QMTRECV:" (skip the "+QMTRECV:" prefix)
messagePart = messagePart.substring(messagePart.indexOf(':') + 1);
// Extract client_idx and msg_id
int firstComma = messagePart.indexOf(',');
int secondComma = messagePart.indexOf(',', firstComma + 1);
String client_idx = messagePart.substring(0, firstComma);
String msg_id = messagePart.substring(firstComma + 1, secondComma);
// Extract topic
int firstQuote = messagePart.indexOf('"', secondComma + 1);
int secondQuote = messagePart.indexOf('"', firstQuote + 1);
String topic = messagePart.substring(firstQuote + 1, secondQuote);
// Extract payload length
int thirdComma = messagePart.indexOf(',', secondQuote + 1);
int fourthComma = messagePart.indexOf(',', thirdComma + 1);
String payloadLengthStr = messagePart.substring(thirdComma + 1, fourthComma);
int payloadLength = payloadLengthStr.toInt();
// Extract payload
int thirdQuote = messagePart.indexOf('"', fourthComma + 1);
int fourthQuote = messagePart.indexOf('}', thirdQuote + 1);
int fifthQuote = messagePart.indexOf('"', fourthQuote + 1);
String payload = messagePart.substring(thirdQuote + 1, fifthQuote );
// Debug print
SerialMon.print("Received Topic: ");
SerialMon.println(topic);
SerialMon.print("Received Payload: ");
SerialMon.println(payload);
SerialMon.print("Payload Length: ");
SerialMon.println(payloadLength);
// Convert topic and payload to mutable char arrays
char topicArr[topic.length() + 1];
byte payloadArr[payload.length() + 1];
topic.toCharArray(topicArr, topic.length() + 1);
for (int i = 0; i < payload.length(); ++i) {
payloadArr[i] = (byte)payload[i];
}
payloadArr[payload.length()] = '\0'; // Null-terminate byte array
// Call the MQTT callback function with the extracted values
mqttCallback(topicArr, payloadArr, payload.length());
} else {
SerialMon.println("Unexpected response format.");
}
} else {
SerialMon.println("No new MQTT messages or unexpected response format.");
}
}
void Init(void) { // Connecting with the network and GPRS
delay(5000);
gsm_send_serial("AT+CFUN=1", 10000);
gsm_send_serial("AT+CPIN?", 10000);
gsm_send_serial("AT+CSQ", 1000);
gsm_send_serial("AT+CREG?", 1000);
gsm_send_serial("AT+COPS?", 1000);
gsm_send_serial("AT+CGATT?", 1000);
gsm_send_serial("AT+CPSI?", 500);
gsm_send_serial("AT+CGDCONT=1,\"IP\",\"dialogbb\"", 1000);
gsm_send_serial("AT+CGACT=1,1", 1000);
gsm_send_serial("AT+CGATT?", 1000);
gsm_send_serial("AT+CGPADDR=1", 500);
}
void connectToGPRS(void) {
gsm_send_serial("AT+CGATT=1", 1000);
gsm_send_serial("AT+CGDCONT=1,\"IP\",\"dialogbb\"", 1000);
gsm_send_serial("AT+CGACT=1,1", 1000);
gsm_send_serial("AT+CGPADDR=1", 500);
}
void connectToMQTT(void) {
// Initialize MQTT configurations
gsm_send_serial("AT+QMTCFG=\"recv/mode\",0,0,1", 1000);
gsm_send_serial("AT+QMTOPEN=0,\"mqtt.sensoper.net\",1881", 1000);
delay(2000); // Wait for the connection to establish
String command = "AT+QMTCONN=0,\"EC25 client\",\"" + username + "\",\"" + password + "\"" ;
gsm_send_serial(command, 1000);
delay(2000); // Wait for the connection to establish
// Subscribe to the downlink topic
String downlinkTopic = "NORVI/+/OUTPUT";
String subscribeCommand = "AT+QMTSUB=0,1,\"" + downlinkTopic + "\",0"; // QoS 1
gsm_send_serial(subscribeCommand, 1000);
delay(2000); // Allow time for subscription confirmation
// Check for subscription confirmation
String response = gsm_send_serial("AT+QMTSUB?", 1000); // Check subscription status
SerialMon.print("Subscription Response: ");
SerialMon.println(response);
// Debug: Print MQTT connection status
String connStatus = gsm_send_serial("AT+QMTCONN?", 1000);
SerialMon.print("MQTT Connection Status: ");
SerialMon.println(connStatus);
}
bool isNetworkConnected() {
String response = gsm_send_serial("AT+CREG?", 3000);
return (response.indexOf("+CREG: 0,1") != -1 || response.indexOf("+CREG: 0,5") != -1);
}
bool isGPRSConnected() {
String response = gsm_send_serial("AT+CGATT?", 3000);
return (response.indexOf("+CGATT: 1") != -1);
}
String gsm_send_serial(String command, int timeout) {
String buff_resp = "";
Serial.println("Send ->: " + command);
SerialAT.println(command);
unsigned long startMillis = millis();
while (millis() - startMillis < timeout) {
while (SerialAT.available()) {
char c = SerialAT.read();
buff_resp += c;
}
delay(10); // Small delay to allow for incoming data to accumulate
}
Serial.println("Response ->: " + buff_resp);
return buff_resp;
}

Step 4: You Will Need to Configure the Following

  1. GPRS Credentials: Enter your APN and GPRS user/pass in the code.
const char apn[] = "your_apn";
const char gprsUser[] = "";
const char gprsPass[] = "";
  1. MQTT Broker Details: Input the Datacake MQTT broker, port, and credentials from your Datacake account.


String broker = "mqtt.datacake.co";
String MQTTport = "8883";

Step 5: Connect the Device to MQTT

Once the firmware is uploaded, the Quectel EC25 will try to connect to your GPRS network and MQTT broker. The device's status and digital inputs will be published to the Datacake MQTT topic.

  1. Check the serial monitor for feedback on GPRS and MQTT connection.
  2. If the device is connected, it will subscribe to the downlink topic for receiving messages and will start publishing sensor data

Step 6: Verify the Connection on Datacake

  1. Log in to your Datacake account.
  2. Go to the Devices section to see if your NORVI GSM device is online and receiving data from the EC25 module.
  3. You can now create dashboards and trigger actions based on the MQTT messages sent by the device.

Step 7: Configure the MQTT Broker

  1. Download MQTT.fx client and install it.
  2. Open the MQTT.fx and click the Settings icon.
  3. Click + to create a profile.
  4. Enter the Connection Profile and General information.

Step 8: Enter the User Credential Information

  1. After completing the above steps, click Apply > OK to save.
  2. Then, select the name of the file just created in the profile box and click Connect.
  3. If the round icon in the top-right corner is green, the connection to IoT Hub is successful, and publishing and subscribing operations can be performed.

Step 9: Connected to the Broker

Step 10: Subscribe to the MQTT Topic

  1. Select the Subscribe tab in the client, scan and select the topic name, and click Subscribe to subscribe to the topic

Step 11: MQTT Subscriber Dashboard

  1. The subscribing result can be seen in the bottom right corner.

Step 12: Integration With Data Visualization Platform

  1. Access the DATACAKE from this link and navigate the Datacake dashboard. Select the “Add Devices”.(Datacake dashboard)

Step 13: For the MQTT Integration Select the API.

  1. This will add the device to the Datacake account.

Step 14: Add MQTT Broker Configuration.

  1. 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.

Step 15: MQTT Broker Connected Successfully

Step 16: Decoder Function to Verify Functionality

  1. To create a first database field, click on the “Add Field”. This will open another modal asking for some required input for the fields.
  2. 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.



Step 17: Added Fields

  1. If the device is already sending data, an indication of incoming data can be seen in the “Last data” column.

Step 18: Data Logs

  1. 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.

Step 19: MQTT Device Dashboard View in Datacake