NEO-6M GPS 模块接 Arduino:实时坐标、首次定位与室内定不了位

Cartoon of a blue NEO-6M GPS module with a silver metal-can chip and four pins, its grey coaxial cable running to a large square silver-shielded ceramic patch antenna tilted skyward with teal signal arcs and navy stars above it

NEO-6M 只用三根线就能把实时坐标送进 Arduino —— VCC 接 5 V、GND 接 GND,模块的 TX 交叉接到一支 SoftwareSerial 引脚上,波特率 9600 —— 但前提是在户外。在室内,它照样上电、照样吐数据,却永远定不了位,那颗定位指示灯也就永远不闪。那是一颗正常工作的模块,不是坏掉的模块。

在 Arduino 上读 GPS 坐标,需要哪些零件?

GY-NEO-6M GPS 模块是一块蓝色小板,板上是一颗银色金属罐封装的 u-blox NEO-6M 接收芯片,外加一片 25 mm 见方、表面银色屏蔽的陶瓷贴片天线,用一条灰色同轴细线连出来。它的四个连接沿着板子上缘丝印着 VCC、RX、TX、GND —— 而到手时那是四个光秃秃的镀金孔。从一条 40 针公排针上掰下四针焊上去,是这个作品的第一步,排在任何接线之前;技巧看我们的排针焊接教程

选配 —— 桌面显示、数据线与焊接工具

ItemPriceQty
0.96‘ OLED Display Module Blue White Screen I2C IIC Serial 128X64 LCD Display Board - 0.96' OLED WHITE0.96‘ OLED Display Module Blue White Screen I2C IIC Serial 128X64 LCD Display Board - 0.96' OLED WHITE096OLWHRM15.80
Data Cable Type-A Type-C MicroUSB Type-B 0.5m 1m 30cm 0.3m 100cm Data Transfer Upload Code - TYPE-A TO TYPE-B (1.0M)Data Cable Type-A Type-C MicroUSB Type-B 0.5m 1m 30cm 0.3m 100cm Data Transfer Upload Code - TYPE-A TO TYPE-B (1.0M)ATBC100RM4.90
80W Electronics Soldering Iron with LCD Display Ajustable Temperature Microconstroller Electronic Project80W Electronics Soldering Iron with LCD Display Ajustable Temperature Microconstroller Electronic ProjectASOIRONRM39.95
50g Soldering Wire Lead with Flux Core 1mm Diameter50g Soldering Wire Lead with Flux Core 1mm Diameter50SOTINRM9.90

0.96 寸 OLED 能把这个作品变成一台手持机,不用笔电也看得到坐标;它用的是 Uno 的 I2C 引脚 A4 和 A5,而这个作品正好没占用这两支。USB A 转 B 线就是 Uno 那个方形大插座认的形状 —— 抽屉里没有这个形状的线就加一条。烙铁和焊锡是给模块那四根排针用的;已经有烙铁就跳过。

焊好之后,模块身上是公针,Uno 那边是母排座,而 OLED 出厂就已经装好公针 —— 所以一包公对母杜邦线就能把两者直接接上板子,中间不用面包板。

蓝色 GPS 模块一角的卡通对比图:左边是四个空的金色环孔,打着橙色叉号;右边是同样的孔位焊上了黑色底座的四针银色排针,配青色勾号,旁边放着一条掰断的 40 针排针
模块出厂就是四个光秃秃的金色环孔。从排针上掰四针焊上去,是第零步,不是升级选项。

数据手册说 27 秒,为什么第一次定位要好几分钟?

NEO-6M 的 u-blox 数据手册给出的是冷启动 27 秒、热启动 1 秒、水平精度 2.5 m,全部是开阔天空下的数字。这个数字和大家实际等的十分钟之间的差距,不是模块有毛病,而是卫星在物理上能送多快的问题。

每一颗 GPS 卫星都以每秒 50 比特的速率播发导航电文,电文按 30 秒一帧组织;而接收机从每颗卫星身上真正要的东西 —— 子帧 1 里的钟差改正、子帧 2 和 3 里的轨道数据(也就是星历)—— 占掉了五个 6 秒子帧里的前三个,所以这段内容每 30 秒才重复一次。一台什么都不知道的接收机,必须把它想用的每一颗卫星那 18 秒的播发完整解码下来,而它每一帧只有一次机会从头接上。十八到三十秒的纯听时间是下限;改接线、换库都压不下去。

电文剩下的部分是历书 —— 整个星座的粗略轨道,稀稀拉拉地摊在连续 25 帧里,25 × 30 秒 = 12.5 分钟才收得齐。「先放外面待一会儿」背后就是这个数字:u-blox 给的典型值是首次定位之后约 12 分钟拿到完整历书。模块一旦有了它,就大致知道每颗卫星该在什么位置,不必再瞎找。

板子背面那颗备用电池,管的也是这件事。它用大约 22 µA 的电流,把接收机的时钟和最后一次的星历留在电池供电的存储器里,所以下一次上电就是热启动 —— 模块已经知道时间、也知道往哪里找,数据手册上那个 1 秒才变得现实。断电放上几个星期,或者电池已经没电,就又回到冷启动。

在户外看它一步步搜到星,这段等待会具体很多:

NEO-6M 为什么在室内永远定不了位?

NEO-6M 的灵敏度指标把这件事答得很干脆。跟踪与导航的灵敏度是 −161 dBm,但没有任何辅助信息的冷启动需要 −147 dBm。从零开始捕获一颗卫星,比守住一颗已经抓到的卫星,要多 14 dB 的信号 —— 功率上是二十五倍。数据手册那些时间,假设的是卫星信号到达时大约 −130 dBm,于是冷启动只剩 17 dB 余量,而跟踪有 31 dB —— 普通的混凝土、瓷砖和石膏,很快就把这点余量吃光。所以那颗你从花园拿进屋、摆在窗台上看起来一切正常的模块,在那里永远等不到它的第一次定位。

陶瓷贴片天线是有方向性的:那一面平整的银色金属才是接收面,它要的是这一面朝上对着开阔的天空,而不是立在一边 —— u-blox 的天线应用笔记要求贴片平面与地平线平行、天空视野无遮挡。阳台栏杆、花园桌子、车顶都行;底下垫一块平的金属不但不碍事,反而有帮助:它相当于扩大了天线的接地平面,这也是 u-blox 把车顶或仪表台称作理想位置的原因,并把有用的接地平面尺寸定在 50–70 mm 见方。真正挡住天空的,是那一面银色上方或旁边的金属 —— 包括车窗上的金属隔热膜 —— 而不是它底下的金属。雨和云不挡:1.5 GHz 的 GPS 信号能穿过水滴,倒是湿透的树叶和混凝土会把它吸掉。

卡通对比图:左边蓝色 GPS 模块和银色陶瓷天线待在灰色混凝土天花板底下,青色的信号弧线被橙色叉号挡住,指示灯是暗的;右边同一块模块在开阔天空下,信号弧线畅通无阻,红色指示灯在闪
同一块模块、同样的接线,换了两个地方。左边定位指示灯是暗的,而模块完全健康;右边它每秒闪一次。

NEO-6M 要怎么接到 Arduino Uno?

NEO-6M 芯片跑在 2.7–3.6 V 的电源轨上,而这条轨是蓝色小板上那颗小稳压器从你喂给 VCC 的电压做出来的,所以给 5 V 是正确的、也是设计预期的用法。要按 3.3 V 看待的是芯片自己的 I/O 引脚:u-blox 规定它们的输入范围是 0 V 到 VCC,绝对最大值 3.6 V,所以 Uno 那支 5 V 的 TX 不能直接落在模块的 RX 脚上。下面这个作品干脆完全不用那个方向。

模块引脚 Uno 为什么
VCC 5V 板上的稳压器把它降到接收芯片的 3.3 V 电源轨
GND GND 共同的参考地 —— 少了它,串口信号没有意义
TX D4 交叉接。模块说,Uno 听。代码需要的就只有这一根线
RX —(空着) 只有要重新配置模块时才用得上。Uno 的 TX 是 5 V,得先加分压

TX 接 TX 是悄无声息地失败 —— 两个发送端对着喊,既不出数据,也不报错 —— 所以这个错误活得最久。D4 本身可以随便选 —— 除了 0 和 1,任何数字引脚都行。

模块的 TX 把信号线驱到它内部那条 3.3 V 电源轨的电平。工作在 5 V 的 ATmega328P 把高于 0.6 × VCC —— 也就是 3.0 V —— 的电平算作逻辑高,所以 3.3 V 越过门限,还剩大约 0.3 V 余量。这 0.3 V 的余量很薄,长杜邦线上的噪声足以在一个字符中间把某一位拉到门限以下:TX 那根线要短。我们的 HC-05 蓝牙教程出于同样的理由,用的也是这种只接收的接法。

NEO-6M 的 Arduino 代码要用哪个库?

NEO-6M 从上电那一刻起,就以 9600 波特、8-N-1 送出纯文本的 NMEA 语句 —— GGA、GLL、GSA、GSV、RMC、VTG 和 TXT,不管有没有定位都照送。TinyGPSPlus(作者 Mikal Hart,从 Library Manager 安装)负责把这些逗号分隔的语句变成数字:原始纬度的格式是 ddmm.mmmm,不是十进制度,而且每一条语句都带一个校验和可以核对。

这些语句还会用纯文本报出定位状态,这比看指示灯靠谱得多。在 $GPRMC 里,时间后面紧接着的是一个字母 —— V 代表 void,也就是还没有位置,首次定位那一刻会变成代表 active 的 A —— 而 $GPGGA 用一个定位质量数字说同一件事,定位前是 0,定位后是 1。没有定位的模块,这两条照样每秒送一次,只是位置字段留空。

/*
  NEO-6M GPS -> Arduino Uno:实时坐标显示在串口监视器上。
  库:TinyGPSPlus,作者 Mikal Hart(Library Manager 安装)。

  接线只接收,而且 TX/RX 必须交叉:
    GPS VCC -> Uno 5V     GPS GND -> Uno GND     GPS TX -> Uno D4
  Uno D3 -> GPS RX 是可选的,而且要加分压(见正文)。

  串口监视器用 115200,模块用 9600。两个数字不一样是故意的。
*/
#include <SoftwareSerial.h>
#include <TinyGPS++.h>

const uint8_t  UNO_RX_PIN = 4;      // Uno 在这里听    <- 模块 TX
const uint8_t  UNO_TX_PIN = 3;      // Uno 在这里说    -> 模块 RX(可选)
const uint32_t GPS_BAUD   = 9600;   // NEO-6M 出厂默认,8-N-1

SoftwareSerial gpsSerial(UNO_RX_PIN, UNO_TX_PIN);
TinyGPSPlus gps;

uint32_t lastReport = 0;

void setup() {
  Serial.begin(115200);
  gpsSerial.begin(GPS_BAUD);
  Serial.println(F("NEO-6M starting. Give it sky - indoors it will never fix."));
}

void loop() {
  // 每一圈都要把模块的数据抽干。漏掉这一步,64 字节的缓冲区就会溢出。
  while (gpsSerial.available() > 0) {
    gps.encode(gpsSerial.read());
  }

  if (millis() - lastReport < 2000) return;
  lastReport = millis();

  // 故障 1:什么都收不到 -> 接线或波特率的问题,绝不是天空的问题。
  if (gps.charsProcessed() < 10) {
    Serial.println(F("NO DATA on D4 - check GPS TX -> D4 (crossed) and 9600 baud"));
    return;
  }

  Serial.print(F("chars "));
  Serial.print(gps.charsProcessed());
  Serial.print(F("  bad-checksum "));
  Serial.print(gps.failedChecksum());
  Serial.print(F("  sats "));
  if (gps.satellites.isValid()) {
    Serial.print(gps.satellites.value());
  } else {
    Serial.print(F("--"));
  }

  // 故障 2:语句在流但没有位置 -> 天线和天空的问题,不是接线。
  if (!gps.location.isValid()) {
    Serial.println(F("  NO FIX YET - data is flowing, so the module is alive"));
    return;
  }

  Serial.print(F("  lat "));
  Serial.print(gps.location.lat(), 6);
  Serial.print(F("  lng "));
  Serial.print(gps.location.lng(), 6);
  Serial.print(F("  age "));
  Serial.print(gps.location.age());
  Serial.print(F(" ms  alt "));
  Serial.print(gps.altitude.meters(), 1);
  Serial.println(F(" m"));
}

在 Uno 上,这份代码编译出 8,434 字节 Flash —— 32 KB 的 26% —— 以及 518 字节静态 RAM。两个波特率分开是值得的:SoftwareSerial 的接收缓冲区只存 64 字节,而模块在 9600 波特下每秒最多送出 960 字节。用 9600 打印一行 110 个字符的报告,会占住板子大约 115 ms,这段时间里又有大约 110 字节涌进来,缓冲区就溢出了;换成 115200,同样一行只要大约 10 ms,排队的不到十几个字节。把打印出来的纬度和经度直接贴进 Google Maps,就能核对。

加上 OLED 就是再多四根线 —— GND、VCC、SCL 接 A5、SDA 接 A4,注意它排针上排在最前面的是 GND,不是 VCC —— 然后用地址 0x3C 的 Adafruit_SSD1306 把同一个 gps.location.lat() 的值打上去。在 Uno 上这不是白拿的:那个库会在 RAM 里留一整块 128 × 64 的帧缓冲,占掉 ATmega328P 那 2 KB 里的 1 KB,所以字符串常量要用 F() 包起来,重画也要省着来 —— 走 I2C 刷一整帧要几十毫秒,而 64 字节的串口缓冲区六十七毫秒就满了。改成记录到 SD 卡,是很自然的下一个作品 —— 一条 GPS 轨迹不过是一行行带时间戳的数据,文件那一头由我们的 Arduino SD 卡数据记录教程负责。

一颗健康、但还没定位的 NEO-6M 是什么样子?

NEO-6M 给的是两个互相独立的健康信号,把它们分开来读,「模块是不是坏了」的慌张就变成两分钟的诊断。定位指示灯由 TIMEPULSE 输出驱动,u-blox 的协议规范里它的默认设定是按 GPS 时间每秒输出一个 100 ms 的脉冲,而在没有定位时完全不输出 —— 而接收机在解出自己的钟差之前,没办法把任何东西对齐到 GPS 时间,解钟差又必须先有定位。所以这颗灯不是电源灯:暗着或者常亮,就是没定位时的正常状态,而 1 Hz 的闪烁是在宣布坐标已经有效。

你看到的 它的意思 该往哪里查
串口监视器上什么都没有 模块的数据根本没到 Uno TX/RX 交叉了吗?模块的 TX 接在代码声明的那支引脚上吗?监视器是 115200、gpsSerial 是 9600 吗?VCC 和 GND 都接上了吗?
语句在流,没有坐标,指示灯是暗的 一颗完全健康、只是没有天空的模块 到户外去等 —— 笔电一起带出去,或者装上 OLED 就不必带。顺手确认天线那条细线是扣紧的,然后盯着 $GPRMC,等它的 V 变成 A
卫星数在涨,还是没定位 它在工作 —— 只是卫星还不够,或者卫星的轨道数据还没收齐 再等久一点,把天空视野让得更开。四颗是最低要求:三颗定出位置,第四颗解接收机自己的钟差
坐标出现了,然后不动了 信号丢了,保留的是最后一次已知的值 看 age 那个数字在不在往上爬;超过几秒就代表它已经停止更新
屏幕上是乱码 波特率对不上,或者缓冲区溢出 把串口监视器的下拉框设成 115200 —— 用默认的 9600 只会印出乱码;gpsSerial.begin() 里保持 9600;TX 那根杜邦线要短;不要在 loop() 里卡住
两台笔电的卡通对比图:左边是一片漆黑的空白屏幕,配橙色叉号和一根松脱的黄色杜邦线;右边屏幕上满是抽象的灰色数据行,配青色勾号,线接着一块蓝色 GPS 模块和银色陶瓷天线
监视器一片空白,代表线接错了。数据在流却没有坐标,代表线接对了,不对的是天空。

我们从真实客户那里常看到的错误

用指示灯来判断模块的死活。有位客户直白地告诉我们:“Helo my gps cant turn on”,接着是 “i tried with uno, nano”,然后又一次 “My gps cant turn on”。换板子永远回答不了这个问题 —— Uno 和 Nano 跑这份代码用的是同样的引脚 —— 而两个真正的原因根本不是故障:NEO-6M 在室内定不了位,所以它的灯永远不闪;还有 TX 必须交叉接到 RX。在重新接线、或者怀疑板子之前,先把这两件事查一遍。

天线没有扣到位。板子角上那个金色的 u.FL 座子很小,插到位时会有清楚的一下轻脆咔嗒。只插了一半,模块照样启动、照样吐语句 —— 只是卫星数永远累积不起来,看上去和信号差一模一样。拔的时候要捏住插头本体,绝对不要拽同轴线:先被扯下来的会是板上的座子。

用一个电量将尽的充电宝供电。捕获阶段正是接收机耗电最凶的时候,搜星搜到一半电压塌下去,冷启动就要从零重来。第一次定位,笔电的 USB 口或者一个正经的适配器是更保险的电源。

在桌面上期待无人机级别的表现。这就是飞控上用的同一块板 —— 但在无人机上,它拿到的是一路空闲的硬件 UART,还有一根架在高杆上的天线,而不是一片平躺在你手底下的贴片天线。

常见问题

我的 NEO-6M 指示灯为什么不闪?

因为它还没有定位,而在室内这是正常现象,不是故障。这颗灯跟着 timepulse 输出走,而那个输出要等接收机拿到有效的 GPS 时间才开始 —— GPS 时间是随第一次定位一起到的。暗着或者常亮,代表它还在搜。如果 NMEA 语句已经跑到你的串口监视器上,那么模块要的是天空,不是维修。

NEO-6M 定位一次要多久?

在开阔天空下,u-blox 给的是冷启动 27 秒、热启动 1 秒。而第一次定位常常要好几分钟,因为卫星是以每秒 50 比特播发的,轨道数据每 30 秒才重复一次。第一次就把它放在户外通着电等十分钟;之后的启动会快得多。

NEO-6M 接 Arduino Uno 需要电平转换吗?

只是读它的话,不需要。模块 TX 输出的 3.3 V 越过了 Uno 3.0 V 的逻辑高门限,所以一根线直连就能用。只有模块的 RX 脚那一侧需要分压,而且只有在你要给它发配置命令时才需要 —— 那个输入的额定电平是它内部那条 3.3 V 电源轨,不是 5 V。

可以用 0 和 1 脚,不用 SoftwareSerial 吗?

可以,但你会失去串口监视器:0 和 1 脚就是 USB 芯片用的那一路硬件 UART,模块和你的电脑会在同一条线上一起喊。用 SoftwareSerial 接到其他任何一支数字引脚,两边都保得住。

NEO-6M 和 NEO-8M 模块有什么区别?

GY-NEO-8M 上是一颗 u-blox NEO-M8N,它能同时接收好几个卫星星座,而不是只有 GPS。视野里的卫星更多,意味着定位更快也更稳,在天空被挡掉一半的地方差别最明显。引脚定义、9600 波特的输出和上面那份代码都不变 —— 是可以直接换上去的升级。

最后更新:2026 年 8 月。有问题?WhatsApp 联系我们

Leave a Reply

Your email address will not be published. Required fields are marked *