> Projects / esp32-gps-tracker

ESP32 GPS Tracker with MQTT

DATE: 2026.04.25 STATUS: COMPLETED LEVEL: INTERMEDIATE
ESP32 and Neo-6M Wiring Diagram

> problem

I needed a reliable way to track outdoor assets and visualize them in my Home Assistant dashboard without relying on proprietary cloud services.

> hardware setup

The core of the system is an ESP32 DevKit V1 paired with a Neo-6M GPS module. Communication is handled over HardwareSerial (Serial2) at 9600 baud. It's powered via a standard 5V USB connection.

> key challenges

The primary hurdle was finding the correct baud rate for the Neo-6M, which defaults to 9600 rather than the common 115200. Additionally, indoor GPS signal reception is limited, requiring optimal placement near windows for initial satellite lock.

> architecture

graph LR A[Neo-6M GPS] -->|UART 9600| B[ESP32] B -->|WiFi| C[MQTT Broker] C -->|Dashboard| D[Home Assistant]

> logic flow

sequenceDiagram participant G as GPS Module participant E as ESP32 participant M as MQTT Broker Loop Every 1s G->>E: Raw NMEA Data E->>E: Decode with TinyGPS++ Note over E: Validate Fix alt Fix is Valid E->>M: Publish Lat/Lng else No Fix Note over E: Wait for Satellites end end

> code: esp32-gps-tracker.ino

Purpose: Primary logic for GPS decoding and MQTT transmission.

#include <TinyGPS++.h>
#include <HardwareSerial.h>
#include <WiFi.h>
#include <PubSubClient.h>

// Configuration
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
const char* mqtt_server = "192.168.1.100";

TinyGPSPlus gps;
HardwareSerial SerialGPS(2);
WiFiClient espClient;
PubSubClient client(espClient);

void setup() {
  Serial.begin(115200);
  SerialGPS.begin(9600, SERIAL_8N1, 16, 17);
  
  Serial.println("Connecting to WiFi...");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi connected.");

  client.setServer(mqtt_server, 1883);
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  while (SerialGPS.available() > 0) {
    if (gps.encode(SerialGPS.read())) {
      if (gps.location.isValid()) {
        char msg[50];
        snprintf(msg, 50, "Lat: %f, Lng: %f", gps.location.lat(), gps.location.lng());
        client.publish("lab/gps/location", msg);
        Serial.print("Published: ");
        Serial.println(msg);
      }
    }
  }
  delay(1000);
}

void reconnect() {
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    if (client.connect("ESP32GPSClient")) {
      Serial.println("connected");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);
    }
  }
}

> result

The system is now fully operational, providing stable coordinates to my Home Assistant setup with a latency of approximately 2 seconds. The use of TinyGPS++ and interrupt-style decoding ensures no data is lost even with the 1-second stabilization delay.