🛰️ ESP32 GPS NEO-6M 卫星定位:DIY 你的第一个 GPS 定位器

📅 2026-05-26 · 📂 硬件编程 · ⏱ 阅读约 10 分钟

📺 B站推荐视频:GPS NEO-6M 卫星定位模块的经纬度读取实验 · 零基础手把手教学

📡 概述

GPS(全球定位系统)是物联网和嵌入式项目中最常用的定位技术之一。NEO-6M 是 u-blox 公司出品的经典 GPS 模块,凭借价格便宜(¥15~30)、接口简单(串口通信)、精度够用(2.5m)三大优势,成为 Arduino/ESP32 社区最受欢迎的定位方案。

NEO-6M 模块自带陶瓷天线和信号放大器,上电后自动搜索卫星。一旦锁定 3 颗以上卫星,就开始通过串口持续输出 NMEA-0183 标准格式的定位数据——包括经纬度、海拔、速度、UTC 时间等信息。我们的 ESP32 只需要用软串口(SoftwareSerial)或硬件串口读取这些文本,用 TinyGPS++ 库解析即可。

💡 适用场景:户外 GPS 轨迹记录仪、共享单车定位、无人机导航、宠物追踪器、车载定位、地理围栏报警。在室内或高楼密集区信号会衰减,建议户外开阔地带测试。

📦 物料清单

物料型号/参数数量参考价格
ESP32 开发板ESP32-WROOM-32 / ESP32-S31¥15~25
GPS 模块NEO-6M(带陶瓷天线+信号放大器)1¥15~30
OLED 显示屏SSD1306 128×64 I2C(可选)1¥8~15
MicroSD 模块SPI 接口 TF 卡读写模块(可选,轨迹记录用)1¥3~8
TF 卡4GB+ Class101¥10~15
锂电池3.7V 18650 ×1 + TP4056 充电模块1 套¥12~20
面包板 + 杜邦线400 孔面包板 + 公母杜邦线1 套¥5~10

💰 总计:约 ¥68~123(不含运费)。核心 GPS 模块不到 20 块,性价比极高。

🔌 接线图

NEO-6M 模块通过 UART 串口 与 ESP32 通信。模块上的 PPS 引脚是秒脉冲输出,本项目不用,悬空即可。

┌─────────────────────────────────────────────────────────┐
│                 NEO-6M GPS 接线图                        │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  【NEO-6M 模块引脚】        【ESP32 引脚】                │
│                                                         │
│  VCC  (红线)  ────────  3.3V                            │
│  GND  (黑线)  ────────  GND                             │
│  TX   (蓝线)  ────────  GPIO 16 (RX2 / 软串口RX)        │
│  RX   (绿线)  ────────  GPIO 17 (TX2 / 软串口TX)        │
│  PPS  (黄线)  ────────  (悬空,不用)                     │
│                                                         │
│  ⚠️ 模块工作电压 3.3V~5V,建议接 3.3V                    │
│  ⚠️ GND 必须共地!                                      │
│                                                         │
├─────────────────────────────────────────────────────────┤
│  【OLED SSD1306(可选显示)】                             │
│  OLED VCC ──── 3.3V                                     │
│  OLED GND ──── GND                                      │
│  OLED SDA ──── GPIO 21                                  │
│  OLED SCL ──── GPIO 22                                  │
│                                                         │
├─────────────────────────────────────────────────────────┤
│  【MicroSD 模块(可选记录)】                              │
│  SD CS   ──── GPIO 5                                    │
│  SD SCK  ──── GPIO 18                                   │
│  SD MOSI ──── GPIO 23                                   │
│  SD MISO ──── GPIO 19                                   │
│  SD VCC  ──── 3.3V / 5V(视模块)                        │
│  SD GND  ──── GND                                       │
└─────────────────────────────────────────────────────────┘
💡 为什么用 GPIO 16/17?ESP32 有三个硬件串口:Serial0(USB调试)、Serial1(GPIO 9/10,部分板子被Flash占用)、Serial2(GPIO 16/17,最好用)。我们使用 Serial2,波特率设为 9600(NEO-6M 默认),稳定可靠。

💻 基础代码:读取经纬度并打印到串口监视器

首先安装 TinyGPS++ 库:Arduino IDE → 工具 → 管理库 → 搜索 "TinyGPSPlus" → 安装(作者:Mikal Hart)。

// ESP32 + NEO-6M GPS 基础定位程序 // 使用 Serial2 硬件串口 + TinyGPS++ 库 #include <TinyGPS++.h> #include <HardwareSerial.h> // 使用 Serial2:RX=GPIO16, TX=GPIO17 #define GPS_RX 16 #define GPS_TX 17 #define GPS_BAUD 9600 TinyGPSPlus gps; HardwareSerial gpsSerial(2); // 使用 UART2 void setup() { Serial.begin(115200); gpsSerial.begin(GPS_BAUD, SERIAL_8N1, GPS_RX, GPS_TX); Serial.println("🛰️ GPS NEO-6M 定位启动中..."); Serial.println("请将模块放在窗边或户外开阔处"); } void loop() { // 持续喂数据给 TinyGPS++ while (gpsSerial.available() > 0) { char c = gpsSerial.read(); gps.encode(c); } // 每秒输出一次定位信息 static unsigned long lastPrint = 0; if (millis() - lastPrint > 1000) { lastPrint = millis(); displayGPSInfo(); } } void displayGPSInfo() { Serial.print("📍 卫星数: "); Serial.println(gps.satellites.value()); if (gps.location.isValid()) { Serial.print("🌍 纬度: "); Serial.println(gps.location.lat(), 6); Serial.print("🌍 经度: "); Serial.println(gps.location.lng(), 6); Serial.print("⛰️ 海拔: "); Serial.print(gps.altitude.meters()); Serial.println(" m"); Serial.print("🚀 速度: "); Serial.print(gps.speed.kmph()); Serial.println(" km/h"); } else { Serial.println("⏳ 等待定位中... (请确保在户外开阔处)"); } if (gps.time.isValid()) { Serial.printf("🕐 UTC时间: %02d:%02d:%02d\n", gps.time.hour(), gps.time.minute(), gps.time.second()); } Serial.println("───────────────────"); }

📟 进阶应用:OLED 屏幕实时显示定位

光在串口监视器上看数据不够酷。我们把 OLED 屏接上,做一个手持 GPS 定位器——屏幕实时显示经纬度、海拔和卫星数。

// ESP32 + NEO-6M + SSD1306 OLED 手持定位器 #include <TinyGPS++.h> #include <HardwareSerial.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_ADDR 0x3C Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1); TinyGPSPlus gps; HardwareSerial gpsSerial(2); void setup() { Serial.begin(115200); gpsSerial.begin(9600, SERIAL_8N1, 16, 17); Wire.begin(21, 22); if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) { Serial.println("OLED 初始化失败!"); while (1); } display.clearDisplay(); display.setTextColor(SSD1306_WHITE); } void loop() { while (gpsSerial.available() > 0) { gps.encode(gpsSerial.read()); } display.clearDisplay(); display.setTextSize(1); display.setCursor(0, 0); display.print("🛰️ Sat:"); display.println(gps.satellites.value()); if (gps.location.isValid()) { display.setCursor(0, 14); display.print("Lat:"); display.println(gps.location.lat(), 5); display.setCursor(0, 26); display.print("Lng:"); display.println(gps.location.lng(), 5); display.setCursor(0, 40); display.print("Alt:"); display.print(gps.altitude.meters(), 1); display.println("m"); display.setCursor(0, 53); display.print("Spd:"); display.print(gps.speed.kmph(), 1); display.println("km/h"); } else { display.setCursor(10, 30); display.println("Searching GPS..."); } display.display(); delay(200); }
💡 需要安装的库:Adafruit SSD1306 + Adafruit GFX Library。Arduino IDE → 管理库 → 分别搜索安装。OLED 地址通常是 0x3C,不显示的话用 I2C Scanner 扫描确认。

🔧 实战场景一:GPS 轨迹记录仪

场景:骑车/徒步时记录行进路线,生成 GPX 文件导入地图软件查看。

物料组合:ESP32 + NEO-6M + MicroSD 模块 + 18650 电池

思路:每秒采集一次 GPS 坐标,写入 MicroSD 卡上的 CSV 文件(lat,lng,alt,speed,time)。回到家拔卡插电脑,用 Google Earth 或 GPX Editor 打开,完整轨迹一目了然。

联动之前学的:

🏠 实战场景二:地理围栏报警器

场景:设定一个安全区域(如家周围 500 米),当设备离开该范围时触发蜂鸣器报警——适合宠物项圈或老人防走失。

物料组合:ESP32 + NEO-6M + 有源蜂鸣器 + LED

核心逻辑:计算当前位置与预设中心点的距离(Haversine公式),超过半径阈值就触发 GPIO 输出。配合 WiFi 还可以发 HTTP 报警到手机。

// Haversine 公式 — 计算两点间距离(米) double haversine(double lat1, double lng1, double lat2, double lng2) { const double R = 6371000; // 地球半径(米) double dLat = (lat2 - lat1) * PI / 180.0; double dLng = (lng2 - lng1) * PI / 180.0; double a = sin(dLat/2) * sin(dLat/2) + cos(lat1 * PI/180.0) * cos(lat2 * PI/180.0) * sin(dLng/2) * sin(dLng/2); return R * 2 * atan2(sqrt(a), sqrt(1 - a)); } // 使用示例:判断是否超出500米围栏 double homeLat = 22.5431; // 你的家经纬度 double homeLng = 113.9526; double distance = haversine( gps.location.lat(), gps.location.lng(), homeLat, homeLng ); if (distance > 500) { digitalWrite(BUZZER_PIN, HIGH); // 蜂鸣报警! }

⚠️ 常见问题

❌ 问题1:模块一直显示 "等待定位中",搜不到卫星
原因:GPS 信号无法穿透墙壁和金属。NEO-6M 冷启动首次定位需要 30秒~2分钟,且必须在户外开阔处。
解决:① 将模块放到窗边或阳台;② 模块陶瓷天线朝上(有字一面朝天);③ 耐心等待 2 分钟以上。如果模块上的蓝色 LED 开始闪烁(1秒1闪),说明已锁定卫星。
❌ 问题2:串口收到乱码或 TinyGPS++ 解析不出来
原因:波特率不匹配。部分 NEO-6M 模块出厂波特率不是 9600。
解决:① 先用 USB-TTL 模块直接连接 NEO-6M(TX→RX, RX→TX),在串口助手 9600 波特率下看是否有 $GPGGA 开头的数据;② 如果没有,切换到 4800 / 38400 / 115200 逐个尝试;③ 用 u-center 软件(u-blox 官方工具)重新配置模块波特率。
❌ 问题3:经纬度偏差很大,漂移几百米
原因:① 模块附近有强电磁干扰(电机、大电流走线);② 卫星数少于 4 颗时精度下降;③ HDOP(水平精度因子)过高。
解决:① GPS 模块远离电机和电源走线,至少 5cm;② 检查 gps.hdop.value(),值越小越精确(<1.0 为优秀,>5.0 不可用);③ 在代码中加入滤波——连续取 10 次坐标取中位数。
❌ 问题4:GPS 模块带电拔插后损坏或异常
原因:NEO-6M 模块对电压敏感,热拔插可能导致闩锁效应或损坏 RF 前端。
解决:① 永远断电后再拔插模块;② 在 VCC 引脚并联一个 100μF 电解电容做电源滤波;③ 如果模块异常(灯不亮/不输出),断电放置 30 秒后再上电,通常能复位。