BermejaSat #13: Ensayo Telemétrico por Cable (Tethered Test), Calibración Relativa y Estación Terrena en Python
Introducción
En el desarrollo de cualquier sistema espacial, antes de arriesgar una transmisión por radiofrecuencia, se realiza un ensayo por cable (tethered test). Este procedimiento nos permite aislar los fallos de hardware o lecturas erróneas de los problemas causados por interferencias, pérdidas de cobertura o atenuación de la señal de radio.
En esta decimotercera entrega de BermejaSat, configuramos un bucle local de telemetría de alta fiabilidad: el CanSat lee la presión del sensor BMP280, calcula la altitud relativa (fijando el punto de calibración a $0.0\text{ m}$ en el suelo), refresca todos los datos en la pantalla OLED interna y los envía por el puerto USB/Serie a nuestro ordenador para graficar la trayectoria y generar el registro de la Caja Negra.
📐 Arquitectura del Ensayo por Cable
[ SENSOR BMP280 ]
│ (I2C Externo: Pines 41/42)
▼
[ HELTEC V3.2 (CanSat) ] ─────────(Cable USB / Serie)─────────► [ PC ESTACIÓN TERRENA ]
- Calibración del Cero al arrancar - Grafica en vivo (Python)
- Visualización uniforme en OLED (Pines 17/18) - Registro CSV (Caja Negra)
💻 1. Código Firmware (Arduino IDE): Heltec V3.2 + BMP280
Carga este programa en la placa Heltec V3.2. Al iniciar, el microcontrolador realiza una lectura inicial de presión atmosférica para establecer el nivel del suelo como $0.0\text{ metros}$. Todos los datos se muestran en la pantalla OLED interna con un tamaño de fuente uniforme para facilitar la lectura.
C++
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_BMP280.h>
// --- PINES HELTEC V3.2 ---
#define OLED_SDA 17
#define OLED_SCL 18
#define OLED_RST 21
#define VEXT_PIN 36
#define BME_SDA 41
#define BME_SCL 42
Adafruit_SSD1306 display(128, 64, &Wire, OLED_RST);
Adafruit_BMP280 bmp(&Wire1);
float presionSuelo = 1013.25;
int paqueteID = 0;
bool sensorOk = false;
unsigned long ultimoEnvio = 0;
const long intervalo = 1000; // Refresco cada 1 segundo
void setup() {
Serial.begin(115200);
// 1. Activar alimentación periférica y reset de 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);
// 2. Inicializar buses I2C (Wire = OLED, Wire1 = Sensor)
Wire.begin(OLED_SDA, OLED_SCL);
Wire1.begin(BME_SDA, BME_SCL);
// 3. Inicializar Pantalla OLED
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(0, 10);
display.println("BERMEJASAT - HELTEC V3");
display.setCursor(0, 30);
display.println("Calibrando Cero...");
display.display();
// 4. Inicializar BMP280 y fijar la altitud cero del suelo
if (bmp.begin(0x76)) {
sensorOk = true;
delay(500);
presionSuelo = bmp.readPressure() / 100.0F; // Cero de altitud
}
}
void loop() {
unsigned long tiempoActual = millis();
if (tiempoActual - ultimoEnvio >= intervalo) {
ultimoEnvio = tiempoActual;
if (sensorOk) {
paqueteID++;
float temp = bmp.readTemperature();
float pres = bmp.readPressure() / 100.0F;
float altitud = bmp.readAltitude(presionSuelo);
// --- TRAMA CSV PARA PYTHON POR USB ---
// Formato: ID,Altitud,Presion,Temperatura
Serial.print(paqueteID); Serial.print(",");
Serial.print(altitud, 2); Serial.print(",");
Serial.print(pres, 2); Serial.print(",");
Serial.println(temp, 2);
// --- RENDERIZADO EN PANTALLA OLED (TAMAÑO 1 UNIFORME) ---
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.println("BERMEJASAT - TETHERED");
display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
display.setCursor(0, 15);
display.print("Altitud: ");
display.print(altitud, 1);
display.println(" m");
display.setCursor(0, 27);
display.print("Presion: ");
display.print(pres, 1);
display.println(" hPa");
display.setCursor(0, 39);
display.print("Temp: ");
display.print(temp, 1);
display.println(" C");
display.setCursor(0, 51);
display.println("Modo: USB Direc.");
display.display();
}
}
}
🐍 2. Código Python: Estación Terrena y Graficador en Tiempo Real
⚠️ ¡ATENCIÓN IMPORTANTE ANTES DE EJECUTAR EN EL AULA!
Debes identificar en qué puerto COM se ha conectado tu placa Heltec.
- Abre el Administrador de Dispositivos en Windows (o comprueba el menú Herramientas > Puerto en Arduino IDE).
- Modifica la variable
PUERTO_SERIE = 'COMXX'en el código de Python con tu número de puerto correspondiente (por ejemplo'COM3','COM7','COM22', etc.).- Cierra el Monitor Serie de Arduino IDE antes de lanzar el script de Python, ya que dos programas no pueden acceder al mismo puerto serie simultáneamente.
Python
import serial
import time
import csv
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# ==============================================================================
# CONFIGURACIÓN DEL PUERTO SERIE (¡CAMBIAR SEGÚN TU PLACA Y EQUIPO!)
# ==============================================================================
PUERTO_SERIE = 'COM22' # <--- SUSTITUYE 'COM22' POR TU PUERTO REAL (ej: 'COM3')
BAUDIOS = 115200
# Archivo de Caja Negra
ARCHIVO_CSV = 'caja_negra_ensayo_cable.csv'
# Listas en memoria para la representación gráfica
lista_id = []
lista_altitud = []
lista_presion = []
lista_temp = []
# Crear archivo CSV e inscribir la cabecera oficial
with open(ARCHIVO_CSV, mode='w', newline='') as f:
writer = csv.writer(f)
writer.writerow(['ID', 'Altitud_m', 'Presion_hPa', 'Temperatura_C', 'Timestamp'])
# Apertura de la conexión por puerto Serie
try:
ser = serial.Serial(PUERTO_SERIE, BAUDIOS, timeout=1)
time.sleep(2)
print(f"[OK] Estación Terrena conectada con éxito en {PUERTO_SERIE}")
except Exception as e:
print(f"[ERROR CRÍTICO] No se pudo acceder a {PUERTO_SERIE}.")
print("Comprueba que has puesto el puerto COM correcto y que el Monitor Serie de Arduino está CERRADO.")
exit()
# Configuración del lienzo de visualización dinámica
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8))
fig.suptitle('BERMEJASAT - ENSAYO DE TELEMETRÍA POR CABLE (USB)', 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) == 4:
pkt_id = int(partes[0])
altitud = float(partes[1])
presion = float(partes[2])
temperatura = float(partes[3])
timestamp = time.strftime('%H:%M:%S')
# Almacenamiento en estructuras locales
lista_id.append(pkt_id)
lista_altitud.append(altitud)
lista_presion.append(presion)
lista_temp.append(temperatura)
# Mantener los últimos 30 paquetes para desplazar la gráfica
ids = lista_id[-30:]
alts = lista_altitud[-30:]
press = lista_presion[-30:]
# Escritura en tiempo real en la Caja Negra (.csv)
with open(ARCHIVO_CSV, mode='a', newline='') as f:
writer = csv.writer(f)
writer.writerow([pkt_id, altitud, presion, temperatura, timestamp])
print(f"[{timestamp}] Pkt #{pkt_id} | Alt: {altitud:.2f} m | P: {presion:.2f} hPa | T: {temperatura:.2f} °C")
# Gráfica Superior: Altitud Relativa
ax1.clear()
ax1.plot(ids, alts, color='red', marker='o', linewidth=2)
ax1.set_title('Perfil de Altitud Relativa (m)')
ax1.set_ylabel('Altitud (m)')
ax1.grid(True)
# Gráfica Inferior: Presión Atmosférica
ax2.clear()
ax2.plot(ids, press, color='blue', marker='s', linewidth=2)
ax2.set_title('Presión Atmosférica (hPa)')
ax2.set_xlabel('Paquete ID')
ax2.set_ylabel('Presión (hPa)')
ax2.grid(True)
except Exception as e:
pass # Descartar líneas incompletas de arranque
ani = FuncAnimation(fig, actualizar_grafica, interval=500)
plt.tight_layout()
plt.show()
📋 Pasos para Realizar la Prueba en el Laboratorio
- Conecta la Heltec V3.2 con el módulo BMP280 al ordenador mediante el cable USB de datos.
- Carga el script de Arduino IDE.
- Al encender, verifica en la OLED que la lectura de altitud marca
0.0 m. - Cierra el Monitor Serie de Arduino IDE.
- Abre la consola de comandos de Python y ejecuta el script (tras haber cambiado el puerto
COMXX). - Validación dinámica: Eleva manualmente la placa o sopla suavemente sobre el sensor BMP280; observarás la curva de altitud subir dinámicamente en el monitor de Python mientras el archivo
caja_negra_ensayo_cable.csvregistra cada evento.
Ficha de Metadatos para la Publicación (MonteSTEAM)
- Slug:
bermejasat-13-ensayo-telemetrico-cable-calibracion-relativa-estacion-terrena-python - Extracto: Decimotercera entrega de BermejaSat: realización del ensayo telemétrico por cable (Tethered Test). Incluye la calibración del punto cero de altitud en la Heltec V3.2, renderizado OLED en tamaño uniforme, envío USB-Serie y la Estación Terrena en Python con gráficas y Caja Negra.
- Palabras clave: BermejaSat, CanSat, ESA, Heltec V3.2, BMP280, Python, Matplotlib, Serial COM, Caja Negra, MonteSTEAM, IES Monterroso, AIDARAC.
Etiquetas: AIDARAC, BermejaSat, CanSat, ESA, ies monterroso, montesteam, Arduino, ESP32, Heltec, Python, Telemetria, Hardware, Software
Etiqueta:AIDARAC, BermejaSat, BMP280, Caja Negra, cansat, ESA, Heltec V3.2, ies monterroso, matplotlib, montesteam, python, Serial COM


