The NEO-6M is the GPS module in most Indian kits, sold as the GY-NEO6MV2 with a square patch antenna. It sends standard NMEA sentences at 9600 baud, and TinyGPSPlus turns them into latitude, longitude, satellites and time.
The ESP32 DevKit V1 has a spare hardware UART for this: Serial2 on GPIO16 (RX2) and GPIO17 (TX2). Core 3.x needs those pins named in Serial2.begin(). The module’s logic is 3.3 V, so it wires straight in. The sketch prints the position and Indian Standard Time every two seconds once it has a fix.
Wiring
| ESP32 DevKit V1 | NEO-6M GPS module (GY-NEO6MV2) | Wire | Notes |
|---|---|---|---|
| VIN | VCC | red | The module has its own 3.3 V regulator; 5 V on VCC gives it headroom. 3V3 also works on most boards. |
| GND | GND | black | |
| RX2 | TX | yellow | GPS TX → ESP32 RX2 (GPIO16). The GPS chip runs at 3.3 V, so no divider. |
| TX2 | RX | green | ESP32 TX2 (GPIO17) → GPS RX. Only needed to change GPS settings; reading works without it. |
3.3–5 V (on-board 3.3 V regulator) part, 3.3 V board. The module’s TX is a 3.3 V signal, which every board reads fine. Its RX pin is 3.3 V logic: from a 5 V Arduino TX, add a 1 kΩ / 2 kΩ divider. You only need RX if your sketch sends configuration commands.
Board quirks
- Arduino-ESP32 3.x moved the default Serial2 pins to RX 4 / TX 25. Without
Serial2.begin(9600, SERIAL_8N1, 16, 17)the sketch hears nothing on the pins printed RX2/TX2. - The ESP32 has three hardware UARTs, so there’s no SoftwareSerial here. UART0 (TX0/RX0) stays free for USB and uploads.
- First fix after power-up (cold start) takes 1–5 minutes with a clear view of the sky. Indoors, under a concrete roof, it often never gets one. The blue LED blinks once a second when it has a fix.
- GPS time is UTC. The sketch adds 5 h 30 min for IST. The date can be a day off between 18:30 and midnight UTC if you print it — add the offset to the date as well.
- The backup cell on the module keeps the almanac, so warm starts take seconds. If the cell is flat (common on cheap boards), every start is a cold start.
Code
// NEO-6M GPS (GY-NEO6MV2) on an ESP32 DevKit V1 with TinyGPSPlus.
// GPS TX → RX2 (GPIO16), GPS RX ← TX2 (GPIO17), VCC → VIN, GND → GND. Module logic is 3.3 V.
#include <TinyGPSPlus.h>
constexpr uint8_t RX_PIN = 16, TX_PIN = 17;
TinyGPSPlus gps;
void setup() {
Serial.begin(115200);
// Name the pins: arduino-esp32 3.x would otherwise put Serial2 on RX 4 / TX 25.
Serial2.begin(9600, SERIAL_8N1, RX_PIN, TX_PIN);
Serial.println("NEO-6M on Serial2 — first fix takes 1–5 min outdoors");
}
void loop() {
while (Serial2.available()) gps.encode(Serial2.read());
static uint32_t last = 0;
if (millis() - last < 2000) return;
last = millis();
if (gps.charsProcessed() < 10 && millis() > 5000) {
Serial.println("No data from GPS — swap TX/RX, check 9600 baud and power");
return;
}
if (!gps.location.isValid()) {
Serial.printf("Waiting for fix… satellites %lu\n", (unsigned long)gps.satellites.value());
return;
}
// GPS time is UTC; India is UTC+5:30.
int mins = (gps.time.hour() * 60 + gps.time.minute() + 330) % 1440;
Serial.printf("Lat %.6f Lon %.6f Sats %lu HDOP %.1f %02d:%02d:%02d IST\n", gps.location.lat(),
gps.location.lng(), (unsigned long)gps.satellites.value(), gps.hdop.hdop(), mins / 60, mins % 60,
gps.time.second());
}// NEO-6M GPS (GY-NEO6MV2) on an ESP32 DevKit V1 with TinyGPSPlus.
// GPS TX → RX2 (GPIO16), GPS RX ← TX2 (GPIO17), VCC → VIN, GND → GND. Module logic is 3.3 V.
#include <TinyGPSPlus.h>
constexpr uint8_t RX_PIN = 16, TX_PIN = 17;
TinyGPSPlus gps;
void setup() {
Serial.begin(115200);
// Name the pins: arduino-esp32 3.x would otherwise put Serial2 on RX 4 / TX 25.
Serial2.begin(9600, SERIAL_8N1, RX_PIN, TX_PIN);
Serial.println("NEO-6M on Serial2 — first fix takes 1–5 min outdoors");
}
void loop() {
while (Serial2.available()) gps.encode(Serial2.read());
static uint32_t last = 0;
if (millis() - last < 2000) return;
last = millis();
if (gps.charsProcessed() < 10 && millis() > 5000) {
Serial.println("No data from GPS — swap TX/RX, check 9600 baud and power");
return;
}
if (!gps.location.isValid()) {
Serial.printf("Waiting for fix… satellites %lu\n", (unsigned long)gps.satellites.value());
return;
}
// GPS time is UTC; India is UTC+5:30.
int mins = (gps.time.hour() * 60 + gps.time.minute() + 330) % 1440;
Serial.printf("Lat %.6f Lon %.6f Sats %lu HDOP %.1f %02d:%02d:%02d IST\n", gps.location.lat(),
gps.location.lng(), (unsigned long)gps.satellites.value(), gps.hdop.hdop(), mins / 60, mins % 60,
gps.time.second());
}Library: TinyGPSPlus (Mikal Hart) — compiled with TinyGPSPlus 1.0.3.
Expected serial output
NEO-6M on Serial2 — first fix takes 1–5 min outdoors
Waiting for fix… satellites 0
Waiting for fix… satellites 3
Lat 12.971599 Lon 77.594566 Sats 7 HDOP 1.2 14:32:08 IST
Lat 12.971602 Lon 77.594561 Sats 8 HDOP 1.1 14:32:10 ISTNEO-6M on Serial2 — first fix takes 1–5 min outdoors Waiting for fix… satellites 0 Waiting for fix… satellites 3 Lat 12.971599 Lon 77.594566 Sats 7 HDOP 1.2 14:32:08 IST Lat 12.971602 Lon 77.594561 Sats 8 HDOP 1.1 14:32:10 IST
Common errors
“No data from GPS”
TX/RX crossed the wrong way (GPS TX must go to RX2), Serial2 started without the pin numbers, or the wrong baud — the NEO-6M ships at 9600.
Data arrives but never a fix
Take it outdoors or to a window, antenna side up, and wait 5 minutes. Keep it away from the ESP32’s Wi-Fi antenna and USB cables, which add noise.
Satellites seen but location jumps by tens of metres
Normal with HDOP above 2 or near tall buildings. Wait for more satellites, or average a few readings.