Component · Sensor
NEO-6M GPS Module
A NEO-6M gives an outdoor robot absolute position over a serial port. How to read NMEA, why HDOP matters more than the accuracy figure, and what it cannot do.
What it is
A GNSS receiver: a chip that listens for the timing signals broadcast by a constellation of satellites and solves for its own position from the differences between their arrival times. The NEO-6M is the module most hobby robots meet first — a u-blox receiver, a ceramic patch antenna, and a serial port.
It is the only sensor on a robot that reports an absolute position. Everything else on this site — encoders, an IMU, a reflectance array — measures a change or a local feature. GPS is what stops odometry drift from being unbounded, and it is why an outdoor rover can be given a destination a kilometre away.
How it works
You do not talk to it. It talks to you, at 9600 baud, in NMEA 0183 sentences — one comma-separated line per message, several times a second.
Two of them carry everything a robot needs:
$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,... fix quality, sats, HDOP
$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,... validity, position, speed, course
Read the fix, then gate it. The status field in RMC is A for active or V for void, and the fix quality in GGA is 0 when there is no solution — in both cases the sentence still carries plausible-looking coordinates. The HDOP field is the one that catches the subtler failure: it describes satellite geometry, and a bad geometry yields a confident answer tens of metres out.
Latitude arrives as degrees-and-decimal-minutes (4807.038 is 48° 07.038′), which is not degrees. Convert once, then convert again into metres relative to a fixed origin — the arithmetic is here — and everything downstream works in units a controller understands.
When to use it
Use one whenever the robot works outdoors over distances longer than odometry survives: a waypoint rover, a lawnmower, a survey platform, an agricultural robot. Roughly, past thirty metres of driving, dead reckoning alone has accumulated more error than a GPS fix carries.
Do not reach for it indoors, at all, ever. And do not reach for it for precision: 2.5 m is a long way for a robot that is 30 cm wide. GPS tells you which part of the field you are in; a wheel encoder tells you where you are within a metre of that. Use both, fused.
The upgrade path, in order of value: a higher update rate first (5 or 10 Hz removes most of the staleness), then a multi-constellation module (GPS plus GLONASS or Galileo improves geometry and therefore HDOP), and only then RTK — which reaches centimetres and needs a base station, a data link, and a different budget.
Common gotchas
- Cold start is minutes, not seconds. With no almanac the receiver has to download one from the satellites at 50 bits per second. A robot that appears dead on first power-up outdoors is usually just waiting.
- It has no heading. Position only. Pair it with a magnetometer or a gyro; course over ground is meaningless below about 1 m/s.
- 5 V on RX kills 3.3 V modules. Check whether your breakout level-shifts. Module TX into a 5 V board is fine.
- The antenna needs sky, not a view. Metal above it — including your own chassis plate — costs you satellites. Mount it on top, flat, facing up.
- Never follow raw fixes. Dead reckon between them and correct on each one; following the fixes directly costs an order of magnitude in tracking error and four times the steering.
- Six decimal places is 11 cm — and more than a
floatholds. Convert to metres relative to a mission origin rather than doing arithmetic on raw degrees.
Pinout and wiring
| Pin | Name | What it does |
|---|---|---|
| 1 | VCC | 3.3–5 V. Check whether your breakout regulates — many NEO-6M boards do |
| 2 | RX | Module receives configuration. 3.3 V logic on a bare module |
| 3 | TX | Module transmits NMEA. Safe into a 5 V input |
| 4 | GND | Ground |
| 5 | PPS | One pulse per second, aligned to GPS time. Ignored by most projects |
| GPS | Arduino Uno | Note |
|---|---|---|
| VCC | 5 V | If your board has a regulator; 3.3 V if it does not |
| GND | GND | |
| TX | Pin 4 (SoftwareSerial RX) | Module talks, board listens |
| RX | Pin 3 via divider | Only needed if you configure the module |
You can leave RX unconnected entirely. A GPS module in its default state streams NMEA at
9600 baud without being asked, and for a robot that reads position and nothing else, that is
all you need. Connect RX only when you want to raise the update rate or silence unused
sentences.
Reading it without a library
#include <SoftwareSerial.h>
SoftwareSerial gps(4, 3); // RX from module TX, TX to module RX
char line[100];
uint8_t len = 0;
void setup() {
Serial.begin(115200);
gps.begin(9600);
}
void loop() {
while (gps.available()) {
char c = gps.read();
if (c == '\n') {
line[len] = '\0';
if (strncmp(line, "$GPRMC", 6) == 0) handleRmc(line);
len = 0;
} else if (len < sizeof(line) - 1) {
line[len++] = c;
}
}
}
void handleRmc(char *s) {
// $GPRMC,time,status,lat,N/S,lon,E/W,speed,course,date,...
char *field[13] = {nullptr};
uint8_t n = 0;
for (char *tok = strtok(s, ","); tok && n < 13; tok = strtok(nullptr, ",")) field[n++] = tok;
if (n < 7 || field[2][0] != 'A') return; // 'V' = void. Coordinates are still present
// and still meaningless. Gate on this.
Serial.print(toDegrees(field[3], field[4][0]), 6);
Serial.print(' ');
Serial.println(toDegrees(field[5], field[6][0]), 6);
}
// NMEA gives ddmm.mmmm — degrees and DECIMAL MINUTES, not decimal degrees.
double toDegrees(const char *ddmm, char hemisphere) {
double raw = atof(ddmm);
int deg = (int)(raw / 100);
double minutes = raw - deg * 100;
double result = deg + minutes / 60.0;
return (hemisphere == 'S' || hemisphere == 'W') ? -result : result;
}
The two lines that matter most are the ones people skip.
field[2][0] != 'A' is the validity gate. A receiver with no fix still emits perfectly
well-formed RMC sentences containing coordinates — usually the last known position, or zeros.
Code that does not check the status flag will confidently navigate to the middle of the
Atlantic, which is where 0°N 0°E is.
toDegrees exists because 4807.038 is not 4807.038 degrees, and it is not 48.07038
degrees either. It is 48° 07.038′, which is 48.1173°. Treating NMEA coordinates as decimal
degrees is the most common GPS bug there is, and the error is large enough to be obvious —
which is the only good thing about it.
For real projects, use TinyGPS++. It handles every sentence type, checksums, and the edge cases. The manual version above is worth running once so you know what it is doing.
Accuracy: what the numbers actually mean
| Figure | Typical NEO-6M | What it means |
|---|---|---|
| Horizontal accuracy | ~2.5 m CEP | Half of all fixes fall within 2.5 m. Half do not |
| Update rate | 1 Hz default, 5 Hz configurable | At 1 m/s, a 1 Hz fix is up to a metre stale before you read it |
| Cold start | ~27 s, minutes with no almanac | Nothing is wrong; it is downloading at 50 bits per second |
| Hot start | ~1 s | With backup power keeping the almanac alive |
| Velocity accuracy | ~0.1 m/s | Genuinely good — better than the position |
CEP is a median, not a bound. Half your fixes are worse than 2.5 m, and the tail is long near buildings. For a robot 30 cm wide, “somewhere within a few metres” is not a position — it is a neighbourhood.
HDOP is the field to watch. It describes satellite geometry rather than signal quality, and it is the thing that produces a confident answer that is tens of metres wrong:
| HDOP | Quality | Use it? |
|---|---|---|
| < 1 | Ideal | Yes |
| 1–2 | Excellent | Yes |
| 2–5 | Good | Yes, with caution |
| 5–10 | Moderate | Only as a slow correction |
| > 10 | Poor | Reject the fix |
Gate on HDOP as well as on the validity flag. A fix with HDOP of 12 is not a fix; it is a guess with a decimal point.
Static drift, and why it matters
Leave a GPS module perfectly still for ten minutes and log every fix. You will get a cloud several metres across. Nothing moved.
This is the single most important thing to see for yourself before writing any navigation code, because it explains why following raw fixes is hopeless. A controller fed that cloud chases it, and the robot weaves continuously while making little progress. Dead reckon with encoders between fixes and use each fix only to correct the slow drift, and the same controller tracks cleanly — the waypoint navigation tutorial has the numbers.
Configuring the module
Raising the update rate is the single most valuable change, and it is worth doing before anything else. u-blox modules take binary UBX commands:
// UBX-CFG-RATE: 200 ms measurement period -> 5 Hz
const uint8_t setRate5Hz[] = {
0xB5, 0x62, 0x06, 0x08, 0x06, 0x00,
0xC8, 0x00, // 200 ms
0x01, 0x00, // one measurement per solution
0x01, 0x00, // align to GPS time
0xDE, 0x6A // checksum
};
void setup() {
gps.begin(9600);
delay(100);
gps.write(setRate5Hz, sizeof(setRate5Hz));
}
At 5 Hz, 9600 baud becomes the bottleneck — the full NMEA sentence set does not fit in 200 ms. Either raise the baud rate to 38400, or disable the sentences you do not use (GSV in particular is verbose and reports satellite details no robot needs).
Note that on a NEO-6M the configuration does not persist without battery backup on the module. Send it at every startup.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| No data at all | TX/RX not crossed, or wrong baud | Module TX → board RX; try 9600 first |
| Data arrives, no fix ever | Indoors | GPS needs sky. A window is usually not enough |
| First fix takes minutes | Cold start with no almanac | Normal. Wait outdoors with a clear view |
| Position is 0, 0 | Using a void fix | Gate on the RMC status flag A |
| Position is wildly wrong | Treating ddmm.mmmm as degrees | Convert: deg + minutes/60 |
| Position jumps several metres | Normal receiver noise | Expected. Fuse with odometry; never follow raw fixes |
| Fix is confident but far out | Poor satellite geometry | Reject fixes with HDOP above ~5 |
| Fewer satellites than expected | Antenna obstructed | Mount on top, facing up, clear of the chassis plate |
| Module got hot and died | 5 V into a 3.3 V RX pin | Check whether your board level-shifts before wiring |
| Works, then stops at 5 Hz | 9600 baud cannot carry the sentences | Raise the baud rate or disable GSV |
Choosing a module
| Module | Constellations | Rate | Notes |
|---|---|---|---|
| NEO-6M | GPS | 1–5 Hz | The cheap default. Ageing but everywhere |
| NEO-M8N | GPS + GLONASS + Galileo | up to 10 Hz | Better geometry, better HDOP — the sensible upgrade |
| NEO-M9N | Multi-band capable | up to 25 Hz | Noticeably better in urban settings |
| ZED-F9P (RTK) | Multi-band | 20 Hz | Centimetre accuracy, with a base station and a data link |
The upgrade path is worth taking in order. A higher update rate removes most of the staleness problem and costs nothing. A multi-constellation receiver sees more satellites, which improves geometry and therefore HDOP — this is a bigger real-world improvement than the accuracy specification suggests, especially near buildings. RTK reaches centimetres and is transformative for a robot that has to follow a line in a field, but it needs a base station, a correction data link, and roughly ten times the budget.
One thing no GPS module gives you at any price: heading while stationary. Course over ground is derived from successive positions, so below about 1 m/s it is noise, and at zero speed it is meaningless. A robot that needs to know which way it is facing needs a magnetometer or a gyro regardless of how good its GPS is.
Explore the graph
Used in these builds
Projects, learning paths, and simulators that include the NEO-6M GPS Module.
Questions
NEO-6M GPS Module FAQ
How accurate is a NEO-6M really?
About 2.5 metres half the time, with a clear view of the sky and good satellite geometry — which is the number on the datasheet and an optimistic one for a robot driving beside a building or under trees. The important part is that it degrades into confident wrong answers rather than obvious failures, so gate every fix on the reported fix quality and on HDOP rather than trusting the position because it parsed.
What is HDOP and why does it matter more than the accuracy number?
Horizontal dilution of precision describes the geometry of the satellites currently in view. Satellites spread across the sky give a well-conditioned solution; satellites bunched together give a badly conditioned one, and the receiver still reports a position with full confidence. A HDOP under 2 is good and above about 2.5 is worth rejecting. It is the single most useful field in the GGA sentence and almost nobody reads it.
Can a GPS module tell my robot which way it is pointing?
No. A fix is a position, and a stationary robot facing north gives exactly the same fix as one facing south. Course over ground is computed from consecutive positions, so it only means anything while the robot is genuinely moving — below roughly 1 metre per second it is mostly noise. Outdoor rovers pair GPS with a magnetometer for absolute heading, a gyro for short-term heading, or both.
Why does my robot weave when following GPS waypoints?
Because a 1 Hz fix is both stale and noisy, and a path follower fed raw fixes lunges at each new one. Measured on the same course with the same controller, a 1 Hz fix at metre-scale noise turns 0.09 m of RMS cross-track error into 1.25 m and multiplies steering effort by four and a half. The fix is to dead reckon between fixes and use each new fix to correct that estimate rather than replace it.
Does it work indoors?
Essentially never, and that is not a fault. The signal from a GPS satellite arrives weaker than the thermal noise floor and needs an unobstructed sky; a roof stops it. Modules on a windowsill sometimes acquire and report large errors from multipath. Test outdoors, in the open, and budget a minute for a cold start before assuming anything is broken.
5 V or 3.3 V?
Most breakout boards carry a regulator and accept 5 V on VCC, but the module's own logic is 3.3 V. Feeding 5 V into RX on a board without level shifting is the usual way these die. Check your specific board, and if in doubt drive RX through a divider — the module's TX into a 5 V microcontroller is fine, since 3.3 V clears the logic-high threshold on an AVR.
Further reading