Introduction: Secure MQTT With QUECTEL EC25 Guide on ESP32 Based NORVI GSM Series

About: ESP32 & STM32 based Industrial Controllers

NORVI devices are durable industrial controllers built for IoT applications. By integrating the Quectel EC25 module with SSL configurations, these devices can utilize cellular connectivity to securely communicate with cloud platforms via the MQTT protocol.

This guide offers detailed instructions for using NORVI GSM series devices alongside the Quectel EC25 module to establish a secure SSL connection with an MQTT broker using AT commands. The Quectel EC25, a widely used LTE module in IoT, enables cellular communication. The guide covers the required setup, code details, and key AT commands.

Click here to view the web guide

Supplies

MQTT Protocol -Click here

SSL MQTT Communication


SSL (Secure Sockets Layer) is a widely used security protocol for creating an encrypted connection between a client and a server. In MQTT communication, SSL ensures that the data transmitted between the device and the MQTT broker is encrypted, safeguarding confidentiality and maintaining data integrity.


Configuring SSL on NORVI GSM series EC25 Devices #


To establish an SSL-encrypted MQTT connection with the NORVI GSM series EC25 device, follow these steps,

  1. Ensure the SIM card is correctly inserted into the NORVI GSM series EC25 device.
  2. Connect the device to a power source and power it on.
  3. Open the serial monitor on your development environment to interact with the device.
  4. Configure the Quectel EC25 module to use SSL for MQTT communication. This involves sending specific AT commands to the module.
  5. Ensure your device and broker certificates are up-to-date to maintain a secure connection.

NORVI GSM series

Setting Up Norvi EC25 to MQTT SSL


Key Features

  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.
  4. SSL/TLS security: Secure data transmission using SSL/TLS over MQTT.

Prerequisites

  1. NORVI GSM series Device with an integrated Quectel EC25 module
  2. MQTT broker details (hostname, port, username, password)
  3. MQTT.fx software for testing and monitoring
  4. Arduino IDE and necessary libraries for programming the NORVI GSM series device.
  5. Data visualization platform (e.g., DATACAKE)
  6. SSL Certificates: ( CA certificate, client certificate, and client key).


Libraries

  1. Arduino.h
  2. Wire.h
  3. WiFi.h
  4. ArduinoJson.h
  5. Datacake.h
  6. Secret.h: This file should contain the MQTT username and password,


SSL Certificate Setup to establish an SSL-encrypted connection, include the SSL certificates in the code.

  1. Root CA Certificate: The certificate authority that issued the broker’s certificate.
  2. Client Certificate: Your device’s certificate.
  3. Client Key: The private key corresponding to the client certificate.


Hardware setup

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: Test Program

Click here to get the code github - EC25_Connecting_to_Datacake_with_MQTT_Broker_TLS_secured

This code is designed for an ESP32-powered NORVI GSM series device connected to a Quectel EC25 module over UART. It establishes a secure MQTT connection with SSL, subscribes to a topic for controlling a relay output, and regularly publishes the status of multiple digital inputs to an MQTT broker. Additionally, the code manages automatic reconnections to both the network and MQTT broker if either connection is interrupted.

Click here for understand the test program 


AT Commands

Once the code is uploaded to the NORVI GSM series device, it’s essential to confirm that the SIM card is properly registered and that the device can communicate with the network. This can be verified by monitoring the serial output for responses to specific AT commands.

Essential AT Commands


gistfile EC25 MQTT (SSL).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 "Secret.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 = "mqtt2.sensoper.net";
String MQTTport = "8883";
#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"];
// 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+QMTCFG=\"SSL\",0,1,2", 1000);
gsm_send_serial("AT+QMTOPEN=0,\"mqtt2.sensoper.net\",8883", 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;
}
view raw gistfile EC25 MQTT (SSL).txt hosted with ❤ by GitHub

Step 2: Steps to Configure in Mqtt.fx

Check this link for detailed instructions on how to configure the MQTT broker and the Subscriber.

  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 3: Enter the User Credentials Information

Step 4: SSL/TLS

  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.
  4. Select the Subscribe tab in the client, scan and select the topic name, and click Subscribe to subscribe to the topic.
  5. The subscribing result can be seen in the bottom right corner.


Step 5: Subscribing Result

Step 6: Integration With Data Visualization Platform


NORVI devices can be connected to an IoT platform, enabling users to view and analyze data gathered by the devices. In this case, the platform used is Datacake. Below is a guide on how to integrate NORVI devices with Datacake.Click here

Step 7: Access the DATACAKE

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


Step 8: For the MQTT Integration Select the API

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

Step 9: Add API Device

Step 10: Fill in the Server Details and Add.

  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 11: Update MQTT Broker

Step 12: MQTT Broker Connected Successfully