Learn how to interface the NEO-6M GPS module with the ESP32 to get GPS data. You’ll learn how to get raw GPS data and interpret NMEA sentences. Finally, we’ll show you how to easily get latitude, longitude, altitude, speed, and UTC time using the TinyGPSPlus library. The ESP32 will be programmed using Arduino IDE.
In summary, in this tutorial you’ll learn how to:
- Wire the NEO-6M GPS module to the ESP32 via serial
- Get raw GPS data
- Parse raw data to obtain selected and readable GPS information
- Get your current location
We have a similar guide for the ESP8266 board with NEO-6M GPS Module (Arduino IDE)
Table of Contents
Throughout this tutorial, we’ll cover the following subjects:
- Introducing the NEO-6M GPS Module
- Wiring the NEO-6M GPS Module to the ESP32
- Getting Raw GPS Data – Testing the NEO-6M GPS Module with the ESP32
- NMEA Sentences
- Parsing NMEA Sentences with TinyGPSPlus Library
- Getting GPS Data Using the NEO-6M GPS Module and the TinyGPSPlus Library
Introducing the NEO-6M GPS Module
The NEO-6M GPS module is a GPS receiver compatible with most microcontroller boards. It can get data about location, speed, altitude, and time.
It comes with a small backup battery, external EEPROM, and an LED signal indicator. This LED will start blinking when it gets a position fix.
Usually, these modules come with a GPS ceramic antenna.
But, you can change it to any other compatible antenna that might suit your project better. For example, I like to use the one at the right in the picture below because it is waterproof, and the antenna comes with a long cable which allows for more flexibility.
The NEO-6M GPS Module communicates with a microcontroller using serial communication protocol.
This module works with standard NMEA sentences. NMEA stands for National Marine Electronics Association, and in the world of GPS, it is a standard data format supported by GPS manufacturers.
NEO-6M GPS Module Features
In summary:
- This module has an external antenna and built-in EEPROM.
- Interface: RS232 TTL
- Power supply: 3V to 5V
- Default baudrate: 9600 bps
- Works with standard NMEA sentences
Where to buy?
You can get the NEO-6M GPS module for a price between $5 to $20. We recommend checking the NEO-6M GPS module page on Maker Advisor to compare the prices in different stores and find the best one.
You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!
Wiring the NEO-6M GPS Module to the ESP32
We’ll connect the NEO-6M GPS Module using the ESP32 default UART2 pins. You can use the following picture and table as a reference.
NEO-6M GPS Module | ESP32 |
VCC | 3V3 |
RX | TX2 (GPIO 17) |
TX | RX2 (GPIO 16) |
GND | GND |
Getting Raw GPS Data – Testing the NEO-6M GPS Module with the ESP32
To get raw GPS data you just need to start a serial communication with the GPS module and read the available data.
The following code establishes a serial communication with the GPS module and reads the available data.
/*********
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete instructions at https://RandomNerdTutorials.com/esp32-neo-6m-gps-module-arduino/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/
// Define the RX and TX pins for Serial 2
#define RXD2 16
#define TXD2 17
#define GPS_BAUD 9600
// Create an instance of the HardwareSerial class for Serial 2
HardwareSerial gpsSerial(2);
void setup(){
// Serial Monitor
Serial.begin(115200);
// Start Serial 2 with the defined RX and TX pins and a baud rate of 9600
gpsSerial.begin(GPS_BAUD, SERIAL_8N1, RXD2, TXD2);
Serial.println("Serial 2 started at 9600 baud rate");
}
void loop(){
while (gpsSerial.available() > 0){
// get the byte data from the GPS
char gpsData = gpsSerial.read();
Serial.print(gpsData);
}
delay(1000);
Serial.println("-------------------------------");
}
How Does the Code Work?
This sketch assumes you are using GPIO 16 and GPIO 17 as RX and TX serial pins to establish serial communication with the GPS module. If you’re using other pins you should edit that on the following lines:
// Define the RX and TX pins for Serial 2
#define RXD2 16
#define TXD2 17
Also, if your module uses a different default baud rate than 9600 bps, you should modify the code on the following line:
#define GPS_BAUD 9600
Then, we create an instance of the HardwareSerial to use UART 2 called gpsSerial.
// Create an instance of the HardwareSerial class for Serial 2
HardwareSerial gpsSerial(2);
In the setup(), we initiate the Serial Monitor.
// Serial Monitor
Serial.begin(115200);
Then, we initialize a serial communication with the GPS module.
// Start Serial 2 with the defined RX and TX pins and a baud rate of 9600
gpsSerial.begin(GPS_BAUD, SERIAL_8N1, RXD2, TXD2);
Serial.println("Serial 2 started at 9600 baud rate");
In the loop(), the code listens to the GPS serial port, and when data is received from the module, it is printed in the serial monitor.
void loop(){
while (gpsSerial.available() > 0){
// get the byte data from the GPS
char gpsData = gpsSerial.read();
Serial.print(gpsData);
}
delay(1000);
Serial.println("-------------------------------");
}
Testing the Code
Upload the code to your board.
Make sure the antenna is connected and that the module or antenna is placed outside or next to a window so that it can get data from the satellites.
The module’s blue LED will start blinking when it finds a position fix.
The Serial Monitor will display NMEA sentences with GPS data. When you run this sketch for the first time, it may take a few minutes until it can get a position fix. You’ll start getting actual data when the blue LED starts blinking.
You should get a bunch of information in the GPS standard language, NMEA. Each line you get in the serial monitor is an NMEA sentence.
NMEA stands for National Marine Electronics Association, and in the world of GPS, it is a standard data format supported by GPS manufacturers.
NMEA Sentences
NMEA sentences start with the $ character, and each data field is separated by a comma.
$GPRMC,110827.00,A,4107.32485,N,00831.79799,W,0.888,30.44,180724,,,A*4B
$GPVTG,30.44,T,,M,0.888,N,1.644,K,A*01
$GPGGA,110827.00,41XX.32485,N,00831.79799,W,1,07,0.99,123.1,M,50.1,M,,*48
$GPGSA,A,3,03,32,22,08,04,14,17,,,,,,2.25,0.99,2.02*0A
$GPGSV,3,1,11,3,11,22,26,296,29,27,01,142,,32,17,042,23*48
$GPGLL,4107.32485,N,00831.79799,W,110827.00,A,A*7F
There are different types of NMEA sentences. The type of message is indicated by the characters before the first comma.
The GP after the $ indicates it is a GPS position. The $GPGGA is the basic GPS NMEA message, that provides 3D location and accuracy data.
In the following sentence:
$GPGGA,110827.00,41XX.32485,N,008XX.XXXXX,W,1,07,0.99,123.1,M,50.1,M,,*48
- 110827 – represents the time at which the fix location was taken, 11:08:27 UTC
- 41XX.32845,N – latitude 41 deg XX.32845,N
- 00831.79799,W – Longitude 008 deg XX.XXXXX′ W
- 1 – fix quality (0 = invalid; 1= GPS fix; 2 = DGPS fix; 3 = PPS fix; 4 = Real Time Kinematic; 5 = Float RTK; 6 = estimated (dead reckoning); 7 = Manual input mode; 8 = Simulation mode)
- 07 – number of satellites being tracked
- 0.99 – Horizontal dilution of position (less than one is ideal)
- 123.1, M – Altitude, in meters above the sea level
- 50.1, M – Height of geoid (mean sea level) above WGS84 ellipsoid
- empty field – time in seconds since last DGPS update
- empty field – DGPS station ID number
- *48 – the checksum data, always begins with *
The other NMEA sentences provide additional information:
- $GPGSA – GPS DOP and active satellites
- $GPGSV – Detailed GPS satellite information
- $GPGLL – Geographic Latitude and Longitude
- $GPRMC – Essential GPS pvt (position, velocity, time) data
- $GPVTG – Velocity made good
You can use this Online NME Analyser and paste your sentences there to interpret the GPS data.
However, the easiest way to get and interpret the GPS data you want is to parse your NMEA sentences directly in the code. For that, we can use the TinyGPSPlus library that provides methods to extract data from the NMEA sentences easily.
Parsing NMEA Sentences with TinyGPSPlus Library
The TinyGPSPlus library makes it simple to get GPS data in a format that is easy to understand. You can click here for more information about the TinyGPSPlus Library.
Installing the TinyGPSPlus Library
In the Arduino IDE, go to Sketch > Include Library > Manage Libraries or click on the Libary Manager icon at the left sidebar.
Search for TinyGPSPlus and install the library by Mikal Hart.
Getting GPS Data Using the NEO-6M GPS Module and the TinyGPSPlus Library
The following code shows how to get GPS data using the TinyGPSPlus library. We’ll get date, time, speed, altitude, number of visible satellites, and HDOP (a measurement of how precise the signal is).
/*********
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete instructions at https://RandomNerdTutorials.com/esp32-neo-6m-gps-module-arduino/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/
#include <TinyGPS++.h>
// Define the RX and TX pins for Serial 2
#define RXD2 16
#define TXD2 17
#define GPS_BAUD 9600
// The TinyGPS++ object
TinyGPSPlus gps;
// Create an instance of the HardwareSerial class for Serial 2
HardwareSerial gpsSerial(2);
void setup() {
// Serial Monitor
Serial.begin(115200);
// Start Serial 2 with the defined RX and TX pins and a baud rate of 9600
gpsSerial.begin(GPS_BAUD, SERIAL_8N1, RXD2, TXD2);
Serial.println("Serial 2 started at 9600 baud rate");
}
void loop() {
// This sketch displays information every time a new sentence is correctly encoded.
unsigned long start = millis();
while (millis() - start < 1000) {
while (gpsSerial.available() > 0) {
gps.encode(gpsSerial.read());
}
if (gps.location.isUpdated()) {
Serial.print("LAT: ");
Serial.println(gps.location.lat(), 6);
Serial.print("LONG: ");
Serial.println(gps.location.lng(), 6);
Serial.print("SPEED (km/h) = ");
Serial.println(gps.speed.kmph());
Serial.print("ALT (min)= ");
Serial.println(gps.altitude.meters());
Serial.print("HDOP = ");
Serial.println(gps.hdop.value() / 100.0);
Serial.print("Satellites = ");
Serial.println(gps.satellites.value());
Serial.print("Time in UTC: ");
Serial.println(String(gps.date.year()) + "/" + String(gps.date.month()) + "/" + String(gps.date.day()) + "," + String(gps.time.hour()) + ":" + String(gps.time.minute()) + ":" + String(gps.time.second()));
Serial.println("");
}
}
}
How Does the Code Work?
You start by importing the TinyGPSPlus library.
#include <TinyGPS++.h>
#include <SoftwareSerial.h>
Then, you define the UART 2 RX and TX pins and the GPS baud rate. If your board uses different UART 2 pins, or if the GPS module uses a different baud rate, you can modify them on the following lines.
// Define the RX and TX pins for Serial 2
#define RXD2 16
#define TXD2 17
#define GPS_BAUD 9600
Then, you create a TinyGPS++ object:
TinyGPSPlus gps;
Create an instance of the HardwareSerial class for Serial 2 called gpsSerial.
HardwareSerial gpsSerial(2);
In the setup(), initialize the Serial Monitor and the serial communication with the GPS module.
void setup() {
// Serial Monitor
Serial.begin(115200);
// Start Serial 2 with the defined RX and TX pins and a baud rate of 9600
gpsSerial.begin(GPS_BAUD, SERIAL_8N1, RXD2, TXD2);
Serial.println("Serial 2 started at 9600 baud rate");
}
In the loop() is where you request the information. Parse the data from the GPS module into the TinyGPS++ object using the encode() method as follows.
while (gpsSerial.available() > 0) {
gps.encode(gpsSerial.read());
}
Then, you can query the gps object to see if any data fields have been updated:
if (gps.location.isUpdated()) {
If there is new data, we can get it as follows:
Latitude | gps.location.lat() |
Longitude | gps.location.lng() |
Speed (km/h) | gps.speed.kmph() |
Altitude (meters) | gps.altitude.meters() |
HDOP | gps.hdop.value() |
The number of visible satellites | gps.satellites.value() |
Year | gps.date.year() |
Month | gps.date.month() |
Day | gps.date.day() |
Hour | gps.time.hour() |
Minutes | gps.time.minute() |
Seconds | gps.time.second() |
In the code, we get the data and print all the information in the Serial Monitor.
Serial.print("LAT: ");
Serial.println(gps.location.lat(), 6);
Serial.print("LONG: ");
Serial.println(gps.location.lng(), 6);
Serial.print("SPEED (km/h) = ");
Serial.println(gps.speed.kmph());
Serial.print("ALT (min)= ");
Serial.println(gps.altitude.meters());
Serial.print("HDOP = ");
Serial.println(gps.hdop.value() / 100.0);
Serial.print("Satellites = ");
Serial.println(gps.satellites.value());
Serial.print("Time in UTC: ");
Serial.println(String(gps.date.year()) + "/" + String(gps.date.month()) + "/" + String(gps.date.day()) + "," + String(gps.time.hour()) + ":" + String(gps.time.minute()) + ":" + String(gps.time.second()));
Serial.println("");
Testing the Code
Upload the code to your ESP32 board. Open the Serial Monitor at a baud rate of 115200. Make sure your GPS module is placed outside or next to a window to get data from satellites.
You’ll get GPS data on the Serial Monitor about your current location, speed, altitude, number of visible satellites HDOP, and time.
HDOP stands for Horizontal Dilution of Precision. This is a measurement of position-fixing accuracy. The higher the HDOP value is, the less accurate the position fix will be. Ideally, you should get a value lower than 2. A lower value means a better accuracy.
Now, you can take this project further and display the data on an OLED display or a TFT display.
Wrapping Up
In this tutorial, you learned how to use the NEO-6M GPS Module with the ESP32 to get GPS data: latitude, longitude, altitude, speed, date, time, number of satellites, and more.
Interfacing the NEO-6M GPS module with the ESP32 is quite easy—you just need to establish a serial communication with the sensor and read the available data.
The accuracy of the data will depend on where the module is placed and the antenna used. For better performance, the sensor should be placed outside or next to a window and away from narrow streets with tall buildings. The higher the number of visible satellites, the more accurate the position will be.
We hope you’ve found this tutorial useful. We have guides for more than 25 modules and sensors with the ESP32, you can check them below:
If you want to learn more about the ESP32, check out our resources:
Thanks for reading.
Rui;
is the altitude data related to the WGS84 or is it the real altitude of my site please ?
Is digital terrain elevation data DTED taken into consideration ?
Thanks
Hi Rui;
I have been playing around with the M8N for a while using both UNO R3 and ESP-32 and was curious why you went with the older generation 6M instead of a more current product? Price point is not much different.
Was wondering also which antenna you used?
Thanks
Hi.
I used this one because it is the module we’re currently have.
I’ll search for that one that you recommend.
I used both antennas that I show in the tutorial. But, I prefer the waterproof with the longer cable.
Regards,
Sara
Thanks Sara.
As for the antenna, what I was really asking was did you have a make / model of the one with the longer cable?
You can find the antenna on amazon by searching for “GPS antenna”. Then, you also need an SMA to uFL adapter. Some packages already come with the adapter.
Let me know if this answers your question.
Regards.
Sara
Here is a link to Andreas Spiess’s YouTube channel.
Comparison of precision between Neo-M8N and Neo-6M Modules.
youtube.com/watch?v=FmdG66kTfsI
Hi Rui,
How fast the speed monitoring can happen with a arduino nano and this gps module? Are there better accurate variations of the gps module that can be used to find a faster fix, accurate and fast speed monitoring purpose? Thank you for the wonderful and easy to understand tutorial.
Hi Sarah
In the sketch where you use TinyGPS++
should unsigned long start = millis()
not be in setup, instead of being in the main loop.
and then use the if else statement.
if (millis() – start < 200) {
}
else{
while (gpsSerial.available() > 0) {
and then
start = millis();
after the last serial.println
hi Sara
of course it has to be
if (millis() – start < 1000) {
if it is a one second delay.
hi Rui and Sara
Is this supposed to be a one second delay,
between each reading.
void loop() {
// This sketch displays information every time a new sentence is correctly encoded.
unsigned long start = millis();
while (millis() – start < 1000) {
while (gpsSerial.available() > 0) {
gps.encode(gpsSerial.read());
}
if (gps.location.isUpdated()) {
Thanks JB. I have also noted the altitude anomaly in these receivers and attributed it to orbital variation and / or atmospheric disturbances. It can vary 10’s of meters over a very short period.
Another good reference I have found is on MIT App Inventor – Exploring with LocationSensor in AI2.
Hi Floyd.
I thank you for your reply.
Hi JB,
Yesterday I decided to run my own evaluation to see how it stacked up against Andreas Spiess’s work. I put U-Center to work and ran the deviation map for a 12 hour period overnight. My M8N is connected using a DSD Tech SH-U06B USB to TTL Serial Adapter and a Bingfu antenna affixed to the inside of a window in my house which faces E/NE (not ideal but adequate for this initial experiment). While the receiver was seeing multiple satellites, those with the strongest and best C/NO signals were located in the East-North quadrant. No surprise there. I was seeing an average of 18 satellites at any given time with an almost even mix of GPS and GLONASS. There were no BeiDou but I think that is an option I can enable in the M8. I will have to look into that further. With U-Center it is easier to see and begin to understand why the excursions in readings take place. Back to the results though…
This morning I was pleasantly surprised to see my 12 hour evaluation using my less than ideal setup showed average position was within 10m radius (99%) and the remaining were within 15m.
I will try to get my antenna set up in a better location and run this test again at some later date.
Hi Floyd.
Thanks info.
the advantage of the M8 is that it should be able to see more than US satellites, I think.
Although the Neo-6M is a rather old module, I think it works quite well.
Maybe it’s consuming too much power.
Agreed.
Even though the M8N is capable of reception from the Russian and Chinese satellites it seems to rely primarily on the GPS system. Since I posted earlier I have improved my setup by moving the antenna out and away from the house so there is a fairly clear and unobstructed view of the East, 0-180 degrees. Signal strength increased almost twofold with a corresponding improvement in the C/NO. The house still obstructs the view to the West but just this one improvement seems to have cut the deviation to half or better. It will need to run for several more hours to get a better comparison to last night’s run but so far it’s holding to within 5m.
It all comes down to quality of reception.
Hi Floyd.
5m deviation, I wouldn’t expect better than that.
I use the Esp32 C3 mini module with the gps module.
I have added a button.
every time the button changes position
a log point is saved in a file in the LittleFs file system.
I also added wifi,
so it is possible to download the log file.
You can see an image and code in the link below
if you should be interested.
drive.google.com/drive/folders/1yZrISHWsS-RxwEnvy-fmbnBvmO1kIYpH?usp=sharing
Hi.
I have tried the ATGM336H GPS module.
And the ATGM336H module sees quite a few more satellites
than the NEO-6M module.
Even in my room the module can see 23 satellites.
Impressive I think.
I don’t know how many of them the module uses.