Preview build — page text is a draft awaiting human review. Code badges come from real compiles. The flasher is real but not yet hardware-tested; the Playground is still a preview.

NEO-6M GPS with ESP32-S3-DevKitC-1 — UART1 on GPIO17/18

Read a NEO-6M GPS on an ESP32-S3-DevKitC-1 with Serial1 on explicit pins (RX 18, TX 17) — core 3.x defaults Serial1 to 15/16 — and TinyGPSPlus.

Arduino C++Verified — core esp32 3.3.12, TinyGPSPlus 1.0.3, 3 Nov 2026Compile-only

Community draft — report a mistake

The NEO-6M gives position, altitude and UTC time over a serial line at 9600 baud. The S3 has three hardware UARTs, so the GPS gets its own (Serial1) while USB keeps the Serial Monitor.

The S3-specific detail is the pin assignment: pass RX 18 / TX 17 to Serial1.begin() rather than relying on the core’s defaults. Take the board outdoors or near a window for the first fix.

Wiring

ESP32-S3-DevKitC-1Only the pins used are shown · 3.3 V logicNEO-6M GPS module (GY-NEO6MV2)17 · 1718 · 185VGNDVCCRXTXGND
Every connection
ESP32-S3-DevKitC-1NEO-6M GPS module (GY-NEO6MV2)WireNotes
5VVCCredThe module has its own 3.3 V regulator.
GNDGNDblack
18TXyellowGPS TX → S3 RX (GPIO18). 3.3 V signal.
17RXgreenS3 TX (GPIO17) → GPS RX. Only needed to send config 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

  • Arduino core 3.x starts Serial1 on RX 15 / TX 16 on the S3 unless you pass pins. The sketch calls Serial1.begin(9600, SERIAL_8N1, 18, 17), matching the S3’s native UART1 pins.
  • Don’t use TX/RX (GPIO43/44) for the GPS: they are UART0, wired to the USB bridge behind the UART port, and carry your Serial Monitor.
  • The module’s TX is a 3.3 V signal even on 5 V power, so it connects directly to the S3.
  • First fix needs open sky and can take 2–5 minutes (cold start). The blue LED on the module blinks once a second when it has a fix.

Code

neo6m-gps-esp32s3.inoArduino C++Open in PlaygroundOpen in PlaygroundFlash
// NEO-6M GPS on an ESP32-S3-DevKitC-1 via UART1.
// VCC → 5V, GND → GND, GPS TX → GPIO18 (S3 RX), GPS RX → GPIO17 (S3 TX).
// Arduino core 3.x defaults Serial1 to RX 15 / TX 16 on the S3, so the pins are passed explicitly.
#include <TinyGPSPlus.h>

constexpr int GPS_RX = 18;  // S3 receives on GPIO18 from GPS TX
constexpr int GPS_TX = 17;  // S3 transmits on GPIO17 to GPS RX
TinyGPSPlus gps;

void setup() {
  Serial.begin(115200);  // UART USB-C port
  Serial1.begin(9600, SERIAL_8N1, GPS_RX, GPS_TX);
  Serial.println("NEO-6M on Serial1 (RX 18, TX 17) - first fix can take a few minutes outdoors");
}

void loop() {
  while (Serial1.available()) gps.encode(Serial1.read());
  static uint32_t last = 0;
  if (millis() - last < 2000) return;
  last = millis();
  if (gps.charsProcessed() < 10) {
    Serial.println("No data from GPS - check GPS TX to GPIO18 and power");
  } else if (!gps.location.isValid()) {
    Serial.printf("Waiting for fix... satellites: %lu\n", (unsigned long)gps.satellites.value());
  } else {
    Serial.printf("Lat %.6f  Lng %.6f  Sats %lu  Alt %.0f m  %02d:%02d:%02d UTC\n",
                  gps.location.lat(), gps.location.lng(), (unsigned long)gps.satellites.value(),
                  gps.altitude.meters(), gps.time.hour(), gps.time.minute(), gps.time.second());
  }
}

Library: TinyGPSPlus (Mikal Hart) — compiled with TinyGPSPlus 1.0.3.

Expected serial output

Serial Monitor · 115200Output
NEO-6M on Serial1 (RX 18, TX 17) - first fix can take a few minutes outdoors
Waiting for fix... satellites: 3
Lat 12.971599  Lng 77.594566  Sats 8  Alt 921 m  06:41:12 UTC

Common errors

“No data from GPS”

TX and RX crossed the wrong way, or the pins weren’t passed to Serial1.begin(). GPS TX must go to GPIO18.

Data but never a fix

Indoors or under a concrete roof. Full fix.

↑↓ to move · Enter to open