
BermejaSat #14: Integración y Test Dedicado del Módulo GPS GY-NEO6MV2
Introducción
En el desarrollo de subsistemas para aeronaves y satélites, aislar las pruebas de hardware evita quebraderos de cabeza. A la espera de realizar las soldaduras definitivas del sensor de presión BMP280 en el taller del instituto, nos centramos al 100% en el subsistema de geolocalización y tiempo (GPS).
En esta decimocuarta entrega de BermejaSat, configuramos la placa Heltec V3.2 para comunicarse vía UART con el módulo GY-NEO6MV2 (u-blox NEO-6M). El sistema decodifica las sentencias NMEA mediante la librería TinyGPS++, renderiza las coordenadas y el estado de la red en la pantalla OLED (a tamaño uniforme) e inyecta la telemetría de posición a la Estación Terrena en Python.
📐 Arquitectura de Conexiones y Alimentación (¡Atención a los 5V!)
El módulo GY-NEO6MV2 integra su propio regulador interno de tensión para alimentar el chip principal a 3.3 V. Si alimentamos el módulo desde la salida de 3.3 V de la Heltec, la caída de tensión del regulador interno hace que el chip GPS reciba un voltaje insuficiente, provocando reinicios o fallos de lectura.
Por tanto, debemos alimentar la patilla VCC del GPS con 5V (obtenidos del bus de alimentación USB de la Heltec). Aunque el GPS se alimente a 5V, sus líneas de datos (TX/RX) funcionan a niveles lógicos seguros de 3.3 V, protegiendo los pines GPIO del ESP32.
[ HELTEC V3.2 ] ─────────(UART 9600)─────────► [ MÓDULO GY-NEO6MV2 ]
- GPIO 47 (RX) ◄────────────────────────────── TX GPS (Lógica 3.3V)
- GPIO 48 (TX) ──────────────────────────────► RX GPS (Lógica 3.3V)
- 5V / GND ──────────────────────────────► VCC / GND
│
└─────────────(Cable USB)────────────► [ PC ESTACIÓN TERRENA ]
- Visualización Python
- Registro CSV
Tabla de Pines
| Módulo GY-NEO6MV2 | Heltec V3.2 | Descripción |
| VCC | 5V (Pin 5V / USB) | Alimentación principal (Requiere 5V para regulador interno) |
| GND | GND | Masa común |
| TX | GPIO 47 | Transmisión del GPS (RX de la Heltec) |
| RX | GPIO 48 | Recepción del GPS (TX de la Heltec) |
📦 Instalación de Librerías en Arduino IDE
Antes de compilar el firmware, es necesario instalar la librería encarga del parseo de sentencias NMEA del GPS:
- Abre Arduino IDE y dirígete a Herramientas > Administrar Bibliotecas… (o pulsa
Ctrl + Shift + I). - En el buscador escribe exactamente
TinyGPSPlus(con la palabra Plus). - Localiza la librería desarrollada por Mikal Hart y haz clic en Instalar.
💻 1. Código Firmware (Arduino IDE): Heltec V3.2 + GPS GY-NEO6MV2
Carga este sketch en la placa Heltec V3.2. Muestra en la pantalla OLED (con tipografía de tamaño uniforme) la cantidad de satélites enganchados, el estado del fijado (Fix), latitud, longitud y altitud GPS, mientras transmite por USB la trama formateada en CSV hacia la Estación Terrena.
C++
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <TinyGPS++.h>
// --- PINES HELTEC V3.2 ---
#define OLED_SDA 17
#define OLED_SCL 18
#define OLED_RST 21
#define VEXT_PIN 36
// --- PINES UART GPS ---
#define GPS_RX_PIN 47 // Conectar a TX del GPS
#define GPS_TX_PIN 48 // Conectar a RX del GPS
#define GPS_BAUD 9600
Adafruit_SSD1306 display(128, 64, &Wire, OLED_RST);
TinyGPSPlus gps;
HardwareSerial serialGPS(2); // Usa el puerto UART2 del ESP32
int paqueteID = 0;
unsigned long ultimoEnvio = 0;
void setup() {
Serial.begin(115200);
// 1. Iniciar puerto Serie dedicado para el GPS (UART2)
serialGPS.begin(GPS_BAUD, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
// 2. Alimentación Vext y Reset de la pantalla OLED
pinMode(VEXT_PIN, OUTPUT);
digitalWrite(VEXT_PIN, LOW);
delay(100);
pinMode(OLED_RST, OUTPUT);
digitalWrite(OLED_RST, LOW); delay(20);
digitalWrite(OLED_RST, HIGH);
// 3. Inicializar Pantalla OLED
Wire.begin(OLED_SDA, OLED_SCL);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(0, 10);
display.println("BERMEJASAT - TEST GPS");
display.setCursor(0, 30);
display.println("Buscando satelites...");
display.display();
}
void loop() {
// Decodificación continua de la ráfaga NMEA entrante
while (serialGPS.available() > 0) {
gps.encode(serialGPS.read());
}
unsigned long tiempoActual = millis();
if (tiempoActual - ultimoEnvio >= 1000) {
ultimoEnvio = tiempoActual;
paqueteID++;
// Extracción y validación de variables GPS
bool fijado = gps.location.isValid();
double lat = fijado ? gps.location.lat() : 0.0;
double lng = fijado ? gps.location.lng() : 0.0;
double altGps = gps.altitude.isValid() ? gps.altitude.meters() : 0.0;
int satelites = gps.satellites.isValid() ? gps.satellites.value() : 0;
// --- TRAMA CSV POR USB PARA LA ESTACIÓN TERRENA ---
// Formato: ID,Latitud,Longitud,Altitud_GPS,Satelites,Fix
Serial.print(paqueteID); Serial.print(",");
Serial.print(lat, 6); Serial.print(",");
Serial.print(lng, 6); Serial.print(",");
Serial.print(altGps, 1); Serial.print(",");
Serial.print(satelites); Serial.print(",");
Serial.println(fijado ? 1 : 0);
// --- RENDERIZADO OLED (TAMAÑO DE FUENTE UNIFORME 1) ---
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.println("BERMEJASAT - GPS TEST");
display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
display.setCursor(0, 15);
display.print("Sats: "); display.print(satelites);
display.print(" Fix: "); display.println(fijado ? "OK" : "NO");
display.setCursor(0, 27);
display.print("Lat: "); display.println(lat, 5);
display.setCursor(0, 39);
display.print("Lon: "); display.println(lng, 5);
display.setCursor(0, 51);
display.print("Alt GPS: "); display.print(altGps, 1); display.println(" m");
display.display();
}
}
🐍 2. Código Python: Estación Terrena y Registrador de Caja Negra
⚠️ REQUISITOS PREVIOS DE EJECUCIÓN:
- Abre el Administrador de Dispositivos en tu ordenador y confirma el puerto COM asignado a la Heltec.
- Modifica la variable
PUERTO_SERIE = 'COMXX'en el script de Python con tu número de puerto.- Cierra el Monitor Serie de Arduino IDE para evitar conflictos de acceso al puerto USB.
Python
import serial
import time
import csv
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# ==============================================================================
# CONFIGURACIÓN DEL PUERTO SERIE (AJUSTAR SEGÚN TU PUERTO REAL)
# ==============================================================================
PUERTO_SERIE = 'COM22' # <--- SUSTITUYE 'COM22' POR TU PUERTO REAL (ej: 'COM3')
BAUDIOS = 115200
# Nombre de la Caja Negra para este ensayo
ARCHIVO_CSV = 'caja_negra_gps_dedicado.csv'
lista_id = []
lista_sats = []
lista_alt_gps = []
# Inicialización de la Caja Negra CSV con cabecera de geolocalización
with open(ARCHIVO_CSV, mode='w', newline='') as f:
writer = csv.writer(f)
writer.writerow(['ID', 'Latitud', 'Longitud', 'Altitud_GPS_m', 'Satelites', 'Fix', 'Timestamp'])
try:
ser = serial.Serial(PUERTO_SERIE, BAUDIOS, timeout=1)
time.sleep(2)
print(f"[OK] Estación Terrena GPS conectada con éxito en {PUERTO_SERIE}")
except Exception as e:
print(f"[ERROR CRÍTICO] No se pudo acceder a {PUERTO_SERIE}: {e}")
print("Verifica el puerto COM asignado y cierra el Monitor Serie en Arduino IDE.")
exit()
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8))
fig.suptitle('BERMEJASAT - TELEMETRÍA DEDICADA GPS (GY-NEO6MV2)', fontsize=14, fontweight='bold')
def actualizar_grafica(i):
if ser.in_waiting > 0:
try:
linea = ser.readline().decode('utf-8').strip()
partes = linea.split(',')
if len(partes) == 6:
pkt_id = int(partes[0])
latitud = float(partes[1])
longitud = float(partes[2])
alt_gps = float(partes[3])
satelites = int(partes[4])
fix = int(partes[5])
timestamp = time.strftime('%H:%M:%S')
lista_id.append(pkt_id)
lista_sats.append(satelites)
lista_alt_gps.append(alt_gps)
# Mantener los últimos 30 paquetes para animación fluida
ids = lista_id[-30:]
sats = lista_sats[-30:]
alts = lista_alt_gps[-30:]
# Registro continuo en la Caja Negra (.csv)
with open(ARCHIVO_CSV, mode='a', newline='') as f:
writer = csv.writer(f)
writer.writerow([pkt_id, latitud, longitud, alt_gps, satelites, fix, timestamp])
estado_fix = "3D FIX" if fix == 1 else "BUSCANDO"
print(f"[{timestamp}] Pkt #{pkt_id} | Sats: {satelites} [{estado_fix}] | Lat: {latitud:.5f} | Lon: {longitud:.5f} | Alt: {alt_gps:.1f}m")
# Gráfica Superior: Satélites en Cobertura
ax1.clear()
ax1.plot(ids, sats, color='green', marker='o', linewidth=2)
ax1.set_title('Satélites en Cobertura')
ax1.set_ylabel('Nº Satélites')
ax1.grid(True)
# Gráfica Inferior: Altitud reportada por el GPS
ax2.clear()
ax2.plot(ids, alts, color='purple', marker='s', linewidth=2)
ax2.set_title('Altitud GPS sobre el Nivel del Mar (m)')
ax2.set_xlabel('Paquete ID')
ax2.set_ylabel('Altitud (m)')
ax2.grid(True)
except Exception as e:
pass
ani = FuncAnimation(fig, actualizar_grafica, interval=500)
plt.tight_layout()
plt.show()
🛠️ Guía de Diagnóstico y Lecciones Aprendidas
- Alimentación a 5V obligatoria: El módulo GY-NEO6MV2 equipa un regulador lineal interno LDO que requiere 5V en su pin
VCCpara estabilizar la alimentación del chip a 3.3V. Conectarlo directamente a la salida de 3.3V provocará cuelgues o lecturas nulas. - Revisión de conexiones de señal flojas: Al trabajar en protoboard o con latiguillos DuPont, si las lecturas se pierden, verifica la presión física de los conectores en las patillas del GPS (un pequeño doblado preventivo de las patillas mejora el contacto).
- Indicador LED del estado de Fix:
- LED rojo fijo o apagado: El módulo está recibiendo alimentación pero no tiene suficientes satélites (mínimo 4) para fijar la posición 3D. Las coordenadas mostrarán
0.00000. - LED rojo parpadeando (1 Hz): Posición fijada (3D Fix). Las coordenadas reales comenzarán a actualizarse en tiempo real.
- LED rojo fijo o apagado: El módulo está recibiendo alimentación pero no tiene suficientes satélites (mínimo 4) para fijar la posición 3D. Las coordenadas mostrarán
- Pruebas en interiores: Las paredes y forjados atenúan fuertemente las señales GPS (1.5 GHz). Sitúa la antena cerámica marrón pegada a una ventana o en exteriores durante el arranque en frío (Cold Start, entre 2 y 5 minutos).
Etiqueta:AIDARAC, BermejaSat, cansat, ESA, gps, GY-NEO6MV2, Heltec V3.2, ies monterroso, montesteam, NEO-6M, python, Telemetria, TinyGPS++


