ESP32 4G IoT: Send Data to MySQL using SIM A7670G & MQTT

Take your IoT projects off the grid. Learn how to transmit real-time sensor data over 4G LTE using the ESP32 and SIM A7670G, and store it permanently in your own MySQL database using a secure MQTT pipeline. This guide covers everything from hardware wiring to the Python backend script.

ESP32 4G IoT: Send Data to MySQL using SIM A7670G & MQTT

In this guide, we are building the "Holy Grail" of IoT projects: a complete data pipeline that works anywhere in the world.

We will read Temperature & Humidity data from a DHT11 sensor, transmit it over the 4G LTE network using the SIM A7670G, and store it permanently in a MySQL Database via a custom Python script.

πŸš€ The Architecture

  1. Device: ESP32 + SIM A7670G reads sensor data.
  2. Transport: Sends JSON data to an MQTT Broker (4G LTE).
  3. Backend: A Python script listens to the MQTT topic.
  4. Storage: Python saves the decoded data into MySQL.

This is not just a toy example; this is the exact architecture used in professional vehicle tracking, industrial monitoring, and smart agriculture systems.

Video Tutorial

If you prefer to watch the step-by-step build, check out the full video tutorial on the IoTBhai YouTube channel here:

Prerequisites: The Hardware Checklist

Before we begin, ensure you have the following components ready:

πŸ› οΈ Step 1: Hardware Setup

The wiring remains the same as Part 1. If you have not wired your module yet, or if you are unsure about the power requirements, please refer to our detailed setup guide here:

πŸ‘‰Read Part 1: Connect SIM A7670G with ESP32 - Hardware & Wiring Guide

⚠️ Important: Ensure your SIM module is powered by an external 5V 2A power source, not the ESP32.

πŸ› οΈ Step 2: Software Setup

❓ Why Do We Need a VPS?

You might wonder: "Can't I just run this on my laptop?"

Here is why a Cloud Server (VPS) is mandatory for 4G IoT:

  1. The "No Public IP" Problem: Mobile networks use firewalls (CGNAT) that block incoming connections. You cannot "call" your ESP32 directly. A VPS provides a Static IP, acting as a permanent meeting point for your devices.
  2. 24/7 Reliability: Laptops sleep and lose Wi-Fi. A VPS runs 24/7/365 in a datacenter, ensuring you never miss a single data packet.
  3. Data Privacy: Public brokers (like HiveMQ) expose your data to everyone. A VPS allows you to run a private, password-protected broker that only you can access.
FeatureLocal PC / Public BrokerYour Own VPS
ConnectionBlocked by Mobile NetworkAlways Accessible (Static IP)
UptimeLow (Sleep/Reboots)99.9% Always On
SecurityZero (Public)Encrypted & Private

πŸ—„οΈ Step 3: Database Setup

Now, we need a place to store our data. We will use MySQL, the world's most popular open-source database.

If you don't have MySQL installed on your Linux VPS yet, I have a step-by-step security guide for that too: πŸ‘‰Read Guide: How to Install MySQL on Linux VPS & Secure Remote Access

Create the Table

Once your MySQL server is running, log in (via SSH or Workbench) and run these SQL commands to create the database and table for this project:

CREATE DATABASE weather;

USE weather;

CREATE TABLE weather_data (
    id INT AUTO_INCREMENT PRIMARY KEY,
    device_id VARCHAR(50),
    temperature FLOAT,
    humidity FLOAT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

πŸ’» Step 4: The ESP32 Firmware

To communicate with the modem using AT commands without writing complex parsing code, we rely on the TinyGSM library.

⚠️ Crucial Library Warning

The standard version of TinyGSM found in the Arduino Library Manager may not fully support the A7670G's specific AT command set yet.

For this tutorial to work, you must use the specific fork linked below:

Installation Instructions:

  1. Download the repository as a .zip file from GitHub.
  2. Open Arduino IDE.
  3. Go to Sketch -> Include Library -> Add .ZIP Library...
  4. Select the downloaded file.

To keep our code clean and easy to edit, we will split it into two files:

  1. config.h: Stores your settings (WiFi, APN, Pins) so you can change them easily.
  2. main.ino: Contains the actual logic and code.

Step 1: Create the Config File

  1. Open Arduino IDE and create a new sketch.
  2. Click the small arrow icon (β–Ό) near the top right of the editor tabs.
  3. Select "New Tab".
  4. Name the file config.h and click OK.
  5. config.h: Stores your settings (WiFi, APN, Pins) so you can change them easily.
  6. main.ino: Contains the actual logic and code.

Config.h

#define DEVICE_ID "W001"

// === DHT11 Pin Definitions ===
#define DHTPIN 23
#define DHTTYPE DHT11  // DHT 22  (AM2302), AM2321

// === SIM7600 Pin Definitions ===
#define MODEM_RESET_PIN 5
#define MODEM_PWKEY 4
#define MODEM_POWER_ON 12
#define MODEM_TX 26
#define MODEM_RX 27
#define MODEM_RESET_LEVEL HIGH
#define BUILTIN_LED 2


const char apn[] = "internet";
const char gprsUser[] = "";
const char gprsPass[] = "";

const char *mqtt_broker = "";
int port = 1883;
const char *mqtt_username = "";
const char *mqtt_password = "";
const char *publish_topic = "weather/data";
const char *subs_topic = "device/command";

Main.ino

#define TINY_GSM_MODEM_A7670
#define SerialMon Serial
#define SerialAT Serial1
#define TINY_GSM_DEBUG SerialMon
#define GSM_PIN ""

#include <PubSubClient.h>
#include <TinyGsmClient.h>
#include <ArduinoJson.h>
#include <DHT.h>
#include "config.h"

#ifdef DUMP_AT_COMMANDS
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, Serial);
TinyGsm modem(debugger);
#else
TinyGsm modem(SerialAT);
#endif


TinyGsmClient client(modem);
PubSubClient mqtt(client);
DHT dht(DHTPIN, DHTTYPE);

uint32_t lastReconnectAttempt = 0;
long lastMsg = 0;

void mqttCallback(char *topic, byte *message, unsigned int len) {
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String messageTemp;

  for (int i = 0; i < len; i++) {
    Serial.print((char)message[i]);
    messageTemp += (char)message[i];
  }
  Serial.println();
}

boolean mqttConnect() {
  SerialMon.print("Connecting to ");
  SerialMon.print(mqtt_broker);
  boolean status = mqtt.connect(DEVICE_ID, mqtt_username, mqtt_password);
  if (status == false) {
    SerialMon.println(" fail");
    ESP.restart();
    return false;
  }
  SerialMon.println(" success");
  mqtt.subscribe(subs_topic);
  return mqtt.connected();
}

void setup() {
  SerialMon.begin(115200);
  // Power ON the modem
  pinMode(MODEM_POWER_ON, OUTPUT);
  pinMode(BUILTIN_LED, OUTPUT);
  pinMode(BUILTIN_LED, LOW);
  dht.begin();

  digitalWrite(MODEM_POWER_ON, HIGH);
  pinMode(MODEM_RESET_PIN, OUTPUT);
  digitalWrite(MODEM_RESET_PIN, !MODEM_RESET_LEVEL);
  delay(100);
  digitalWrite(MODEM_RESET_PIN, MODEM_RESET_LEVEL);
  delay(2600);
  digitalWrite(MODEM_RESET_PIN, !MODEM_RESET_LEVEL);

  // Toggle PWRKEY to power up the modem
  pinMode(MODEM_PWKEY, OUTPUT);
  digitalWrite(MODEM_PWKEY, LOW);
  delay(100);
  digitalWrite(MODEM_PWKEY, HIGH);
  delay(1000);
  digitalWrite(MODEM_PWKEY, LOW);

  SerialMon.println("Wait ...");
  SerialAT.begin(115200, SERIAL_8N1, MODEM_RX, MODEM_TX);
  delay(3000);

  SerialMon.println("Initializing modem...");
  if (!modem.init()) {
    SerialMon.println("Failed to restart modem, delaying 10s and retrying");
    delay(10000);
    return;
  }

  String modemInfo = modem.getModemInfo();
  SerialMon.print("Modem Info: ");
  SerialMon.println(modemInfo);
  // Unlock SIM if needed
  if (GSM_PIN && modem.getSimStatus() != 3) {
    modem.simUnlock(GSM_PIN);
  }
  String ccid = modem.getSimCCID();
  DBG("CCID:", ccid);
  String imei = modem.getIMEI();
  DBG("IMEI:", imei);
  String imsi = modem.getIMSI();
  DBG("IMSI:", imsi);
  String cop = modem.getOperator();
  DBG("Operator:", cop);

  int csq = modem.getSignalQuality();
  DBG("Signal quality:", csq);

  SerialMon.print("Waiting for network...");
  if (!modem.waitForNetwork()) {
    SerialMon.println(" fail");
    delay(10000);
    return;
  }
  SerialMon.println(" success");
  if (modem.isNetworkConnected()) {
    SerialMon.println("Network connected");
  }
  SerialMon.print("Connecting to APN: ");
  SerialMon.print(apn);
  if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
    SerialMon.println(" fail");
    ESP.restart();
  }
  SerialMon.println(" OK");
  if (modem.isGprsConnected()) {
    SerialMon.println("GPRS connected");
  }
  IPAddress local = modem.localIP();
  DBG("Local IP:", local);

  DBG("Asking modem to sync with NTP");
  modem.NTPServerSync("132.163.96.5", 20);
  // MQTT Broker setup
  mqtt.setServer(mqtt_broker, port);
  mqtt.setCallback(mqttCallback);
}

void loop() {
  if (!mqtt.connected()) {
    SerialMon.println("=== MQTT NOT CONNECTED ===");
    // Reconnect every 10 seconds
    uint32_t t = millis();
    if (t - lastReconnectAttempt > 10000L) {
      lastReconnectAttempt = t;
      if (mqttConnect()) {
        lastReconnectAttempt = 0;
      }
    }
    delay(100);
    return;
  }
  long now = millis();
  if (now - lastMsg > 10000) {
    lastMsg = now;
    publish_data();
  }
  mqtt.loop();
}

void publish_data() {
  float h = dht.readHumidity();
  float t = dht.readTemperature();

  if (isnan(h) || isnan(t)) {
    Serial.println("Failed to read from DHT11 sensor!");
    return;
  }

  StaticJsonDocument<200> doc;
  doc["device_id"] = DEVICE_ID;
  doc["temperature"] = t;
  doc["humidity"] = h;

  String jsonStr;
  serializeJson(doc, jsonStr);
  Serial.println(jsonStr);
  Serial.println("Publishing to MQTT Broker");
  mqtt.publish(publish_topic, jsonStr.c_str());
}

🐍 Step 5: The Python Backend

The final step is the bridge. This Python script runs on your VPS. It acts as a "listener" that waits for data from MQTT and instantly inserts it into the MySQL database.

We will split this into two files for better organization.

1. Install Python Libraries:

pip install paho-mqtt mysql-connector-python

2. The Config File (config.py) Create this file to store your credentials securely.

# MQTT Broker details
broker = "mqtt.iotbhai.io"
port = 1883
username = ""
password = ""
topic = "weather/data"

# MySQL config
mysql_host  = "localhost"
mysql_user  = "root"
mysql_password  = "your_password"
mysql_db  = "weather"
table_name = "weather_data"

3. The Main Script (main.py)Run this script to start the service.

import paho.mqtt.client as mqtt
import json
import mysql.connector
from config import *

# === MySQL CONNECTION ===
db = mysql.connector.connect(
    host=mysql_host,
    user=mysql_user,
    password=mysql_password,
    database=mysql_db
)
cursor = db.cursor()

# Called when the client connects to the broker
def on_connect(client, userdata, flags, rc):
    if rc == 0:
        print("Connected to MQTT Broker!")
        client.subscribe(topic)
    else:
        print("Failed to connect, return code:", rc)

# Called when a message is received
def on_message(client, userdata, msg):
    try:
        payload = msg.payload.decode()
        print(f"πŸ“¨ Message received on topic '{msg.topic}': {payload}")
        data = json.loads(payload)

        device_id = data.get("device_id")
        temperature = data.get("temperature")
        humidity = data.get("humidity")

        print(f"🌑️ Temperature: {temperature} °C")
        print(f"πŸ’§ Humidity: {humidity} %\n")

        # SQL Insert using table_name variable
        sql = f"INSERT INTO {table_name} (device_id, temperature, humidity) VALUES (%s, %s, %s)"
        values = (device_id, temperature, humidity)
        cursor.execute(sql, values)
        db.commit()

    except Exception as e:
        print("❗ Error processing message:", e)

# Create client and set username/password
client = mqtt.Client()
client.username_pw_set(username, password)

client.on_connect = on_connect
client.on_message = on_message

# Connect and loop forever
client.connect(broker, port, keepalive=60)
client.loop_forever()

πŸ“Š Step 6: Testing the Pipeline

  1. Start the Python Script: Run python3 main.py on your VPS/PC. You should see "Connected to MQTT Broker".
  2. Power the ESP32: Connect your ESP32 + SIM A7670G. Open the Serial Monitor.
  3. Watch the Magic:
    • ESP32: Publishing to MQTT Broker {"device_id":"W001", ...}
    • Python: πŸ“¨ Message received... πŸ’Ύ Data Saved to MySQL
    • MySQL: Run SELECT * FROM weather_data; and watch the rows fill up!

🎬 Video Tutorial

If you prefer a visual walkthrough, watch my complete video guide here:

🌍 Real-World Use Cases

Since this system uses Cellular 4G instead of Wi-Fi, it works anywhere. Here are three powerful ways to use this architecture:

1. Smart Agriculture (Precision Farming)

Farms are often miles from Wi-Fi. By adding a Soil Moisture Sensor and a solar panel, this device can wake up hourly to report soil health to your database. This allows farmers to automate irrigation valves remotely without walking the field.

2. Vehicle Telematics & Fleet Tracking

Logistics companies need to track trucks in real-time. Since the SIM A7670G has built-in GPS, you can modify the code to send location, speed, and engine temperature. This creates a live fleet dashboard that stores historical route data permanently.

3. Remote Industrial Monitoring (IIoT)

Remote generators and water pumps often fail unnoticed. By connecting the ESP32 to a machine’s "Fault" relay or vibration sensor, you get instant alerts. If a machine stops, the system logs the error to MySQL and notifies maintenance engineers immediately.

πŸš€ Conclusion

Congratulations! You have successfully built a Full-Stack Cellular IoT Solution.

Most beginners stop at blinking an LED or sending data to a local Wi-Fi server. By completing this guide, you have stepped into the world of Professional IoT. You have solved the three biggest challenges in the industry:

  1. Connectivity: You aren't limited by Wi-Fi range (thanks to 4G LTE).
  2. Scalability: You aren't relying on public, insecure brokers (thanks to your VPS).
  3. Data Ownership: You aren't paying a monthly fee to a cloud dashboard (thanks to your own MySQL database).

What's Next? Now that your data is safe in MySQL, the final step is Visualization. In our next tutorial, we will connect Grafana to this MySQL database to create beautiful, real-time dashboards that look like this:

Subscribe to the IoT Bhai YouTube Channel so you don't miss Part 4!