This repository contains the Python prototyping code for a custom ground-to-satellite RF communication link. It is designed specifically for an STM32L4-based On-Board Computer (OBC) operating within a CubeSat architecture communicating over a half-duplex RF433 module.
The protocol encapsulates telemetry and file transfer logic into a custom layered architecture using KISS framing.
The communication is structured into three contextual layers to handle data routing flawlessly across subsystems:
- Layer 1: KISS Framing (Link Layer)
- Layer 2: Routing (Subsystem Layer)
- Layer 3: Packed Data (Application Layer)
| FEND | Command | SeqNum | PayloadID | PID | DataLen | Data | CRC32 | FEND |
|---|---|---|---|---|---|---|---|---|
| 1B | 1B | 1B | 1B | 1B | 2B | < 64kB | 4B | 1B |
| 0xC0 | KISS | Running number | Subsystem | Type | Data Size | Payload | Calculate | 0xC0 |
Note: The KISS layer escapes any internal 0xC0 (FEND) or 0xDB (FESC) bytes before transmission.
Handles standard packet delimiting overhead.
FEND(0xC0): Frame End/Start marker.Command: Defines traffic direction natively without looking at payloads.0x00: Request Frame (GS -> OBC)0x01: Data/Response Frame (OBC -> GS)
SeqNum: Rolling 8-bit counter ensuring sequential delivery.PayloadID(Subsystem Dest/Src): Serves as an internal router for the OBC.0x00: OBC native (SD Card, Core Sensors)0x01: VR Payload (Pi Zero 2W)
PID(Type): The specific action requested within the subsystem context.0xAC: ACK Frame (Acknowledgment windowing).0x00–0x8F: Normal / read-only commands.0x90–0xFF: Dangerous / irreversible commands (e.g., shutdown). Reserved range to prevent accidental execution.
DataLen: 16-bit Big-Endian length of the followingDatafield.
Instead of string parsing, Data payloads are strictly packed binary C-structs.
Standard Data Types & Status Codes:
uint8_t(1 byte, unsigned) - Pythonstructformat:Buint16_t(2 bytes, unsigned) - Pythonstructformat:Huint32_t(4 bytes, unsigned) - Pythonstructformat:Iint8_t(1 byte, signed) - Pythonstructformat:b
Common Status Byte (returned in most responses):
0x00: Success (OK) /0x01: File Not Found / Generic Error /0x02: Device Busy
| Command | PayloadID | PID | Flow | Description | Subsystem |
|---|---|---|---|---|---|
| 0x00 | 0x00 | 0x00 | GS -> OBC | Request Ping | OBC |
| 0x00 | 0x00 | 0x01 | GS -> OBC | Request List files (SD) | OBC |
| 0x00 | 0x00 | 0x02 | GS -> OBC | Request File Info (SD) | OBC |
| 0x00 | 0x00 | 0x03 | GS -> OBC | Request File Data (SD) | OBC |
| 0x00 | 0x00 | 0x04 | skipped | skipped | OBC |
| 0x00 | 0x00 | 0x05 | GS -> OBC | Request System Status | OBC |
| 0x01 | 0x00 | 0x00 | OBC -> GS | Response Ping | OBC |
| 0x01 | 0x00 | 0x01 | OBC -> GS | Response List files (SD) | OBC |
| 0x01 | 0x00 | 0x02 | OBC -> GS | Response File Info (SD) | OBC |
| 0x01 | 0x00 | 0x03 | OBC -> GS | Response File Data (SD) | OBC |
| 0x01 | 0x00 | 0x04 | OBC -> GS | Beacon | OBC |
| 0x01 | 0x00 | 0x05 | OBC -> GS | System Status | OBC |
| 0x01 | 0x00 | 0xAC | OBC -> GS | OBC ACK | OBC |
| 0x00 | 0x01 | 0x00 | GS -> OBC -> VR | Request Ping | VR Payload |
| 0x00 | 0x01 | 0x01 | OBC -> VR | Request Pi Status | VR Payload |
| 0x00 | 0x01 | 0x02 | GS -> OBC -> VR | Request Capture | VR Payload |
| 0x00 | 0x01 | 0x03 | GS -> OBC | Request Copy Image to SD | VR Payload |
| 0x00 | 0x01 | 0x04 | GS -> OBC | Request Check Copy | VR Payload |
| 0x00 | 0x01 | 0x90 | GS -> OBC -> VR | Request Shutdown Pi | VR Payload |
| 0x01 | 0x01 | 0x00 | VR -> OBC -> GS | Response Ping | VR Payload |
| 0x01 | 0x01 | 0x01 | VR -> OBC | Response Pi Status | VR Payload |
| 0x01 | 0x01 | 0x02 | VR -> OBC -> GS | Response Capture | VR Payload |
| 0x01 | 0x01 | 0x03 | OBC -> GS | Copy Image to SD | VR Payload |
| 0x01 | 0x01 | 0x04 | OBC -> GS | Response Check Copy | VR Payload |
| 0x01 | 0x01 | 0x90 | OBC -> GS | Response Shutdown Pi | VR Payload |
| 0x01 | 0x01 | 0xAC | OBC -> GS | VR ACK | VR Payload |
Request Ping (PID: 0x00)
- Flow: GS -> OBC
- Request (0 byte):
Data - - Response (0 byte):
Data -
Request List files (SD) (PID: 0x01)
- Flow: GS -> OBC
- Request (0 byte):
Data - - Response (Dynamic Length):
Number of files (uint8) Filename Length Filename ASCII 1B 1B N
Request File Info (SD) (PID: 0x02)
- Flow: GS -> OBC
- Request (Dynamic Length):
Filename Length Filename ASCII 1B N - Response (9 bytes):
Status (0x00 = OK, 0x01 = Error) File Size (uint32) Created Timestamp (uint32) 1B 4B 4B
Request File Data (PID: 0x03)
- Flow: GS -> OBC
- Request (Dynamic Length):
Filename Length Filename ASCII FileOffset (uint32) ChunkLength (uint16) 1B N 4B 2B - Response (Dynamic Length):
Status (0x00 = OK, 0x01 = Error) Echoed FileOffset (uint32) Echoed ChunkLength (uint16) Raw file byte of chunk 1B 4B 2B N
Request System Status (PID: 0x05)
- Flow: GS -> OBC
- Request (0 byte):
Data - - Response (Dynamic Length):
Uptime (uint32) CPU Temp (int8) Heap Free (uint32) 4B 1B 4B
OBC ACK (PID: 0xAC)
- Flow: OBC -> GS
- Response (0 byte): Empty payload frame indicating acknowledgement.
Request Ping (PID: 0x00)
- Flow: GS -> OBC -> VR
- Request (0 byte):
Data - - Response (0 byte):
Data -
Request Pi Status (PID: 0x01)
- Flow: OBC -> VR
- Request (0 byte):
Data - - Response (16 bytes):
Timestamp (uint32) Uptime (uint32) CPU Load % (uint8) CPU Temp (signed or un8) RAM Usage (uint8) Disk Usage (uint8) Camera Status (0=Err, 1=Ready, 2=Busy, uint8) Padding 4B 4B 1B 1B 1B 1B 1B 3B
Request Capture (PID: 0x02)
- Flow: GS -> OBC -> VR
- Request (0 byte):
Data - - Response (Dynamic Length):
Status (0x00 = OK, 0x01 = Error) SavedFileNameLength (uint8) SavedFileName ASCII 1B 1B N
Request Copy Image to SD (PID: 0x03)
- Flow: GS -> OBC
- Request (0 byte):
Data - - Response (1 byte):
Status (0x00 = OK, 0x01 = Error) 1B
VR ACK (PID: 0xAC)
- Flow: OBC -> GS
- Response (0 byte): Empty payload frame indicating acknowledgement from VR subsystem.
Request Check Copy (PID: 0x04)
- Flow: GS -> OBC
- Request (0 byte):
Data - - Response (1 byte):
Status (0x00 = Idle, 0x01 = Copying, 0x02 = Error) 1B
Request VR Shutdown (PID: 0x90)
- Flow: GS -> OBC -> VR
- Request (0 byte):
Data - - Response (0 byte): ACK only — the Pi sends an empty ACK frame then immediately executes shut down. No further response will be received.
Data -
Unsolicited EPS Beacon (PID: 0x04)
- Request: None (Broadcast periodically from OBC)
- Beacon Data (121 bytes Fixed C-Struct):
RTC DateTime 8x VI Sensors 6x Out Sensors 6x Out States 2x Battery Temps 1x TMP1075 Raw 7B (date_time_t) 48B (8 * 6B) 36B (6 * 6B) 18B (6 * 3B) 8B (2 * 4B) 4B (i32)
The GS and OBC scripts contain multithreaded emulators that allow you to test this protocol logic visually in the terminal. The output is color-coded to show exactly where the Command, SeqNum, PayloadID, and Data fields sit within the raw hex strings.
- Open two terminals.
- Run
python OBC.pyin one. It will wait for incoming commands. - Run
python GS.pyin the other. It will launch an Interactive CLI.
GS CLI Interface:
--- Ground Station CLI ---
Type 'help' for a list of available commands.
GS> help
Commands:
ping <obc/vr> - Ping subsystem
list - List files on OBC SD card
info <filename> - Request file info
download <filename> - Download file from OBC
status - Request Pi Status
capture - Request Image Capture
copy - Request Copy Image to SD
shutdown - Shutdown the VR Raspberry Pi
exit - Exit Ground Station
The test_resume_downlink.py script is a stress test designed to validate the reliability of the file download resume mechanism. It simulates multiple connection drops by repeatedly terminating and restarting the Ground Station process.
- Objective: Ensure 100% data integrity of a large file (e.g.,
0.jpg) across multiple forced interruptions. - Process: Starts
GS.py, starts download, killsGS.pyevery 15s, restarts, and resumes until finished. - CSV Logging: Automatically records download progress (
test_telemetry.csv) and connection drop events (test_kills.csv). - External Visualization: Use the
visualize_results.pyscript to generate performance charts.- Requires:
pip install pandas matplotlib
- Requires:
- Hardware setup: Connect your STM32 OBC to your PC. Identify the COM port (e.g.,
COM5). - Run the test:
python test_resume_downlink.py --gs_port COM4 --kill_interval 15
- Visualize results:
python visualize_results.py --csv test_telemetry.csv --kills test_kills.csv
You can customize the output filenames and data sources:
python visualize_results.py --csv my_data.csv --kills my_drops.csv --output my_chart.png- Terminal 1 (OBC):
python OBC.py --port COM5 - Terminal 2 (Test Harness):
python test_resume_downlink.py --gs_port COM4 --kill_interval 15