Flight Software Architecture
Overview
The TEMPEST flight software (main.py) runs on a Raspberry Pi Zero and serves as the central command processor for the satellite. It receives commands via radio uplink, processes them through the appropriate subsystem, and returns binary telemetry packets via radio downlink.
System Diagram
┌─────────────────────────────────────────┐
│ Raspberry Pi Zero │
│ │
Radio Uplink ───►│ main.py │
(RFM69/RFM95) │ ├── Command Router │
│ ├── Beacon Thread │
Radio Downlink◄──│ ├── Config Watchdog │
(RFM69/RFM95) │ │ │
│ ├── subsystems/ │
│ │ ├── environmental.py ──► UART ──► RP2040 MCU
│ │ ├── obc.py (psutil) │
│ │ ├── payload.py (rpicam) │
│ │ ├── solar.py ──► I2C ──► INA219 x4
│ │ ├── eps.py ──► GPIO UART ──► ATmega328PB
│ │ └── retransmit.py │
│ │ │
│ └── utils/ │
│ ├── radio.py (SPI) │
│ └── display.py (console) │
└─────────────────────────────────────────┘
Main Loop
The FSW operates in a continuous receive-process-respond loop:
- Receive: Wait for a command string on the uplink radio
- Route: Match the command string to a handler
- Process: Execute the subsystem function
- Pack: Encode the response as a binary telemetry packet using
struct.pack - Transmit: Send the packet(s) on the downlink radio
while True:
packet = receive_uplink_message(uplink)
if packet:
command = packet.decode('utf-8').strip()
response = process_command(command)
send_downlink_message(downlink, response)
Response Types
Commands produce one of several response types:
| Type | Description | Example |
|---|---|---|
| Single packet | One binary struct packet | GET_GYRO → 16-byte GYRO packet |
| Multi-packet list | List of binary packets sent sequentially | OBC_PROCESSES → multiple OBCP chunks |
| Windowed transfer | Paced image transfer with delay between packets | SEND_IMAGE → hundreds of SEND packets at 0.25s intervals |
| Fire-and-forget | No response expected | MORSE, RESET_ENV |
Health Beacon
A background thread broadcasts satellite health telemetry at a configurable interval (default 5 seconds). The beacon packet contains:
- Uptime (seconds since boot)
- CPU usage (%)
- RAM usage (%)
- Disk usage (%)
- Temperature (°C)
The beacon can be enabled/disabled via BEACON_ON [interval] and BEACON_OFF commands.
Configuration Watchdog
The FSW monitors config.yml for changes using the watchdog library. When the file is modified, the radio configuration is reloaded without restarting the service. On startup, config.yml.bak is copied to config.yml to ensure a known-good configuration.
CLI Development Mode
cli_fsw.py provides a command-line interface version of the FSW for development and testing. It uses input() for command entry instead of radio and returns raw data without struct.pack encoding. This allows testing command handlers without radio hardware.
Startup Sequence
- Restore
config.ymlfromconfig.yml.bak - Parse configuration
- Initialize radio(s) via
setup_radios() - Initialize subsystem connections (serial, I2C, GPIO)
- Perform throwaway
POLLto flush stale UART data from RP2040 - Start beacon thread (if configured)
- Start config watchdog thread
- Enter main receive loop