1. Giới thiệu
Trong kỷ nguyên của Internet of Things (IoT), việc thu thập vị trí, tốc độ và trạng thái của thiết bị theo thời gian thực trở nên cực kỳ quan trọng.
Ở bài này, mình chia sẻ dự án ESP32 kết hợp module SIM7600 để lấy toạ độ GPS và gửi dữ liệu lên server MQTT, cho phép giám sát vị trí thiết bị ở bất kỳ đâu có sóng 4G 📡.
Mục tiêu:
Lấy dữ liệu GPS (lat, lon, tốc độ, cao độ, thời gian) từ SIM7600 → truyền qua mạng di động → publish lên MQTT broker → theo dõi trên cloud (hoặc Node-RED, HiveMQ, v.v.)
2. Phần cứng sử dụng
| Thành phần | Mô tả | Ghi chú |
|---|---|---|
| 🧠 ESP32 DevKit V1 | Vi điều khiển chính | UART2 dùng để giao tiếp AT |
| 📶 SIM7600CE-T (hoặc SIM7600E, SIM7600G) | Module 4G LTE + GPS | Hỗ trợ GNSS (GPS/GLONASS/BeiDou) |
| 🛰️ Anten GNSS | Anten GPS chuyên dụng | Cắm vào cổng GNSS của SIM7600 |
| 📱 SIM 4G Viettel | Truy cập GPRS/Internet | APN: v-internet |
| 💡 LED GPIO2 | Báo trạng thái | Điều khiển qua MQTT |
| ⚡ Nguồn 5V – 2A ổn định | Cấp cho ESP32 và SIM7600 | SIM7600 yêu cầu dòng đỉnh 2A khi bật sóng |
3. Phần mềm và thư viện
Dự án được lập trình bằng Arduino IDE với các thư viện:
TinyGSM: giao tiếp AT command với SIM7600.PubSubClient: client MQTT chuẩn.ArduinoJson: tạo JSON để truyền dữ liệu gọn nhẹ.
4. Một số lưu ý khi dùng GPS trên SIM7600
| Vấn đề | Nguyên nhân | Cách khắc phục |
|---|---|---|
| Không fix GPS | Anten GNSS chưa cắm đúng cổng | Dùng cổng GNSS, không dùng MAIN |
| GPS chậm fix (lần đầu > 1 phút) | Chưa có dữ liệu ephemeris | Sau lần đầu, lần sau chỉ 10–20 giây |
| Không thấy tín hiệu | GPS bị tắt | Gửi AT: AT+CGNSPWR=1 để bật GPS |
| Dữ liệu rỗng | Ở trong nhà, tín hiệu yếu | Đưa anten ra ngoài trời |
/**************************************************************
ESP32 + SIM7600 MQTT + GPS Integration
Pinout:
- MODEM_RX: GPIO 27
- MODEM_TX: GPIO 26
- MODEM_RESET: GPIO 25
- LED: GPIO 2
**************************************************************/
#define TINY_GSM_MODEM_SIM7600
#define ARDUINOJSON_USE_DOUBLE 1
// Debug console
#define SerialMon Serial
#define SerialAT Serial2
//#define DEBUG_MODE
#ifdef DEBUG_MODE
#define DUMP_AT_COMMANDS
#define TINY_GSM_DEBUG SerialMon
#endif
// GPRS credentials
const char apn[] = "v-internet"; // Viettel
const char gprsUser[] = "";
const char gprsPass[] = "";
// MQTT details
const char* broker = "broker.hivemq.com";
const char* topicLed = "zenopcb/led";
const char* topicInit = "zenopcb/init";
const char* topicLedStatus = "zenopcb/ledStatus";
const char* topicGps = "zenopcb/gps"; // thêm topic GPS
#include <TinyGsmClient.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#ifdef DUMP_AT_COMMANDS
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm modem(debugger);
#else
TinyGsm modem(SerialAT);
#endif
TinyGsmClient client(modem);
PubSubClient mqtt(client);
// Pin definitions
#define MODEM_RX 27
#define MODEM_TX 26
#define MODEM_RESET_PIN 25
#define LED_PIN 2
int ledStatus = LOW;
uint32_t lastReconnectAttempt = 0;
// --- GPS variables ---
unsigned long lastGpsTime = 0;
#define GPS_INTERVAL 10000UL // 10 giây
float gps_lat = 0, gps_lon = 0, gps_alt = 0, gps_speed = 0;
int gps_vsat = 0, gps_usat = 0, gps_year = 0, gps_month = 0, gps_day = 0;
int gps_hour = 0, gps_min = 0, gps_sec = 0;
float gps_accuracy = 0;
String gps_time = "";
// === MQTT CALLBACK ===
void mqttCallback(char* topic, byte* payload, unsigned int len) {
SerialMon.print("Message arrived [");
SerialMon.print(topic);
SerialMon.print("]: ");
SerialMon.write(payload, len);
SerialMon.println();
if (String(topic) == topicLed) {
ledStatus = !ledStatus;
digitalWrite(LED_PIN, ledStatus);
mqtt.publish(topicLedStatus, ledStatus ? "1" : "0");
}
}
// === MQTT CONNECT ===
boolean mqttConnect() {
SerialMon.print("Connecting to ");
SerialMon.print(broker);
boolean status = mqtt.connect("ESP32_SIM7600_GPS");
if (!status) {
SerialMon.println(" fail");
return false;
}
SerialMon.println(" success");
mqtt.publish(topicInit, "ESP32_SIM7600_GPS connected");
mqtt.subscribe(topicLed);
return true;
}
// === MODEM RESET ===
void modemHardwareReset() {
pinMode(MODEM_RESET_PIN, OUTPUT);
digitalWrite(MODEM_RESET_PIN, HIGH);
delay(50);
SerialMon.println("Resetting modem...");
digitalWrite(MODEM_RESET_PIN, LOW);
delay(1000);
digitalWrite(MODEM_RESET_PIN, HIGH);
SerialMon.println("Waiting for modem to boot...");
delay(10000);
}
// === GET GPS DATA ===
void getGPSData() {
if ((millis() - lastGpsTime) < GPS_INTERVAL) return;
lastGpsTime = millis();
if (modem.getGPS(&gps_lat, &gps_lon, &gps_speed, &gps_alt, &gps_vsat, &gps_usat,
&gps_accuracy, &gps_year, &gps_month, &gps_day,
&gps_hour, &gps_min, &gps_sec)) {
gps_time = modem.getGSMDateTime(DATE_FULL);
SerialMon.println("📡 GPS fix OK:");
SerialMon.print(" Lat: "); SerialMon.println(gps_lat, 6);
SerialMon.print(" Lon: "); SerialMon.println(gps_lon, 6);
SerialMon.print(" Alt: "); SerialMon.println(gps_alt);
SerialMon.print(" Speed: "); SerialMon.println(gps_speed);
SerialMon.print(" Time: "); SerialMon.println(gps_time);
// Gửi dữ liệu lên MQTT
StaticJsonDocument<128> doc;
doc["lat"] = gps_lat;
doc["lon"] = gps_lon;
doc["alt"] = gps_alt;
doc["speed"] = gps_speed;
doc["time"] = gps_time;
String out;
serializeJson(doc, out);
mqtt.publish(topicGps, out.c_str());
SerialMon.print("Publish GPS: ");
SerialMon.println(out);
} else {
SerialMon.println("⚠ Cannot get GPS fix.");
}
}
void setup() {
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
pinMode(MODEM_RX, INPUT_PULLUP);
SerialMon.begin(115200);
delay(1000);
SerialMon.println("\n=== ESP32 + SIM7600 MQTT + GPS ===");
modemHardwareReset();
SerialAT.begin(115200, SERIAL_8N1, MODEM_RX, MODEM_TX);
delay(3000);
SerialMon.println("Initializing modem...");
modem.restart();
SerialMon.print("Modem Info: "); SerialMon.println(modem.getModemInfo());
SerialMon.print("Waiting for network...");
if (!modem.waitForNetwork(60000L)) {
SerialMon.println(" fail");
return;
}
SerialMon.println(" success");
if (modem.isNetworkConnected()) {
SerialMon.println("Network connected");
}
int csq = modem.getSignalQuality();
SerialMon.print("Signal quality: "); SerialMon.println(csq);
// GPRS
SerialMon.print("Connecting to APN: "); SerialMon.println(apn);
if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
SerialMon.println(" fail");
return;
}
SerialMon.println(" success");
SerialMon.println("GPRS connected");
// Enable GPS
SerialMon.println("Enabling GPS (warm up 20s)...");
modem.enableGPS();
delay(20000L);
mqtt.setServer(broker, 1883);
mqtt.setCallback(mqttCallback);
SerialMon.println("Setup complete!\n");
}
void loop() {
// --- Kiểm tra mạng ---
if (!modem.isNetworkConnected()) {
SerialMon.println("Network disconnected");
if (!modem.waitForNetwork(60000L, true)) return;
if (!modem.isGprsConnected()) modem.gprsConnect(apn, gprsUser, gprsPass);
}
// --- Kiểm tra MQTT ---
if (!mqtt.connected()) {
SerialMon.println("MQTT disconnected");
if (millis() - lastReconnectAttempt > 10000L) {
lastReconnectAttempt = millis();
if (mqttConnect()) lastReconnectAttempt = 0;
}
delay(100);
return;
}
mqtt.loop();
getGPSData(); // cập nhật và publish GPS
// --- Publish uptime test ---
static uint32_t lastPublish = 0;
if (millis() - lastPublish > 30000) {
lastPublish = millis();
String msg = "ESP32 uptime: " + String(millis() / 1000) + "s";
mqtt.publish(topicInit, msg.c_str());
SerialMon.println(msg);
}
}

