lunes, 25 de agosto de 2025

GPS NEO-6M: Geolocalización para Proyectos IoT

Módulo GPS NEO-6M conectado a ESP32

GPS NEO-6M: Geolocalización para Proyectos IoT

🛰️ ¿Qué es el NEO-6M?

El NEO-6M es un módulo GPS económico y popular basado en el chip u-blox NEO-6M que permite obtener coordenadas de posición precisas mediante satélites GPS. Es ideal para proyectos IoT que requieren tracking, navegación o monitoreo de ubicación.


📌 Características principales

  • Chip: u-blox NEO-6M (50 canales)
  • Precisión: 2.5 metros (típica)
  • Sensibilidad: -161 dBm
  • Tiempo de arranque: 27s (arranque en frío)
  • Alimentación: 3.3V a 5V
  • Comunicación: UART (9600 baudios por defecto)
  • Protocolo: NMEA 0183
  • Antena: Cerámica integrada + conector para antena externa

🔌 Pinout del NEO-6M

El módulo típico tiene 4 pines principales:

PinFunciónConexión ESP32
VCCAlimentación3.3V o 5V
GNDTierraGND
TXTransmisiónPin RX (GPIO16)
RXRecepciónPin TX (GPIO17)

Pines adicionales (según modelo):

  • PPS: Pulso por segundo (sincronización)
  • EN: Enable/Reset

💻 Conexión básica ESP32 + NEO-6M

ESP32          NEO-6M
3.3V    ───────  VCC
GND     ───────  GND
GPIO16  ───────  TX   (RX del ESP32)
GPIO17  ───────  RX   (TX del ESP32)

Código básico de lectura

#include <SoftwareSerial.h>

// Crear puerto serie software para GPS
SoftwareSerial gpsSerial(16, 17); // RX, TX

void setup() {
  Serial.begin(115200);
  gpsSerial.begin(9600);
  
  Serial.println("GPS NEO-6M - Iniciando...");
  Serial.println("Esperando señal GPS...");
}

void loop() {
  while (gpsSerial.available() > 0) {
    String gpsData = gpsSerial.readStringUntil('\n');
    Serial.println(gpsData);
  }
  delay(1000);
}

📡 Entendiendo los datos NMEA

El NEO-6M envía datos en formato NMEA 0183. Las sentencias más importantes:

$GPGGA - Datos de posición

$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
  • 123519: Hora UTC (12:35:19)
  • 4807.038,N: Latitud 48°07.038’ Norte
  • 01131.000,E: Longitud 11°31.000’ Este
  • 1: Fix quality (0=no fix, 1=GPS fix, 2=DGPS fix)
  • 08: Número de satélites
  • 0.9: HDOP (dilución horizontal de precisión)
  • 545.4,M: Altitud en metros

$GPRMC - Datos recomendados mínimos

$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A
  • A: Status (A=activo, V=void/inválido)
  • 022.4: Velocidad en nudos
  • 084.4: Rumbo en grados

🎯 Proyecto: Tracker GPS básico

Código completo con parsing

#include <SoftwareSerial.h>

SoftwareSerial gpsSerial(16, 17);

struct GPSData {
  float latitude = 0.0;
  float longitude = 0.0;
  float altitude = 0.0;
  float speed = 0.0;
  int satellites = 0;
  bool fix = false;
  String timestamp = "";
};

GPSData gps;

void setup() {
  Serial.begin(115200);
  gpsSerial.begin(9600);
  
  Serial.println("=== GPS NEO-6M Tracker ===");
  Serial.println("Esperando fix GPS...");
}

void loop() {
  while (gpsSerial.available() > 0) {
    String sentence = gpsSerial.readStringUntil('\n');
    
    if (sentence.startsWith("$GPGGA")) {
      parseGPGGA(sentence);
    } else if (sentence.startsWith("$GPRMC")) {
      parseGPRMC(sentence);
    }
  }
  
  // Mostrar datos cada 5 segundos
  static unsigned long lastUpdate = 0;
  if (millis() - lastUpdate > 5000) {
    displayGPSData();
    lastUpdate = millis();
  }
}

void parseGPGGA(String sentence) {
  // Ejemplo simplificado de parsing
  int commaIndex[15];
  int commaCount = 0;
  
  // Encontrar todas las comas
  for (int i = 0; i < sentence.length(); i++) {
    if (sentence.charAt(i) == ',') {
      commaIndex[commaCount++] = i;
    }
  }
  
  if (commaCount >= 14) {
    // Extraer datos
    gps.timestamp = sentence.substring(commaIndex[0] + 1, commaIndex[1]);
    
    // Latitud
    String latStr = sentence.substring(commaIndex[1] + 1, commaIndex[2]);
    String latDir = sentence.substring(commaIndex[2] + 1, commaIndex[3]);
    if (latStr.length() > 0) {
      gps.latitude = convertDMSToDecimal(latStr, latDir);
    }
    
    // Longitud
    String lonStr = sentence.substring(commaIndex[3] + 1, commaIndex[4]);
    String lonDir = sentence.substring(commaIndex[4] + 1, commaIndex[5]);
    if (lonStr.length() > 0) {
      gps.longitude = convertDMSToDecimal(lonStr, lonDir);
    }
    
    // Fix quality
    String fixStr = sentence.substring(commaIndex[5] + 1, commaIndex[6]);
    gps.fix = (fixStr.toInt() > 0);
    
    // Número de satélites
    String satStr = sentence.substring(commaIndex[6] + 1, commaIndex[7]);
    gps.satellites = satStr.toInt();
    
    // Altitud
    String altStr = sentence.substring(commaIndex[8] + 1, commaIndex[9]);
    gps.altitude = altStr.toFloat();
  }
}

void parseGPRMC(String sentence) {
  // Parsing similar para velocidad y otros datos
  // Implementación simplificada
}

float convertDMSToDecimal(String dmsStr, String direction) {
  if (dmsStr.length() < 4) return 0.0;
  
  // Formato: DDMM.MMMMM (latitud) o DDDMM.MMMMM (longitud)
  int dotIndex = dmsStr.indexOf('.');
  if (dotIndex == -1) return 0.0;
  
  // Extraer grados y minutos
  String degreesStr = dmsStr.substring(0, dotIndex - 2);
  String minutesStr = dmsStr.substring(dotIndex - 2);
  
  float degrees = degreesStr.toFloat();
  float minutes = minutesStr.toFloat();
  
  float decimal = degrees + (minutes / 60.0);
  
  // Aplicar dirección
  if (direction == "S" || direction == "W") {
    decimal = -decimal;
  }
  
  return decimal;
}

void displayGPSData() {
  Serial.println("\n=== Estado GPS ===");
  Serial.printf("Fix: %s\n", gps.fix ? "SÍ" : "NO");
  Serial.printf("Satélites: %d\n", gps.satellites);
  
  if (gps.fix) {
    Serial.printf("Latitud: %.6f\n", gps.latitude);
    Serial.printf("Longitud: %.6f\n", gps.longitude);
    Serial.printf("Altitud: %.1f m\n", gps.altitude);
    Serial.printf("Hora UTC: %s\n", gps.timestamp.c_str());
    Serial.printf("Google Maps: https://maps.google.com/?q=%.6f,%.6f\n", 
                  gps.latitude, gps.longitude);
  } else {
    Serial.println("Esperando fix GPS...");
  }
  Serial.println("==================");
}

🔧 Optimización y configuración

Configurar baudrate más alto

void setup() {
  // Cambiar a 38400 baudios para mayor velocidad
  gpsSerial.begin(9600);
  delay(1000);
  gpsSerial.println("$PUBX,41,1,0007,0003,38400,0*20"); // Cambiar baudrate
  delay(500);
  gpsSerial.begin(38400);
}

Configurar frecuencia de actualización

void setUpdateRate(int rate) {
  // rate: 1000ms = 1Hz, 500ms = 2Hz, etc.
  String command = "$PUBX,40,GGA,0,1,0,0,0,0*5A"; // Configurar GGA
  gpsSerial.println(command);
}

🛠️ Proyectos prácticos con NEO-6M

1. Data Logger GPS

  • Guardar coordenadas en SD card
  • Timestamp con RTC
  • Crear tracks GPX

2. Tracker IoT

  • Enviar posición por WiFi/GSM
  • Alertas de geofencing
  • Seguimiento en tiempo real

3. Estación meteorológica móvil

  • GPS + sensores ambientales
  • Mapeo de datos climatológicos
  • Análisis geoespacial

🚨 Problemas comunes y soluciones

❌ No obtiene fix GPS

  • Ubicación: Usa el GPS en exterior, lejos de edificios
  • Antena: Verifica que la antena cerámica esté libre
  • Tiempo: Espera 1-5 minutos para cold start
  • Alimentación: Asegura voltaje estable 3.3V-5V

❌ Datos corruptos

  • Baudrate: Verifica 9600 baudios
  • Conexiones: TX del GPS → RX del ESP32
  • SoftwareSerial: Usa pines apropiados (evita conflictos)

❌ Precisión baja

  • HDOP: Valores < 2.0 son buenos
  • Satélites: Mínimo 4 para fix 3D
  • Obstrucciones: Evita árboles, edificios, túneles

📈 Mejoras y expansiones

Antena externa

Para mejor recepción en interiores:

NEO-6M ─── Cable coaxial ─── Antena externa GPS

Backup battery

Para hot start más rápido:

  • Batería de litio 3V en pin VBAT
  • Mantiene almanaque y posición aproximada

Protocolo UBX

Para configuración avanzada:

  • Mayor precisión
  • Configuración personalizada
  • Datos binarios más eficientes

El NEO-6M es el punto de partida perfecto para geolocalización en IoT. Su facilidad de uso, bajo costo y buena precisión lo convierten en la opción ideal para tracking vehicles, data logging geoespacial y sistemas de navegación en proyectos maker y profesionales.