Đọc định vị GPS với ESP32 và SIM7600CE-M1S

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ầnMô tảGhi chú
🧠 ESP32 DevKit V1Vi điều khiển chínhUART2 dùng để giao tiếp AT
📶 SIM7600CE-T (hoặc SIM7600E, SIM7600G)Module 4G LTE + GPSHỗ trợ GNSS (GPS/GLONASS/BeiDou)
🛰️ Anten GNSSAnten GPS chuyên dụngCắm vào cổng GNSS của SIM7600
📱 SIM 4G ViettelTruy cập GPRS/InternetAPN: v-internet
💡 LED GPIO2Báo trạng tháiĐiều khiển qua MQTT
Nguồn 5V – 2A ổn địnhCấp cho ESP32 và SIM7600SIM7600 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:

4. Một số lưu ý khi dùng GPS trên SIM7600

Vấn đềNguyên nhânCách khắc phục
Không fix GPSAnten GNSS chưa cắm đúng cổngDù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 ephemerisSau lần đầu, lần sau chỉ 10–20 giây
Không thấy tín hiệuGPS bị tắtGử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);
  }
}

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *