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 针公排针上掰下四针焊上去,是这个作品的第一步,排在任何接线之前;技巧看我们的排针焊接教程。
材料清单 —— NEO-6M GPS 模块接 Arduino Uno,坐标显示在串口监视器上
选配 —— 桌面显示、数据线与焊接工具
0.96 寸 OLED 能把这个作品变成一台手持机,不用笔电也看得到坐标;它用的是 Uno 的 I2C 引脚 A4 和 A5,而这个作品正好没占用这两支。USB A 转 B 线就是 Uno 那个方形大插座认的形状 —— 抽屉里没有这个形状的线就加一条。烙铁和焊锡是给模块那四根排针用的;已经有烙铁就跳过。
焊好之后,模块身上是公针,Uno 那边是母排座,而 OLED 出厂就已经装好公针 —— 所以一包公对母杜邦线就能把两者直接接上板子,中间不用面包板。

数据手册说 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 信号能穿过水滴,倒是湿透的树叶和混凝土会把它吸掉。

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() 里卡住 |

我们从真实客户那里常看到的错误
用指示灯来判断模块的死活。有位客户直白地告诉我们:“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 联系我们。



GPS Module GY NEO 6M/8M with Ceramic Antenna Time and Location Tracking - For Arduino IOT Project - GY-NEO-6M GPS MODULE
Arduino Uno Compatible SMD UNO R3 with Type B Cable - ATMEGA328P with CH340G-Microcontroller Project
2.54mm Pin Header Female Male Single Row Header 40P Connector Black White Red Yellow Green Blue - 40P MALE HEADER (BLACK)
40pcs Dupont Wire 10cm 20cm 30cm for Breadboard DIY Experiment Jumper Wire Breadboard wire - DUPONT WIRE M-F 20CM
0.96‘ OLED Display Module Blue White Screen I2C IIC Serial 128X64 LCD Display Board - 0.96' OLED WHITE
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)
80W Electronics Soldering Iron with LCD Display Ajustable Temperature Microconstroller Electronic Project
50g Soldering Wire Lead with Flux Core 1mm Diameter