A NEO-6M gives a NodeMCU its position and accurate time — the core of vehicle trackers and location loggers that upload over Wi-Fi.
The GPS speaks serial at 9600 baud. The ESP8266’s only full hardware UART is busy with USB, so the module goes on SoftwareSerial: module TX to D5, module RX to D6. The sketch prints coordinates, the satellite count and the time converted to IST.
Wiring
| NodeMCU ESP8266 | NEO-6M GPS module (GY-NEO6MV2) | Wire | Notes |
|---|---|---|---|
| VIN | VCC | red | The GY-GPS6MV2 regulates to 3.3 V on board; 5 V from VIN gives it headroom. |
| GND | GND | black | |
| D5 | TX | yellow | Module TX → D5 (GPIO14), the SoftwareSerial RX. 3.3 V signal. |
| D6 | RX | green | D6 (GPIO12) → module RX. Only needed to send configuration commands. |
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
- The NodeMCU’s RX/TX pins are UART0, shared with the USB chip — wiring the GPS there breaks uploads and the Serial Monitor. UART1 is transmit-only (on D4). So the GPS goes on SoftwareSerial, which the ESP8266 core bundles (EspSoftwareSerial); no extra library to install.
- SoftwareSerial’s RX needs a pin-change interrupt, so D0 (GPIO16) can’t be the RX pin. Keep to D5, D6 or D7, and stay at 9600 baud — with Wi-Fi active, higher software baud rates start dropping bytes.
- Alternative:
Serial.swap()moves the hardware UART0 to D7 (RX) / D8 (TX). It is rock solid but you lose USB serial output, so it suits finished projects, not debugging. - GPS time is UTC. The sketch adds 5 h 30 min for IST. The date can roll over at 18:30 UTC — add the same offset to the date if you log it.
- The module’s TX/RX are 3.3 V logic, so both connect directly to the ESP8266 — no divider, unlike on a 5 V Uno.
Code
// NEO-6M GPS (GY-GPS6MV2) on a NodeMCU (ESP8266) using SoftwareSerial (EspSoftwareSerial, bundled
// with the ESP8266 core). VCC → VIN (5 V), GND → GND, module TX → D5 (GPIO14), module RX → D6 (GPIO12).
// The hardware UART (RX/TX pins) stays free for USB and the Serial Monitor.
#include <SoftwareSerial.h>
#include <TinyGPSPlus.h>
SoftwareSerial gpsSerial(D5, D6); // (rx, tx) — RX needs a pin-change interrupt, so never D0
TinyGPSPlus gps;
unsigned long lastPrint = 0;
void setup() {
Serial.begin(115200);
Serial.println();
gpsSerial.begin(9600); // NEO-6M default; 9600 is also where SoftwareSerial is most reliable
Serial.println("Waiting for GPS data (first fix outdoors: 30 s to a few minutes)");
}
void loop() {
while (gpsSerial.available()) gps.encode(gpsSerial.read());
if (millis() - lastPrint < 2000) return;
lastPrint = millis();
if (gps.charsProcessed() < 10) {
Serial.println("No data from GPS - check module TX goes to D5");
return;
}
if (!gps.location.isValid()) {
Serial.printf("No fix yet - satellites in view: %d\n", (int)gps.satellites.value());
return;
}
// GPS time is UTC; India is UTC+5:30.
int mins = gps.time.hour() * 60 + gps.time.minute() + 330;
Serial.printf("Lat %.6f Lon %.6f Sats %d IST %02d:%02d:%02d\n", gps.location.lat(), gps.location.lng(),
(int)gps.satellites.value(), (mins / 60) % 24, mins % 60, gps.time.second());
}// NEO-6M GPS (GY-GPS6MV2) on a NodeMCU (ESP8266) using SoftwareSerial (EspSoftwareSerial, bundled
// with the ESP8266 core). VCC → VIN (5 V), GND → GND, module TX → D5 (GPIO14), module RX → D6 (GPIO12).
// The hardware UART (RX/TX pins) stays free for USB and the Serial Monitor.
#include <SoftwareSerial.h>
#include <TinyGPSPlus.h>
SoftwareSerial gpsSerial(D5, D6); // (rx, tx) — RX needs a pin-change interrupt, so never D0
TinyGPSPlus gps;
unsigned long lastPrint = 0;
void setup() {
Serial.begin(115200);
Serial.println();
gpsSerial.begin(9600); // NEO-6M default; 9600 is also where SoftwareSerial is most reliable
Serial.println("Waiting for GPS data (first fix outdoors: 30 s to a few minutes)");
}
void loop() {
while (gpsSerial.available()) gps.encode(gpsSerial.read());
if (millis() - lastPrint < 2000) return;
lastPrint = millis();
if (gps.charsProcessed() < 10) {
Serial.println("No data from GPS - check module TX goes to D5");
return;
}
if (!gps.location.isValid()) {
Serial.printf("No fix yet - satellites in view: %d\n", (int)gps.satellites.value());
return;
}
// GPS time is UTC; India is UTC+5:30.
int mins = gps.time.hour() * 60 + gps.time.minute() + 330;
Serial.printf("Lat %.6f Lon %.6f Sats %d IST %02d:%02d:%02d\n", gps.location.lat(), gps.location.lng(),
(int)gps.satellites.value(), (mins / 60) % 24, mins % 60, gps.time.second());
}Library: TinyGPSPlus (Mikal Hart) — compiled with TinyGPSPlus 1.0.3.
Expected serial output
Waiting for GPS data (first fix outdoors: 30 s to a few minutes)
No fix yet - satellites in view: 3
No fix yet - satellites in view: 6
Lat 12.971599 Lon 77.594566 Sats 7 IST 14:32:08Waiting for GPS data (first fix outdoors: 30 s to a few minutes) No fix yet - satellites in view: 3 No fix yet - satellites in view: 6 Lat 12.971599 Lon 77.594566 Sats 7 IST 14:32:08
Common errors
“No data from GPS”
TX and RX swapped — the module’s TX must go to D5. Check the module’s LED: it blinks once a second only after a fix, but stays dark without power.
Data arrives but never a fix
Indoors the NEO-6M rarely sees enough satellites. Take it near a window or outside for the first fix. Full fix.
Random resets with Wi-Fi on
A very long loop without yielding. Keep loop() short as shown — reading the GPS a byte at a time is fine. See Soft WDT reset.