
Chisme del Lince AstroPi06: los 10 minutos de la misión
—Lo tengo resuelto, Gata —anuncia Víctor Vílchez, el Lince de la Noche, girando la pantalla—. Diez minutos son seiscientos segundos. Pues for i in range(600) con un sleep(1). Matemáticas puras.
La Gata Montesa abre el data.csv del AstroPi05 y señala dos filas.
—18:49:48… y luego 18:49:50. ¿Dónde está el 49?
—Se… habrá ido a por café.
—Cada vuelta de tu bucle dura un segundo más lo que tarda en leer los sensores y escribir el archivo. Seiscientas vueltas son bastante más de diez minutos. Y en la ISS, a las 10:00 tu programa no termina: lo cortan.
—¿Y si lo corto yo antes?
—Eso es. Pero no contando vueltas: mirando el reloj. Como un astronauta en una caminata espacial: no cuenta pasos, mira cuánto oxígeno le queda.
Introducción
En AstroPi05 guardamos los datos de la Sense HAT en un CSV, y descubrimos que a la columna time le faltaban segundos. Hoy lo arreglamos con un temporizador en la Raspberry Pi hecho con la librería datetime de Python.
Este temporizador Raspberry Pi hace dos cosas: para el programa solo, antes de los 10 minutos de la misión, y toma una lectura cada segundo exacto, sin que el retraso se acumule.
Por qué este chisme
La guía de Mission Space Lab lo exige: cada programa tiene exactamente 10 minutos en la ISS y debe controlar cuánto tiempo lleva funcionando y pararse solo antes de que se acaben. Así termina limpio y los datos quedan bien guardados.
La guía da además un aviso importante: hay que tener en cuenta lo que tarda una vuelta del bucle. Si cada vuelta tarda unos 2 minutos, no se puede esperar hasta el minuto 10 para parar: hay que dejar sitio para que la última vuelta termine.
Qué necesitamos
- La Raspberry Pi 4 B con la Sense HAT V2, con pantalla, teclado y ratón.
- Thonny.
- Haber hecho el AstroPi05, porque usamos la misma forma de guardar el CSV.
Una carpeta para toda la serie
A partir de este chisme, todo va dentro de una carpeta astropi, con una subcarpeta por chisme:
/home/usuario/astropi/
astropi05/
astropi06/
main.py
data.csv
Crea astropi/astropi06 y guarda main.py ahí antes de ejecutar.
El simulador paso a paso
Usad el simulador del temporizador Raspberry Pi. Tiene 6 pasos: crear las carpetas, ver por qué se acumula el retraso, comprobar el límite con vueltas largas, ejecutar una prueba, comparar los datos reales del AstroPi05 y del 06, y preparar el programa para los 10 minutos.

El código
Para probar en clase, RUN_MINUTES = 1: así no hay que esperar 10 minutos.
"""
Chisme del Lince AstroPi06 - Control the 10 minutes
Raspberry Pi 4 B + Sense HAT V2.
The program stops by itself before the time limit and keeps
one reading per second without drifting.
"""
from sense_hat import SenseHat
from pathlib import Path
from datetime import datetime, timedelta
from time import sleep
import csv
base_folder = Path(__file__).parent.resolve()
data_file = base_folder / "data.csv"
print(f"Saving data in: {data_file}")
sense = SenseHat()
sense.set_imu_config(True, True, True)
# ---------------------------------------------------------------
# PART 1 - The time limits
# ---------------------------------------------------------------
RUN_MINUTES = 1 # on the ISS the slot is 10 minutes (test with 1)
SAFETY_SECONDS = 5 # finish a little before the limit
SECONDS_BETWEEN = 1 # one reading per second
start_time = datetime.now()
end_time = start_time + timedelta(minutes=RUN_MINUTES) - timedelta(seconds=SAFETY_SECONDS)
next_reading = start_time
longest_loop = timedelta(0)
count = 0
# ---------------------------------------------------------------
# PART 2 - THE IMPORTANT PART: loop while there is time for one more
# ---------------------------------------------------------------
with open(data_file, "w", newline="") as f:
writer = csv.writer(f)
writer.writerow(["time", "elapsed_s", "work_ms", "mag_x", "mag_y", "mag_z"])
while datetime.now() + longest_loop < end_time:
loop_start = datetime.now()
mag = sense.get_compass_raw()
work_ms = (datetime.now() - loop_start).total_seconds() * 1000
elapsed = (loop_start - start_time).total_seconds()
writer.writerow([loop_start.isoformat(timespec="milliseconds"),
round(elapsed, 3), round(work_ms, 1),
round(mag["x"], 2), round(mag["y"], 2), round(mag["z"], 2)])
f.flush()
count += 1
# Wait exactly until the next reading time (no drift)
next_reading += timedelta(seconds=SECONDS_BETWEEN)
wait = (next_reading - datetime.now()).total_seconds()
if wait > 0:
sleep(wait)
longest_loop = max(longest_loop, datetime.now() - loop_start)
# ---------------------------------------------------------------
# PART 3 - Summary
# ---------------------------------------------------------------
total = (datetime.now() - start_time).total_seconds()
print(f"Readings: {count}")
print(f"Total time: {total:.1f} s (limit: {RUN_MINUTES * 60} s)")
print(f"Longest loop: {longest_loop.total_seconds():.3f} s")
print(f"Done! Your file is here: {data_file}")
El código, línea a línea
Parte 1: los límites de tiempo
datetime.now()da la fecha y la hora de este instante.start_timeguarda la hora a la que empieza el experimento.timedelta(minutes=1)es una duración: un minuto. Las horas y las duraciones se pueden sumar y restar.end_time = start_time + 1 minuto − 5 segundoses la hora a la que ya no debe empezar ninguna vuelta más. Esos 5 segundos (SAFETY_SECONDS) son el margen de seguridad para cerrar el archivo con calma.next_readinges la hora a la que toca la siguiente lectura. Empieza enstart_time.longest_loopguarda lo que ha tardado la vuelta más larga hasta ahora.
Parte 2: el bucle que mira el reloj
while datetime.now() + longest_loop < end_time:es la línea clave. No pregunta «¿queda tiempo?», sino «¿cabe otra vuelta entera antes del final?». Una vuelta puede empezar a tiempo y terminar tarde. Así no pasa.work_msmide cuánto tarda el trabajo: leer el magnetómetro. Lo guardamos en el CSV para verlo.isoformat(timespec="milliseconds")guarda la hora con milisegundos, para ver la precisión real.next_reading += timedelta(seconds=1)calcula la hora exacta de la siguiente lectura.wait = next_reading − ahoraes lo que falta. El programa duerme solo eso. Si el trabajo tardó 24 ms, duerme 976 ms. Así el retraso no se acumula.longest_loop = max(...)actualiza la vuelta más larga.
Parte 3: el resumen
Al terminar, el programa dice cuántas lecturas tomó, cuánto duró y cuánto tardó la vuelta más larga. Así se comprueba a simple vista que todo ha ido bien.
Cómo probarlo
- Guarda
main.pyenastropi/astropi06y ejecútalo. Dura 1 minuto. - Nuestro resultado real fue este:
Saving data in: /home/usuario/astropi/astropi06/data.csv
Readings: 54
Total time: 54.0 s (limit: 60 s)
Longest loop: 1.000 s
Done! Your file is here: /home/usuario/astropi/astropi06/data.csv
- Abre
data.csv: la columnatimedebe avanzar un segundo exacto en cada fila.
Qué miden realmente nuestros datos
Un segundo exacto, de verdad
Las 54 lecturas de nuestra prueba cayeron todas en el milisegundo 741 de cada segundo: 19:17:40.741, 19:17:41.741, 19:17:42.741… El tiempo entre lecturas fue 1,000 s ± 1 ms.
Leer el magnetómetro tardó unos 24 ms por vuelta. Con el sleep(1) del AstroPi05, esos milisegundos se iban sumando vuelta a vuelta, y en medio minuto la hora redondeada llegó a saltarse dos segundos. No se perdió ninguna lectura, pero los datos ya no caían cada segundo.

Por qué 54 lecturas y no 60
El límite de la prueba era 60 s. Le quitamos 5 s de margen: end_time quedó en el segundo 55. La última vuelta empezó en el segundo 53. En el 54 ya no cabía otra vuelta de 1 s antes del 55, así que el programa paró solo. Exactamente lo que queríamos.
El magnetómetro, otra vez
Nuestro magnetómetro volvió a arrancar bajo: 7,7 µT en la primera lectura, y tardó varios segundos en estabilizarse cerca de 38 µT. Ya lo vimos en el AstroPi05. Cuando algo se repite en dos experimentos distintos, deja de ser casualidad: en la ISS conviene descartar las primeras lecturas.
Temporizador Raspberry Pi para la ISS
Para el vuelo de verdad solo hay que cambiar una línea:
RUN_MINUTES = 10
Con 5 s de margen, el programa toma unas 595 lecturas y termina solo hacia el minuto 9:55. El reloj de la misión empieza cuando la Astro Pi lanza el programa: importar las librerías y preparar la Sense HAT también cuentan, y el margen lo cubre.
Si en vuestro experimento cada vuelta tarda mucho (por ejemplo, porque hace una foto y la procesa), longest_loop se encarga de no empezar una vuelta que no vaya a terminar a tiempo.
Para el aula
- Predice: con
RUN_MINUTES = 1ySAFETY_SECONDS = 5, ¿cuántas lecturas saldrán? Comprobadlo. - Cuentas: si cada vuelta tarda 2 minutos, ¿cuántas vueltas caben en 10 minutos con este programa? ¿Y sin la comprobación de
longest_loop? - Compara: abrid los CSV del AstroPi05 y del AstroPi06 y calculad el tiempo entre la primera y la última lectura de cada uno.
- Experimenta: subid
SECONDS_BETWEENa 5. ¿Cuántas lecturas salen ahora? ¿Sigue cayendo cada una en el mismo milisegundo?
Retos
Reto 1
Haz que funcione: guarda main.py en astropi/astropi06 y comprueba que la columna time avanza un segundo exacto.
Reto 2
Cambia SAFETY_SECONDS a 0, a 5 y a 20. Anota cuántas lecturas salen y explica por qué.
Reto 3
Añade al CSV el acelerómetro y el giroscopio, como en el AstroPi05. ¿Cuánto sube work_ms?
Reto 4
Añade un sleep(3) dentro del bucle, para simular una vuelta lenta. ¿Termina el programa a tiempo? ¿Qué vale longest_loop?
Reto 5
No guardes las lecturas de los 10 primeros segundos, para quitar el arranque del magnetómetro.
Reto 6
Prepara la versión de vuelo con RUN_MINUTES = 10 y déjala funcionando los 10 minutos completos. Comprueba que el resumen dice menos de 600 s.
Qué hemos aprendido
- Un programa de la ISS se para mirando el reloj, no contando vueltas.
datetimeytimedeltapermiten sumar y restar horas y duraciones.- La condición
datetime.now() + longest_loop < end_timegarantiza que la última vuelta cabe entera. - Esperar hasta la hora exacta de la siguiente lectura evita que el retraso se acumule.
- Un temporizador Raspberry Pi bien hecho deja datos regulares y un programa que termina solo y limpio.
Próximamente: AstroPi07, probar el programa completo con el Astro Pi Replay Tool.
Para seguir investigando
- AstroPi05: guarda tus datos en CSV
- AstroPi03: los sensores que sí cambian en órbita
- Documentación de datetime de Python
- Guía oficial de Mission Space Lab (creator guide)

Etiqueta:AIDARAC, Astro Pi, datetime, ies monterroso, Mission Space Lab, montesteam, python, Raspberry Pi, Sense HAT, temporizador



