Módulo GPS NEO-6M con Arduino: conexión, NMEA y TinyGPSPlus
Módulo GPS NEO-6M con Arduino: conexión TX/RX, tiempo hasta el primer fix, tramas $GPGGA y $GPRMC, código con TinyGPSPlus, ESP32 con OLED y Pico MicroPython.
El módulo GPS NEO-6M es un receptor GPS barato y muy popular que añade a tu proyecto con Arduino, ESP32 o Raspberry Pi Pico la latitud, la longitud, la velocidad, la altitud y la hora UTC extremadamente precisa que llega de los satélites. Para usarlo con Arduino basta con cuatro cables (VCC, GND, TX, RX): una vez por segundo el módulo envía a 9600 baudios líneas de texto llamadas tramas NMEA, y la librería TinyGPSPlus las convierte en números que puedes usar directamente.
En esta guía verás paso a paso cómo funciona el módulo, cómo conectarlo a Arduino Uno, ESP32 y Pico, por qué el primer fix a veces tarda minutos, qué significa cada campo de las tramas $GPGGA y $GPRMC, cómo convertir la latitud y la longitud en un enlace de Google Maps y cómo mostrar la posición en una pantalla OLED. Al final encontrarás una tabla con las causas y soluciones de los dos problemas más frecuentes: "no llegan datos" y "no encuentra posición".

¿Qué es el módulo GPS NEO-6M y cómo funciona?
El GPS (Sistema de Posicionamiento Global) está formado por más de 30 satélites que orbitan a unos 20.200 km de altura. Cada satélite emite sin parar la hora de su reloj atómico y los datos de su propia órbita. El NEO-6M mide la diferencia entre el instante en que la señal salió del satélite y el instante en que llega, y así calcula la distancia a cada satélite; cuando ve al menos 4 satélites obtiene la latitud, la longitud, la altitud y el error de su propio reloj. Con solo 3 satélites puede calcular una posición bidimensional (2D) sin altitud. Las señales llegan a la superficie extremadamente débiles (unos -130 dBm) y el hormigón, los tejados metálicos e incluso la vegetación densa las bloquean en gran parte. Por eso la regla de oro del GPS es: la antena tiene que ver el cielo.
| Característica | Valor |
|---|---|
| Chip receptor | u-blox NEO-6M (motor de posicionamiento u-blox 6) |
| Canales y señal | 50 canales, GPS L1 C/A (1575,42 MHz), SBAS: EGNOS, WAAS, MSAS |
| Precisión horizontal | 2,5 m CEP (2,0 m con SBAS) |
| Precisión de velocidad y rumbo | 0,1 m/s y 0,5 grados |
| Tiempo hasta el primer fix (TTFF) | En frío 27 s, en tibio 27 s, en caliente 1 s |
| Sensibilidad | Seguimiento -161 dBm, arranque en frío -147 dBm |
| Frecuencia de actualización | 1 Hz por defecto, hasta 5 Hz |
| Comunicación | UART, 9600 baudios 8N1 por defecto, NMEA 0183 |
| Alimentación | Placa: 3,3–5 V (con regulador); chip: 2,7–3,6 V |
| Consumo | ~47 mA buscando satélites, ~39 mA en seguimiento |
| Temperatura de trabajo | De -40 a +85 °C |
La placa que compras es algo más que el chip NEO-6M de 16×12 mm. Las placas GY-NEO6MV2 más comunes incluyen una antena cerámica tipo parche de 25×25 mm, un regulador que permite alimentar el módulo con 3,3–5 V, una pequeña EEPROM que guarda la configuración, una batería de respaldo recargable que conserva la hora y los datos de los satélites y un LED que parpadea cuando hay fix. Algunas placas, como la de la foto, tienen además un conector SMA o un pequeño u.FL para una antena externa y un puerto micro USB para conectarla directamente al ordenador.
Conexión del NEO-6M: Arduino Uno, ESP32 y Raspberry Pi Pico
El NEO-6M se comunica por UART, que funciona como el puerto serie de un ordenador y usa dos líneas de datos. La regla es sencilla: las líneas se cruzan. El pin TX (transmite) del módulo va al pin RX (recibe) de la placa, y el pin RX del módulo va al pin TX de la placa. El único puerto serie por hardware del Arduino Uno (pines 0 y 1) se usa para cargar el código por USB y para el monitor serie, así que conectamos el GPS a los pines D4 y D3 con SoftwareSerial. Tienes los detalles de la comunicación serie en el artículo Comunicación serie UART.
| Pin del NEO-6M | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| VCC | 5V | VIN (5V) o 3V3 | VBUS (5V) o 3V3(OUT) |
| GND | GND | GND | GND |
| TX | D4 (RX de SoftwareSerial) | GPIO 16 (RX2) | GP1 (RX de UART0) |
| RX | D3, a través de un divisor de 1 kΩ / 2 kΩ | GPIO 17 (TX2) | GP0 (TX de UART0) |
| PPS (si lo tiene) | Puede quedar libre | Puede quedar libre | Puede quedar libre |
El primer fix: arranque en frío, en tibio y en caliente
Es totalmente normal que un módulo GPS no dé la posición nada más encenderlo por primera vez. Para calcularla necesita los datos orbitales precisos (efemérides) de los satélites visibles, y cada satélite los emite aproximadamente cada 30 segundos; descargar el almanaque completo, la lista orbital aproximada de todos los satélites, lleva 12,5 minutos. Según cuánta información conserve de antes de apagarse, el módulo arranca de una de tres maneras. Los tiempos de la hoja de datos se midieron en condiciones de prueba con señal fuerte de todos los satélites; con la pequeña antena cerámica de la placa, esperar unos minutos es lo habitual.
| Tipo de arranque | Información guardada en el módulo | Hoja de datos | En la práctica (exterior) |
|---|---|---|---|
| Arranque en frío | Sin hora, posición ni datos de satélites (primer uso o batería de respaldo descargada) | 27 s | 1–5 minutos; con señal débil puede superar los 10 minutos |
| Arranque en tibio | Almanaque, hora y posición aproximadas; efemérides caducadas (apagado más de unas 4 horas) | 27 s | 30 segundos – 1 minuto |
| Arranque en caliente | Efemérides y hora actuales (apagado hace poco, batería de respaldo cargada) | 1 s | Unos segundos |
- Sal al exterior: En interiores, sobre todo en edificios de hormigón, el módulo muchas veces no encuentra ninguna posición. Para probar, usa un balcón, un jardín o al menos el sitio junto a una ventana abierta.
- Orienta bien la antena: La cara plana y clara de la antena cerámica debe mirar al cielo; darle la vuelta o taparla con la mano debilita mucho la señal.
- Aleja la antena de la electrónica: La antena wifi del ESP32, los reguladores conmutados y los cables USB largos emiten ruido; coloca la antena a unos centímetros de las placas.
- Ten paciencia y no lo muevas: No cortes la alimentación durante el primer fix; cada reinicio empieza la búsqueda desde cero.
- Fíjate en el LED: El LED de la placa permanece apagado hasta que hay fix y entonces empieza a parpadear una vez por segundo.
- Carga la batería de respaldo: Si está descargada, cada encendido es un arranque en frío. Dejar el módulo alimentado unas horas la carga y los siguientes arranques bajan a segundos.
Tramas NMEA: cómo leer $GPGGA y $GPRMC
El NEO-6M envía todo lo que calcula como líneas de texto plano separadas por comas según el estándar NMEA 0183. Con la configuración de fábrica llegan cada segundo seis tipos de trama: GGA, GLL, GSA, GSV, RMC y VTG; las tramas TXT suelen aparecer solo al encender y llevan mensajes informativos como la versión del firmware. Cada trama empieza por $; el prefijo GP indica que los datos vienen del GPS y las tres letras siguientes el tipo de trama. Los dos caracteres que siguen al * del final son la suma de comprobación (checksum), que se obtiene haciendo XOR de todos los caracteres entre $ y *, y sirve para descartar las líneas corruptas. Las dos tramas más importantes para la posición son: $GPGGA,092750.00,4100.82500,N,02858.74000,E,1,08,1.01,39.2,M,36.9,M,,*67 y $GPRMC,092750.00,A,4100.82500,N,02858.74000,E,0.512,,281026,,,A*7E
| Campo de $GPGGA | Ejemplo | Significado |
|---|---|---|
| Hora (UTC) | 092750.00 | 09:27:50 UTC (10:27:50 en la España peninsular en invierno) |
| Latitud | 4100.82500,N | 41 grados 00,825 minutos Norte |
| Longitud | 02858.74000,E | 28 grados 58,74 minutos Este |
| Calidad del fix | 1 | 0 = sin posición, 1 = GPS, 2 = diferencial (SBAS) |
| Satélites | 08 | Satélites usados en el cálculo |
| HDOP | 1.01 | Dilución horizontal de la precisión; por debajo de 2 es muy buena |
| Altitud | 39.2,M | 39,2 m sobre el nivel medio del mar |
| Separación del geoide | 36.9,M | Diferencia entre el nivel del mar y el elipsoide WGS84 |
| Campo de $GPRMC | Ejemplo | Significado |
|---|---|---|
| Hora (UTC) | 092750.00 | Horas, minutos, segundos |
| Estado | A | A = posición válida, V = sin posición |
| Latitud / longitud | 4100.82500,N / 02858.74000,E | Mismo formato que en GGA |
| Velocidad | 0.512 | En nudos (0,512 × 1,852 ≈ 0,95 km/h) |
| Rumbo | (vacío) | Dirección de movimiento en grados; puede ir vacío si está parado |
| Fecha | 281026 | Formato ddmmaa: 28/10/2026 |
| Modo | A | A = autónomo, D = diferencial, N = sin datos |
La latitud y la longitud de NMEA no están en grados decimales, sino en grados y minutos (ggmm.mmmm), mientras que Google Maps y casi todos los programas piden grados decimales. Para convertir, toma las dos primeras cifras de la latitud (las tres primeras de la longitud) como grados y suma los minutos restantes divididos entre 60: 4100.82500 → 41 + 0,825 / 60 = 41,013750; 02858.74000 → 28 + 58,74 / 60 = 28,979000. Los valores Sur (S) y Oeste (W) se escriben en negativo. El sexto decimal equivale a unos 11 cm y el quinto a 1,1 m; como el NEO-6M tiene una precisión de 2,5 m, seis decimales son más que suficientes.
Paso a paso: ver los datos GPS en bruto con Arduino Uno
- Conecta el NEO-6M como en la tabla: VCC → 5V, GND → GND, TX del GPS → D4, RX del GPS → D3 a través del divisor.
- Carga el código de abajo; no hace falta ninguna librería extra, SoftwareSerial viene con el IDE de Arduino.
- Abre el monitor serie y elige 115200 baudios.
- Lleva el módulo al exterior o junto a una ventana y observa cómo pasan las líneas que empiezan por $GP.
- Cuando el segundo campo de la línea $GPRMC cambie de V a A y el LED empiece a parpadear, el módulo habrá encontrado su posición.
1#include <SoftwareSerial.h>
2
3SoftwareSerial gpsSerial(4, 3); // RX, TX → GPS TX D4'e, GPS RX D3'e / GPS TX to D4, GPS RX to D3
4
5void setup() {
6 Serial.begin(115200); // Seri Monitör hızı / Serial Monitor speed
7 gpsSerial.begin(9600); // NEO-6M varsayılan hızı / NEO-6M default baud rate
8 Serial.println(F("NEO-6M ham NMEA / raw NMEA"));
9}
10
11void loop() {
12 while (gpsSerial.available()) {
13 Serial.write(gpsSerial.read()); // gelen her baytı aynen aktar / pass every byte through
14 }
15}En el monitor serie verás cada segundo un bloque de seis a nueve líneas (cuantos más satélites hay a la vista, en más líneas se divide la trama GSV). Si el módulo todavía no ha encontrado satélites, las líneas están llenas de comas vacías, como $GPRMC,,V,,,,,,,,,,N*53 y $GPGGA,,,,,,0,00,99.99,,,,,,*48; no es un error, indica que el módulo funciona pero aún no puede calcular la posición. En las líneas $GPGSV puedes seguir el número de satélites visibles y la intensidad de señal (dB-Hz) de cada uno; los valores por encima de 30 dB-Hz indican buena señal. Si no llega ninguna línea, revisa el cableado; si salen caracteres raros, revisa la velocidad en baudios.
Leer latitud, longitud, velocidad y hora con TinyGPSPlus
En lugar de analizar las líneas NMEA a mano, es mucho más práctico usar la librería TinyGPSPlus de Mikal Hart. Búscala como "TinyGPSPlus" en el gestor de librerías e instálala; explicamos los pasos en el artículo cómo añadir una librería en Arduino. Ojo: los ejemplos de la librería vienen a 4800 baudios y el NEO-6M usa 9600. Si ejecutas un ejemplo sin cambiar la velocidad, la ubicación saldrá siempre como INVALID, porque la librería no puede descifrar ni una sola trama a partir de caracteres corruptos. El código siguiente muestra cada segundo la posición, la velocidad, la altitud, el número de satélites, la fecha y la hora, y convierte la posición en un enlace de Google Maps que puedes abrir en el navegador:
1#include <TinyGPSPlus.h>
2#include <SoftwareSerial.h>
3
4const int RX_PIN = 4; // GPS TX → D4
5const int TX_PIN = 3; // GPS RX ← D3 (bölücü üzerinden / through a divider)
6const uint32_t GPS_BAUD = 9600; // NEO-6M varsayılanı / NEO-6M default
7
8TinyGPSPlus gps;
9SoftwareSerial gpsSerial(RX_PIN, TX_PIN);
10unsigned long lastPrint = 0;
11
12void setup() {
13 Serial.begin(115200);
14 gpsSerial.begin(GPS_BAUD);
15 Serial.println(F("NEO-6M + TinyGPSPlus"));
16}
17
18void loop() {
19 while (gpsSerial.available()) {
20 gps.encode(gpsSerial.read()); // her baytı ayrıştırıcıya ver / feed every byte to the parser
21 }
22 if (millis() - lastPrint >= 1000) { // delay() yok, saniyede bir yaz / no delay(), print once per second
23 lastPrint = millis();
24 printInfo();
25 }
26}
27
28void printInfo() {
29 if (gps.charsProcessed() < 10) {
30 Serial.println(F("GPS verisi yok: kablolari ve baud hizini kontrol et / No GPS data: check wiring and baud"));
31 return;
32 }
33 Serial.print(F("Uydu / Sats: "));
34 Serial.println(gps.satellites.value());
35
36 if (gps.location.isValid()) {
37 Serial.print(F("Enlem / Lat: "));
38 Serial.println(gps.location.lat(), 6);
39 Serial.print(F("Boylam / Lng: "));
40 Serial.println(gps.location.lng(), 6);
41 Serial.print(F("Hiz / Speed: "));
42 Serial.print(gps.speed.kmph(), 1);
43 Serial.println(F(" km/h"));
44 Serial.print(F("Yukseklik / Alt: "));
45 Serial.print(gps.altitude.meters(), 1);
46 Serial.println(F(" m"));
47 // Google Haritalar bağlantısı / Google Maps link
48 Serial.print(F("https://www.google.com/maps/search/?api=1&query="));
49 Serial.print(gps.location.lat(), 6);
50 Serial.print(',');
51 Serial.println(gps.location.lng(), 6);
52 } else {
53 Serial.println(F("Konum yok, uydu araniyor / No fix yet, searching"));
54 }
55
56 if (gps.date.isValid() && gps.time.isValid()) {
57 char buf[32];
58 snprintf(buf, sizeof(buf), "%02d.%02d.%04d %02d:%02d:%02d UTC",
59 gps.date.day(), gps.date.month(), (int)gps.date.year(),
60 gps.time.hour(), gps.time.minute(), gps.time.second());
61 Serial.println(buf); // saat UTC'dir / time is UTC
62 }
63 Serial.println();
64}| Función | Qué devuelve |
|---|---|
| gps.encode(c) | Se llama con cada byte recibido; devuelve true cuando se completa una trama válida |
| gps.location.lat() / lng() | Latitud y longitud en grados decimales |
| gps.location.isValid() / age() | Si hay posición válida / milisegundos desde la última actualización |
| gps.speed.kmph() | Velocidad sobre el suelo en km/h (también mps() y knots()) |
| gps.course.deg() | Rumbo en grados (0 = norte, 90 = este) |
| gps.altitude.meters() | Altitud sobre el nivel del mar en metros |
| gps.satellites.value() | Número de satélites usados en la posición |
| gps.hdop.hdop() | Dilución horizontal de la precisión (menor = mejor) |
| gps.date.day() / gps.time.hour() | Campos de fecha y hora en UTC |
| TinyGPSPlus::distanceBetween(...) | Distancia entre dos coordenadas en metros |
| gps.failedChecksum() | Número de tramas corruptas; si no para de subir, se están perdiendo datos |
La regla más importante de este código es no usar delay() dentro de loop(). El GPS envía unos 500 bytes por segundo, pero el búfer de recepción de SoftwareSerial solo tiene 64 bytes; durante un delay() largo se desborda, las tramas quedan cortadas y TinyGPSPlus las descarta por error de checksum. Por eso imprimimos una vez por segundo con millis(); tienes los detalles en el artículo millis() frente a delay(). La hora del GPS siempre es UTC: en la España peninsular suma 1 hora en invierno y 2 en verano. En Arduino Uno un double es en realidad un float de 32 bits, así que el último dígito de la latitud y la longitud se redondea a cerca de medio metro; frente a los 2,5 m de precisión del módulo, esa diferencia no importa.
ESP32 con Serial2: mostrar la posición en una pantalla OLED
En el ESP32 no necesitas SoftwareSerial: el chip tiene tres UART por hardware y Serial2 se puede asignar a los pines que quieras. En el ejemplo de abajo el GPS va a GPIO 16 (RX2) y GPIO 17 (TX2); como el ESP32 trabaja a 3,3 V, tampoco hace falta divisor. La posición, la velocidad y la hora local se muestran en una pantalla OLED SSD1306 de 0,96 pulgadas conectada por I2C a GPIO 21 (SDA) y GPIO 22 (SCL). Además de TinyGPSPlus, el código necesita las librerías Adafruit SSD1306 y Adafruit GFX.
1#include <TinyGPSPlus.h>
2#include <Wire.h>
3#include <Adafruit_GFX.h>
4#include <Adafruit_SSD1306.h>
5
6#define GPS_RX 16 // ESP32 RX2 ← GPS TX
7#define GPS_TX 17 // ESP32 TX2 → GPS RX
8const int UTC_OFFSET = 3; // Türkiye UTC+3, kendi saat dilimini yaz / set your own time zone
9
10TinyGPSPlus gps;
11Adafruit_SSD1306 display(128, 64, &Wire, -1); // OLED: SDA 21, SCL 22
12unsigned long lastDraw = 0;
13
14String mapsLink() {
15 // Google Haritalar bağlantısı üret / build a Google Maps link
16 return "https://www.google.com/maps/search/?api=1&query=" +
17 String(gps.location.lat(), 6) + "," + String(gps.location.lng(), 6);
18}
19
20void drawScreen() {
21 display.clearDisplay();
22 display.setTextSize(1);
23 display.setCursor(0, 0);
24 display.print(F("Uydu/Sat: "));
25 display.print(gps.satellites.value());
26
27 if (gps.time.isValid()) {
28 int h = (gps.time.hour() + UTC_OFFSET + 24) % 24; // yerel saat / local time
29 char t[12];
30 snprintf(t, sizeof(t), "%02d:%02d:%02d", h, gps.time.minute(), gps.time.second());
31 display.setCursor(80, 0);
32 display.print(t);
33 }
34
35 if (gps.location.isValid()) {
36 display.setCursor(0, 16);
37 display.print(F("Lat: "));
38 display.println(gps.location.lat(), 6);
39 display.print(F("Lng: "));
40 display.println(gps.location.lng(), 6);
41 display.setTextSize(2); // hız büyük yazılsın / speed in large digits
42 display.setCursor(0, 44);
43 display.print(gps.speed.kmph(), 1);
44 display.print(F(" km/h"));
45 } else {
46 display.setCursor(0, 24);
47 display.println(F("Uydu araniyor..."));
48 display.println(F("Searching for fix"));
49 }
50 display.display();
51}
52
53void setup() {
54 Serial.begin(115200);
55 Serial2.begin(9600, SERIAL_8N1, GPS_RX, GPS_TX); // donanım UART2 / hardware UART2
56 if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
57 Serial.println(F("OLED bulunamadi / OLED not found"));
58 while (true) delay(10);
59 }
60 display.setTextColor(SSD1306_WHITE);
61}
62
63void loop() {
64 while (Serial2.available()) {
65 gps.encode(Serial2.read()); // NMEA baytlarını çöz / decode NMEA bytes
66 }
67 if (millis() - lastDraw >= 1000) { // saniyede bir güncelle / refresh once per second
68 lastDraw = millis();
69 drawScreen();
70 if (gps.location.isValid()) {
71 Serial.println(mapsLink()); // Telegram ya da SMS ile gönderilebilir / can be sent via Telegram or SMS
72 }
73 }
74}La variable UTC_OFFSET pasa la hora GPS a la hora local; en la España peninsular es 1 en invierno y 2 en verano. La función mapsLink() convierte la posición en una dirección de Google Maps con el parámetro query=latitud,longitud. Puedes copiar ese enlace del monitor serie y abrirlo en el navegador, enviarlo al móvil con un bot de Telegram o mandarlo por SMS con un módulo GSM SIM800L. En las placas ESP32-WROVER con PSRAM, los GPIO 16 y 17 se usan para la memoria, así que conecta el GPS, por ejemplo, a GPIO 26 y 27; en el ESP32-C3, que solo tiene dos UART, usa Serial1 en lugar de Serial2.
Leer el GPS con Raspberry Pi Pico y MicroPython
En la Raspberry Pi Pico conectamos el GPS a la UART0: el TX del módulo va a GP1 y su RX a GP0. En MicroPython podemos analizar nosotros mismos las líneas NMEA sin instalar ninguna librería, y además es un ejercicio estupendo para entender cómo están construidas las tramas. El código comprueba el checksum de cada línea, toma el número de satélites de GGA y la posición, la velocidad y la hora de RMC, y convierte grados y minutos a grados decimales. El ajuste timeout=200 hace que readline() espere hasta 200 ms el final de la línea; si al arrancar se cuela una línea a medias, la comprobación del checksum la descarta. Como el tipo de trama se obtiene con f[0][3:], el código también reconoce las líneas $GNRMC y $GNGGA de los módulos que usan varios sistemas de satélites. Si todavía no sabes ejecutar código con Thonny, echa antes un vistazo al artículo fundamentos de MicroPython.
1from machine import UART, Pin
2
3# GP0 (TX) → GPS RX, GP1 (RX) ← GPS TX
4gps = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1), timeout=200, rxbuf=1024)
5
6def checksum_ok(line):
7 # $ ile * arasındaki karakterlerin XOR'u / XOR of the characters between $ and *
8 try:
9 body, cs = line[1:].split('*')
10 x = 0
11 for ch in body:
12 x ^= ord(ch)
13 return x == int(cs[:2], 16)
14 except ValueError:
15 return False
16
17def to_deg(value, hemi):
18 # ddmm.mmmm → ondalık derece / decimal degrees
19 dot = value.index('.')
20 deg = int(value[:dot - 2]) + float(value[dot - 2:]) / 60
21 return -deg if hemi in ('S', 'W') else deg
22
23sats = '0'
24while True:
25 raw = gps.readline() # bir NMEA satırı / one NMEA line
26 if not raw:
27 continue
28 try:
29 line = raw.decode().strip()
30 except UnicodeError:
31 continue # bozuk bayt / corrupted byte
32 if not line.startswith('$') or not checksum_ok(line):
33 continue
34 f = line.split('*')[0].split(',')
35 kind = f[0][3:] # GPRMC ya da GNRMC → RMC / GPRMC or GNRMC → RMC
36 if kind == 'GGA':
37 sats = f[7] # kullanılan uydu sayısı / satellites in use
38 elif kind == 'RMC':
39 if f[2] != 'A':
40 print('Uydu araniyor / searching, uydu/sats:', sats)
41 continue
42 lat = to_deg(f[3], f[4])
43 lon = to_deg(f[5], f[6])
44 kmh = float(f[7] or 0) * 1.852 # knot → km/sa / knots → km/h
45 t = f[1]
46 print('UTC {}:{}:{} uydu/sats: {}'.format(t[0:2], t[2:4], t[4:6], sats))
47 print('Konum / position: {:.6f}, {:.6f} {:.1f} km/h'.format(lat, lon, kmh))
48 print('https://www.google.com/maps/search/?api=1&query={:.6f},{:.6f}'.format(lat, lon))Ideas de proyectos con el NEO-6M
- Localizador que envía su posición por SMS: Con un NEO-6M y un SIM800L, un localizador para coche o bici que responde a un SMS con la palabra "POSICION" enviando un enlace de Google Maps.
- Registrador de datos GPS: Guarda tu ruta cada segundo en CSV en un módulo de tarjeta SD y luego dibújala en un mapa en el ordenador.
- Velocímetro para bici: Muestra la velocidad actual y la máxima y la distancia total en una pantalla OLED; suma la distancia entre puntos consecutivos con TinyGPSPlus::distanceBetween().
- Reloj sincronizado por satélite: Toma la hora del GPS y pon en hora al segundo un módulo de reloj RTC sin necesidad de internet.
- Alarma de geovalla (geofence): Un sistema que hace sonar un zumbador o envía un aviso cuando algo se aleja más de 100 metros de un punto que tú eliges.
- Seguimiento de largo alcance: Envía la posición a un receptor a kilómetros de distancia con un módulo LoRa y ahorra energía con batería usando el deep sleep del ESP32.
No llegan datos o no encuentra posición: errores frecuentes y soluciones
| Síntoma | Causa probable | Solución |
|---|---|---|
| El monitor serie está totalmente vacío | TX y RX invertidos, GND no común o el módulo no recibe alimentación | Conecta el TX del GPS al RX de la placa (D4 en el Uno), une las masas y mide VCC |
| "No GPS detected: check wiring." | En 5 segundos no han llegado ni 10 caracteres a la librería | Revisa el cableado y los números de pin del código; prueba a intercambiar TX y RX |
| Caracteres sin sentido | La velocidad en baudios no coincide | Usa 9600 para el GPS y la velocidad del código en el monitor serie; si no, prueba 4800 y 38400 |
| Llegan líneas pero con campos vacíos (,V,) | El módulo funciona pero no ha encontrado satélites | Sal al exterior, orienta la antena al cielo y espera 5–15 minutos |
| El número de satélites se queda entre 0 y 3 | Señal débil, antena al revés, conector u.FL flojo o una fuente de ruido cerca | Revisa la antena, encaja bien el conector y aléjala del ESP32 y de los reguladores |
| Los datos en bruto tienen posición, TinyGPSPlus dice INVALID | El delay() de loop() desborda el búfer; failedChecksum() sube | Quita el delay(), usa millis() y llama a encode() continuamente |
| La hora va desfasada varias horas | La hora del GPS es UTC | Suma la diferencia de tu huso horario |
| Parado, la posición se desplaza unos metros y la velocidad no es 0 | Ruido normal del GPS y señales reflejadas en edificios | Considera 0 las velocidades por debajo de 2 km/h, descarta lecturas con HDOP alto y promedia |
| En cada encendido vuelve a tardar minutos | Batería de respaldo descargada o inexistente | Deja el módulo alimentado unas horas para cargar la batería |
El NEO-6M da a tus proyectos, por muy poco dinero, la respuesta a "dónde estoy, a qué velocidad voy y qué hora es"; el secreto está en enseñarle el cielo a la antena y tener paciencia con el primer fix. Para entender mejor la comunicación serie, echa un vistazo al artículo sobre UART. En la app BlueGrays encontrarás proyectos que también te servirán para tus montajes con GPS, como la comunicación serie entre dos Arduino, pantallas OLED, registro de datos en microSD, el deep sleep del ESP32 y una alarma con avisos por Telegram, todos con esquemas de circuito y código paso a paso.
Preguntas frecuentes
¿El módulo GPS NEO-6M funciona en interiores?
Normalmente no. Las señales de los satélites llegan muy débiles y el hormigón o los tejados metálicos las bloquean casi por completo. Junto a una ventana abierta puede captar algunos satélites, pero para un fix fiable la antena necesita ver el cielo directamente; si necesitas posición en interiores, usa una antena activa externa colocada en el tejado o en una ventana.
¿Cuánto tarda el NEO-6M en obtener el primer fix?
Según la hoja de datos, el arranque en frío tarda 27 segundos y el arranque en caliente 1 segundo. En la práctica, con la pequeña antena cerámica y al aire libre, el primer fix suele tardar entre 1 y 5 minutos, y en el primer uso o con la batería de respaldo descargada puede llegar a 10–15 minutos. Si el módulo se apagó hace poco, vuelve a fijar posición en pocos segundos.
¿Qué precisión tiene el módulo GPS NEO-6M?
El fabricante indica una precisión horizontal de 2,5 m CEP con cielo despejado, o 2,0 m con corrección SBAS; es decir, la mitad de las medidas quedan a menos de 2,5 m del punto real. En ciudad, las señales reflejadas en los edificios pueden subir el error a 5–10 metros. La altitud es menos precisa que la posición horizontal.
¿El NEO-6M funciona a 5V? ¿Se puede conectar directamente a Arduino?
Las placas GY-NEO6MV2 habituales llevan regulador, así que VCC puede ir a 5 V. Sin embargo, el pin RX del chip soporta como máximo 3,6 V, por lo que debes bajar la señal TX de 5 V del Arduino Uno con un divisor de tensión de 1 kΩ y 2 kΩ. La salida TX del módulo es de 3,3 V y el Uno la lee sin problema.
¿Un módulo GPS necesita internet o tarjeta SIM?
No. El NEO-6M calcula su posición directamente con las señales de los satélites, gratis y sin internet. Solo necesitas internet o una SIM si quieres enviar la posición a otro sitio, por ejemplo con el wifi del ESP32 o con un módulo GSM como el SIM800L.
¿Qué diferencia hay entre el NEO-6M y el NEO-M8N?
El NEO-6M solo usa satélites GPS. El NEO-M8N, más moderno, puede usar a la vez satélites GLONASS, Galileo o BeiDou además de GPS; al ver más satélites, consigue un fix más rápido y estable en ciudad. Sus tramas suelen empezar por $GN, pero TinyGPSPlus lee ambos con el mismo código.
Proyectos para hacer después de leer esto
Ver todos los proyectos
Comunicación unidireccional entre 2 Arduino
Un proyecto para la transferencia unidireccional de datos entre dos Arduinos mediante comunicación serie. Detecta la pulsación del botón en el lado del transmisor y enseña a controlar el LED en el lad

Comunicación bidireccional entre 2 Arduino
Un proyecto que transfiere datos bidireccionales entre dos Arduinos mediante comunicación serie. Ambas placas leen el botón y envían datos y reciben datos y controlan el LED.

Registrador de Temperatura ESP32 (DHT22 + DS1307 RTC + microSD + OLED)
¡Construye un registrador de temperatura con marca de tiempo con ESP32! Lee temperatura/humedad del DHT22 cada 10 segundos, obtén la hora real del DS1307 RTC y registra en tarjeta microSD como CSV. El OLED muestra en vivo la temperatura (fuente grande), porcentaje de humedad, marca de tiempo y contador de registros. El archivo `/temp_log.csv` en la tarjeta SD se abre en Excel, Google Sheets o Python pandas para análisis — perfecto para monitoreo de invernadero, seguimiento de temperatura del hog

ESP32 HTTPClient + API del tiempo (con pantalla OLED SSD1306)
En este proyecto, utilizamos el ESP32 para realizar una llamada a la API REST de la información meteorológica (wttr.in) a través de Internet, utilizando `HTTPClient.h`. A diferencia de la versión anterior con pantalla LCD I2C, aquí utilizamos una pantalla OLED SSD1306 (128×64), lo que permite utilizar fuentes grandes, iconos vectoriales y una presentación visual muy rica en una sola pantalla. Para indicar el tiempo, se dibujan en tiempo de ejecución 7 iconos vectoriales diferentes (soleado, parc

Modo de suspensión profunda del ESP32 — Ahorro de batería (OLED + contador en RTC RAM)
El modo «Deep Sleep» del ESP32 reduce el consumo de corriente a unos 10 µA al desactivar la CPU y la mayoría de los periféricos, lo cual es de vital importancia para los dispositivos IoT que funcionan con pilas. En este proyecto utilizamos tres fuentes de activación diferentes: el temporizador (15 s), un pin externo (EXT0 — botón GPIO 33) y el sensor táctil (T0/GPIO 4). Las variables marcadas con `RTC_DATA_ATTR` se conservan durante el modo Deep Sleep: el contador de arranque, el contador de act

ESP32: hora real desde Internet con NTP
El ESP32 se conecta a Wi-Fi y obtiene la hora real con precisión de milisegundos desde servidores NTP (Network Time Protocol). Gracias a `configTzTime()`, se aplica automáticamente la zona horaria de Turquía (TRT, UTC+3) y el cambio DST se gestiona mediante la cadena POSIX TZ. La hora se muestra en el monitor serie y en una pantalla LCD I2C opcional. `configTzTime()` inicia el servicio SNTP; se realizan resincronizaciones periódicas mientras hay acceso a la red, y el reloj del sistema sigue avan

Sistema de Alarma de Movimiento ESP32 (PIR + Buzzer + LED + Telegram)
Un sistema inteligente de alarma de seguridad que activa el buzzer y el LED cuando el sensor PIR detecta movimiento, y envía simultáneamente una notificación instantánea al propietario vía Telegram. Cuenta con un tiempo de espera de 30 segundos para limitar la frecuencia de las alarmas repetidas; este mecanismo no comprueba la validez de las detecciones del sensor PIR ni elimina las falsas detecciones. Ideal para seguridad del hogar, vigilancia de garajes, almacenes o salas de trabajo. Con ESP32

Reloj digital con ESP32 y RTC DS1307 (I2C + LCD)
En este proyecto, mostramos la hora y la fecha en una pantalla LCD I2C de 16x2 mediante el módulo de reloj en tiempo real (RTC) DS1307. El DS1307 es un RTC con batería de respaldo que conserva la hora incluso si el ESP32 se apaga. Ambos módulos comparten la misma línea I2C (SDA/SCL), por lo que aprendemos a controlar varios dispositivos con solo dos cables de señal. Con este proyecto conocerás el protocolo I2C, el funcionamiento de los módulos RTC y la visualización dinámica en pantallas LCD.

