Connect ESP32 with SIM7600 for MQTT with TLS via Node.js REST APIs
In this project, we’ll cover,
- Connect an ESP32 board to a SIM7600 4G module.
- Establish an internet connection over 4G using SIM7600.
- Publish data from the ESP32 (through the SIM7600) to an MQTT broker.
- Subscribe to topics and listen to data from the MQTT broker with ESP32.
- Integrate a Node.js backend application that,
- Provides REST APIs to send data to the ESP32 (through MQTT).
- Receives real-time published data from the ESP32 (through MQTT) and makes it available via REST APIs to any client (a web or mobile app).
Hardware and Software Requirements
Hardware
- ESP32 development board — 32 Pin
- SIM7600 module (SIM7600G-H)
- SIM card with active data plan (appropriate APN required).
- Optional — Power supply that can handle the SIM7600’s current requirements (peaks of up to 2A).
Software
- Arduino IDE or PlatformIO with the following libraries
- TinyGSM (for modem commands)
- SSLClient (for TLS on Arduino)
- PubSubClient (MQTT library)
- ArduinoJson (JSON handling)
Arduino IDE version 1.8.15 is used with esp32 board version 3.0.7 is used.
2. Node.js with
- Express.js for REST APIs
- MQTT.js for MQTT client
3. MQTT Broker with TLS support
- HiveMQ Cloud (Free tier)
Or Mosquitto with TLS, or any other broker supporting secure MQTT on port 8883. (For Encryption)
HiveMQ Cloud (TLS) Setup
If you haven’t set up a TLS-enabled MQTT broker yet, consider HiveMQ Cloud,
- Sign up at HiveMQ Cloud.
- Create a free cluster.
- In the Details tab, note down the following,
- Host (xxxxxx.s1.eu.hivemq.cloud)
- Port: Typically 8883 for TLS.
- Username and Password (which you create).
4. Getting the Root CA certificate,
Type the below command on the terminal,
openssl s_client -showcerts -connect your-broker-hostname:8883
Add your broker hostname on the command, then you will get the certificates as a response to the command.
The CA certificate is needed for the ESP32 to verify the broker’s authenticity over TLS.
Copy the Root CA certificate.
Circuit Wiring
Connect the ESP32 and SIM7600 as follows,
Arduino Code
Below is a sample Arduino sketch using TinyGSM + SSLClient + PubSubClient to,
- Initialize the SIM7600.
- Connect to the 4G network using APN.
- Establish a TLS connection to the MQTT broker.
- Subscribe to a command topic to receive instructions from the Node.js app.
- Publish sensor data to a data topic.
We’ll create two custom topics,
- “myProject/esp32/secureData” for data publishing from ESP32.
- “myProject/esp32/secureCommands” for receiving commands to ESP32.
ca_cert.h
// ca_cert.h
// Example Root CA (from HiveMQ Cloud). Replace with your broker's root CA certificate.
const char root_ca[] PROGMEM =
"-----BEGIN CERTIFICATE-----\n"
"MIIFBjCCAuvdhsaanhgdhabjdgbbchgabvxhgnsbdhnshjhanoZIhvcNAQELBQAw\n"
"TzELMAkGA1UEBhMCVVMxbshabhabhavhsgabsnshdgbsghxbshhsdXR5IFJlc2Vh\n"
"cmNoIEdyb3Vhabdhsabah dyajjaU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw\n"
"WhcNMjcwhagdhvaxbhabxvahxbhagdbzgsnvhavhgsgaggxbhxbhaakNTGV0J3Mg\n"
.
.
.
.
"-----END CERTIFICATE-----";
Make sure your actual root CA is correctly copied and the ca_cert.h should be in the same directory as per the .ino file.
Main Sketch
//Example: ESP32 + SIM7600 + 4G + MQTT + TLS + Node.js
#define TINY_GSM_MODEM_SIM7600
#include <TinyGsmClient.h>
#include <SSLClient.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include "ca_cert.h" // Contains root_ca from HiveMQ or your broker
#define SerialMon Serial
#define SerialAT Serial1
// SIM7600 Pins and Config
#define MODEM_UART_BAUD 115200
#define MODEM_TX 17
#define MODEM_RX 16
#define MODEM_PWRKEY 4
// GPRS Credentials
const char apn[] = "your-apn-here"; // Replace with your network APN eg: "Dialog 4G"
const char gprsUser[] = "";
const char gprsPass[] = "";
const char simPIN[] = ""; // If your SIM requires a PIN
// MQTT Broker (TLS)
const char* mqttServer = "xxxxxx.s1.eu.hivemq.cloud"; // Your broker host
const int mqttPort = 8883; // TLS port
const char* mqttUser = "yourHiveMQUser"; // Replace with your HiveMQ Username
const char* mqttPassword = "yourHiveMQPass"; // Replace with your HiveMQ Password
// MQTT Topics
const char* topicData = "myProject/esp32/secureData";
const char* topicCommands = "myProject/esp32/secureCommands";
// TinyGSM & SSL
TinyGsm modem(SerialAT);
TinyGsmClient gsmClient(modem);
SSLClient sslClient(&gsmClient); // SSLClient sits on top of gsmClient
PubSubClient mqttClient(sslClient);
void mqttCallback(char* topic, byte* payload, unsigned int length);
void setupModem();
void connectMQTT();
void publishData(const char* message);
void setup() {
SerialMon.begin(115200);
delay(3000);
SerialMon.println("Starting ESP32 + SIM7600 MQTT (TLS) Example...");
// Begin Serial for Modem
SerialAT.begin(MODEM_UART_BAUD, SERIAL_8N1, MODEM_RX, MODEM_TX);
// Power on the SIM7600
pinMode(MODEM_PWRKEY, OUTPUT);
digitalWrite(MODEM_PWRKEY, LOW);
delay(1000);
digitalWrite(MODEM_PWRKEY, HIGH);
delay(5000);
// Initialize the modem
setupModem();
// Set the CA certificate for TLS
sslClient.setCACert(root_ca);
// Configure MQTT client
mqttClient.setServer(mqttServer, mqttPort);
mqttClient.setCallback(mqttCallback);
// Connect to MQTT
connectMQTT();
}
void loop() {
if (!mqttClient.connected()) {
connectMQTT();
}
mqttClient.loop();
// Example: publish random sensor data every 5s
static unsigned long lastPublish = 0;
if (millis() - lastPublish > 5000) {
lastPublish = millis();
StaticJsonDocument<128> doc;
doc["randomValue"] = random(0, 100);
char payload[128];
serializeJson(doc, payload);
publishData(payload);
}
}
void setupModem() {
SerialMon.println("Initializing modem...");
if (!modem.init()) {
SerialMon.println("Modem init failed!");
while (true) { delay(1000); }
}
// If your SIM requires a PIN:
if (strlen(simPIN) && modem.getSimStatus() != 3) {
modem.simUnlock(simPIN);
}
// Wait for network
SerialMon.print("Waiting for network...");
if (!modem.waitForNetwork()) {
SerialMon.println(" fail");
while (true) { delay(1000); }
}
SerialMon.println(" success!");
// Connect GPRS
SerialMon.print("Connecting to APN: ");
SerialMon.println(apn);
if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
SerialMon.println("GPRS connect failed!");
while (true) { delay(1000); }
}
SerialMon.println("GPRS connected!");
}
void connectMQTT() {
while (!mqttClient.connected()) {
SerialMon.print("Attempting MQTT(TLS) connection...");
String clientId = "ESP32ClientTLS-" + String(random(0xffff), HEX);
// Connect with user & pass if required
if (mqttClient.connect(clientId.c_str(), mqttUser, mqttPassword)) {
SerialMon.println("connected!");
mqttClient.subscribe(topicCommands);
} else {
SerialMon.print("failed, rc=");
SerialMon.println(mqttClient.state());
SerialMon.println("Retry in 5 seconds...");
delay(5000);
}
}
}
void mqttCallback(char* topic, byte* payload, unsigned int length) {
SerialMon.print("Message arrived [");
SerialMon.print(topic);
SerialMon.print("]: ");
String msg;
for (unsigned int i = 0; i < length; i++) {
msg += (char)payload[i];
}
SerialMon.println(msg);
// Parse JSON (if applicable)
StaticJsonDocument<128> doc;
DeserializationError err = deserializeJson(doc, msg);
if (err) {
SerialMon.println("JSON parse error!");
return;
}
// Example: {"action":"toggle", "value":1}
const char* action = doc["action"];
if (action) {
// Implement your device-specific logic
if (strcmp(action, "toggle") == 0) {
int value = doc["value"] | 0;
SerialMon.print("Toggling something with value: ");
SerialMon.println(value);
}
}
}
void publishData(const char* message) {
if (mqttClient.publish(topicData, message)) {
SerialMon.println("Data published securely!");
} else {
SerialMon.println("Publish failed!");
}
}
Key TLS steps,
- SSLClient is initialized with TinyGsmClient.
- “sslClient.setCACert(root_ca)” sets the root CA to validate the broker’s TLS certificate.
- Port is set to 8883, the standard TLS port for MQTT.
Note: You need to replace the hostname, APN, username, and password of the MQTT broker before uploading the code.
On the serial monitor, you will see,
Node.js Backend
- Your Node.js app can also connect to the same MQTT broker over TLS.
- The main difference from a non-TLS setup is specifying the “mqtts://”protocol and, optionally, can provide CA certificates if you need client-side verification.
- However, many brokers (Including HiveMQ Cloud) only require username/password + secure port, without necessarily needing the CA file on the Node.js side (TLS handshake is done by Node).
Project Setup
- Create a project folder, “esp32-sim7600-node-tls”.
- Navigate to that folder through the terminal, and run the following commands,
npm init -y
npm install express mqtt body-parser
3. Create a new file “server.js”, copy, and paste the following code.
Node.js Code
//Node.js + MQTT (TLS) + REST API Example
const express = require('express');
const mqtt = require('mqtt');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
// --- MQTT Configuration (TLS) ---
// Example for HiveMQ Cloud
// If your broker requires a CA file on Node side, you can load it via "ca: fs.readFileSync('rootCA.crt')"
// For many cloud brokers, simply specifying 'mqtts://' + username/password is enough.
const MQTT_BROKER_URL = 'mqtts://xxxxxx.s1.eu.hivemq.cloud:8883';
const MQTT_USERNAME = 'yourHiveMQUser';
const MQTT_PASSWORD = 'yourHiveMQPass';
// Topics
const TOPIC_DATA = 'myProject/esp32/secureData';
const TOPIC_COMMANDS = 'myProject/esp32/secureCommands';
// Connect Options
const options = {
username: MQTT_USERNAME,
password: MQTT_PASSWORD,
// ca: fs.readFileSync('rootCA.crt'), // If needed
// rejectUnauthorized: false // If you want to disable cert validation (not recommended!)
};
const client = mqtt.connect(MQTT_BROKER_URL, options);
let latestData = {};
// Handle MQTT connection
client.on('connect', () => {
console.log('Node.js connected to secure MQTT broker!');
// Subscribe to data topic
client.subscribe(TOPIC_DATA, (err) => {
if (err) {
console.error('Subscribe error:', err);
} else {
console.log(`Subscribed to ${TOPIC_DATA}`);
}
});
});
// Handle incoming MQTT messages
client.on('message', (topic, message) => {
const payload = message.toString();
console.log(`Received on ${topic}: ${payload}`);
if (topic === TOPIC_DATA) {
try {
latestData = JSON.parse(payload);
} catch (e) {
console.error('Error parsing JSON:', e);
}
}
});
//REST Endpoints
// 1) Get latest data
app.get('/api/data', (req, res) => {
res.json({
success: true,
data: latestData
});
});
// 2) Send command
// Example: { "action": "toggle", "value": 1 }
app.post('/api/commands', (req, res) => {
const command = req.body;
client.publish(TOPIC_COMMANDS, JSON.stringify(command), (err) => {
if (err) {
console.error('Publish error:', err);
return res.status(500).json({ success: false, message: 'MQTT publish error' });
}
res.json({ success: true, message: 'Command published securely!' });
});
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Express server listening on port ${PORT}`);
});
Key TLS points:
- “mqtts://” is used in the broker URL.
- If needed: If the broker requires explicit CA verification by Node.js, you can specify “ca: fs.readFileSync(‘rootCA.crt’)” in the “options”.
Username/Password are set for HiveMQ Cloud.
Note: You need to replace the hostname, username, and password of the MQTT broker before uploading the code.
Testing the Flow
1. Upload the Arduino firmware to your ESP32. Ensure the SIM7600 and SIM card are ready (APN).
2. In the Arduino Serial Monitor (115200 baud), look for,
- Successful modem init
- GPRS connected
- MQTT(TLS) connected
3. Run the Node.js server, by running the following commad on the terminal,
node server.js
4. You should see logs indicating Node.js connected to the broker:
Node.js connected to secure MQTT broker!
Subscribed to myProject/esp32/secureData
5. Wait for ESP32 to publish random data. The Node.js console should display:
Received on myProject/esp32/secureData: {"randomValue":42}
6. Send a command from Node.js to the ESP32:
- Using POST request to “http://localhost:3000/api/commands” with JSON body:
{
"action": "toggle",
"value": 1
}
- You should see the ESP32’s Serial Monitor log:
Message arrived [myProject/esp32/secureCommands]: {"action":"toggle","value":1}
Toggling something with value: 1
7. Check the latest data on Node.js:
- GET “http://localhost:3000/api/data”
- Should return something like:
{
"success": true,
"data": {
"randomValue": 42
}
}
Conclusion
You now have a secure, TLS-encrypted connection between your ESP32 (with SIM7600 4G) and an MQTT broker, as well as a Node.js backend that enables bidirectional data flow:
- ESP32 publishes data and receives commands securely over MQTT.
- Node.js listens for the ESP32 data and provides REST endpoints for external clients to query data or send commands back to the ESP32.
You can use WebSockets to reduce the latency even more.
If you have questions or need assistance, contact support at info@protonest.co.
Ready to start your IoT journey? Visit Protonest’s IoT System Design Tool which will guide you through the complete process of IoT system development.
https://iot-system-design-tool.protonest.co/
Contact us for any consultations or projects related to IoT and embedded systems.
Email: info@protonest.co
Protonest for more details.
Protonest specializes in transforming IoT ideas into reality. We offer prototyping services from concept to completion. Our commitment ensures that your visionary IoT concepts become tangible, innovative, and advanced prototypes.
For more details: https://www.protonest.co/
Cheers!