The RedPill PocketQube satellite: a slim box with dark blue solar panels, a white payload block on top and a gold ring at the base.

Nicola Destro · Flight software and applied AI

Ask the
onboard computer

Aerospace engineering student, software lead of the J2050 PocketQube, planned to launch with ESA. Building flight software and AI.

Explore JOSContact

The background shows the Earth as seen from a satellite in a simulated orbit at 550 kilometres. The view depends on the time of your visit.

Anatomy

Take it apart

The parts of the RedPill satellite, from top to bottom.

An illustrative, stylised model built for this site: it is not the flight design, and it omits the real internal detail.

Exploded view of the RedPill satellite: payload, camera, boards, batteries, antenna ring and four side panels separated along the vertical axis.

Mission

RedPill flight software

JOS is the on-board software of the RedPill PocketQube, part of the J2050 project. Nicola leads its development. It is written for flight, so every change is gated by verification. It is under active development.

80 MHzArm Cortex-M4F on the STM32L496 OBC
FreeRTOSthrough the CMSIS-RTOS v2 API
LoRaSX1268 link with an authenticated uplink
SEUSRAM2 parity and periodic RAM scrubbing
CIbuild, static analysis, coverage gate, CodeQL on every PR

Operational state machine: select a state

Subsystem mapThe ground station talks to the on-board computer over LoRa. The OBC talks to FRAM over I2C. Links to the EPS and AOCS boards are shown dashed because their frame formats are still being defined.GroundOBCSTM32L496 + JOSEPSAOCSFRAM 512 KBLoRaI2CDashed lines: interface still being definedGroundLoRaOBCSTM32L496 + JOSEPSAOCSFRAM 512 KBI2CDashed lines: interfacestill being defined

Inside JOS

The flight software

JOS is written by Nicola Destro for the J2050 team of the Università degli Studi di Padova (UNIPD). It runs on an STM32L496 under FreeRTOS and is under active development. Pick a topic; each one has a short version and a "Go deeper" block for the details.

The decisions behind it

Nicola wrote the flight software from start to end. These are the choices that shaped it: the problem, what was chosen, what it costs, and where to look in the code.

  1. 01
    Repair memory upsets instead of only detecting them
    Problem
    Hardware parity in the protected RAM only fires when a corrupted byte is read, and it cannot repair anything: its answer is always a reboot. A flipped bit in a critical structure could sit unnoticed until the worst moment.
    Choice
    A scrubber reads the critical structures on a timer and votes over three legs: the live object, a shadow copy at another address and a reference CRC-32, with a non-volatile record in FRAM. A single upset is repaired; two different ones are recorded and contained, never guessed.
    Trade-off
    It costs discipline: the scrubber assumes a region only changes when its owner says so, so every legitimate write must be followed by a commit, atomically with the write. Forgetting it would make the scrubber undo real updates.
    Go deeper

    Every non-healthy outcome is written to the forensic log with the region, the number of flipped bits and running counters, so the radiation environment can be trended from the ground.

    In the code: Core/Src/seu_mitigation.c

  2. 02
    Log every state change, but never let a bad flash page freeze the satellite
    Problem
    A state transition is written to the forensic log before it is committed, so the trail exists even if the change fails. In an early version a failed write vetoed the transition, and a sick flash page left the software stuck in the initial state, with no way to enter the safe state on low battery.
    Choice
    The log stays before the commit, but a failed record no longer blocks the transition: the loss is counted instead. If logging fails while entering the safe state, the software still forces it.
    Trade-off
    Containment is preferred over evidence: the safe state can be entered without a persisted record. The flash write also happens inside the state lock, with a documented worst-case hold (nominally about 160 ms) and a fixed lock order.
    Go deeper

    The number of transitions committed without a record is exposed by a counter, so the gap is visible instead of silent.

    In the code: App/obsw/state_machine.c

  3. 03
    Use an interrupt mask, not the RTOS critical section, in the radiation code
    Problem
    The log can also be written from the parity fault interrupt. Inside that handler the RTOS critical-section check would spin forever.
    Choice
    The scrubber's lock masks interrupts directly. Commit is SRAM-only and safe under the lock and in the fault path; the blocking FRAM write-through runs later, in task context, outside the lock.
    Trade-off
    Interrupts are masked for a short window on every commit, and the log bookkeeping deliberately stays in the main RAM so that the block that reported a parity error is not on the path that records its own failure.
    Go deeper

    The split between commit and write-through is part of the documented ownership contract of the module.

    In the code: Core/Src/seu_mitigation.c, App/memory/memory.c

  4. 04
    A stalled task means a reset, not a silent suspension
    Problem
    The first watchdog suspended a stalled task while the hardware watchdog kept being refreshed. There was no recovery path at all: a suspended receiver task meant a deaf satellite.
    Choice
    The software monitor flags any task silent for more than three times its declared period, records it and resets the computer. Two hardware watchdogs sit behind it, and the refresh period is kept well inside the external chip's minimum timeout.
    Trade-off
    If the stalled task holds the log mutex, recording would wedge the monitor, so the escalation is deferred for at most 60 scans (about 30 seconds) and then the computer resets without the record. Recovery takes priority over the record: once the deferrals run out, the computer resets even if the record is lost.
    Go deeper

    Each scan samples every task's stack high-water mark for telemetry, and the deferral count is exposed.

    In the code: App/obsw/watchdog.c

  5. 05
    Check the image in plain software and degrade to beacon-only
    Problem
    A corrupted firmware image must not run as if it were trusted, but there is no golden image available in flight to fall back to.
    Choice
    The CRC-32 of the image is computed in pure software before any RTOS object exists, so a mis-configured peripheral cannot defeat it. The policy can reset; after its retry budget the software continues in an untrusted, constrained mode that only beacons.
    Trade-off
    There is no safe halt: the usual error handler would only let the hardware watchdog reset the part in a loop, so it is deliberately not used. The image is stamped at build time, so an unstamped build is a known case handled by policy.
    Go deeper

    The expected value is always the last four bytes of the image, so the check can be reproduced on the ground.

    In the code: App/obsw/boot_crc.c

  6. 06
    Fail closed on authentication, and say what is still weak
    Problem
    A telecommand must not be obeyed just because its structure and CRC are valid: a CRC is integrity, not authentication.
    Choice
    Without a provisioned key the image rejects every authenticated frame. The key is provided at build time through a generated file that never appears on a command line, and a bench profile is the only one that accepts the public default.
    Trade-off
    The current design has a short key and a short tag, no replay protection and no encryption. These are open items to settle before flight, and the site lists them as such.
    Go deeper

    The dispatcher reports a verdict per frame, and every verdict is counted.

    In the code: App/comms/comms.c

How it starts

  1. 01
    Reset

    The processor comes out of reset with nothing trusted yet.

    Go deeper

    Every boot starts from the initial state with an unknown battery: only the ground-commanded beacon override survives a reset.

  2. 02
    MPU and hardware watchdog

    Memory protection and the independent watchdog are switched on before anything else runs.

    Go deeper

    The IWDG (about 31 s) is started first, so a hang in the earliest code still ends in a reset. Long work before the scheduler calls a boot kick.

  3. 03
    HAL and SRAM2 parity

    The hardware layer starts and the 64 KB parity-protected RAM block is initialised.

    Go deeper

    SRAM2 parity must be initialised before any protected data is used. A corrupted read raises an NMI, is recorded, and resets the OBC.

  4. 04
    Clocks and peripherals

    Clocks and the buses to the radio, memory and sensors come up.

    Go deeper

    The radio sits on SPI1 with DMA, the FRAM bank on I2C1 and the camera bus on I2C2.

  5. 05
    Fault handlers and image CRC

    Crash handlers are installed and the firmware image is checked against its stored CRC-32.

    Go deeper

    The check runs in pure software, so a mis-configured peripheral cannot defeat it. The expected value is the last four bytes of the image, stamped at build time.

  6. 06
    Dual-bank policy check

    The bank-switch mechanism is examined.

    Go deeper

    The golden-image fallback is implemented but inhibited in this build: its slot collides with the LastStates log, so it can never arm and is not an available flight fallback.

  7. 07
    Memory and forensic log

    The FRAM bank and the LastStates log, a ring in internal Flash, are initialised.

    Go deeper

    The log is 64 slots of 128 bytes at 0x08080000. Every state transition is recorded there before it is committed.

  8. 08
    Boot faults persisted

    Anything that went wrong early in the boot is written to the log now that it can be.

    Go deeper

    The fault has to be persistable, which is why the CRC policy runs only after the log is up.

  9. 09
    Boot CRC policy

    The result of the image check decides how the software carries on.

    Go deeper

    On failure it can reset. After its retry budget it continues in an untrusted, constrained mode and can only beacon, because there is no golden image to fall back to.

  10. 10
    Radio, state machine, watchdog, SEU

    The remaining subsystems are prepared: temperature sensing, the radio, the state machine, the watchdog and radiation protection.

    Go deeper

    The return value of the radio initialisation is not checked at boot in the current snapshot. It is listed as an open item.

  11. 11
    Tasks created

    The FreeRTOS tasks are created.

    Go deeper

    Created in main(): default, state machine, watchdog, LoRa beacon, LoRa receive and SEU scrub. The AOCS, CLOUD and CLEAR task factories are not wired in main() yet.

  12. 12
    Scheduler starts

    FreeRTOS takes over.

    Go deeper

    The watchdog monitor declares the boot good only after the software has stayed up for a stability window.

What runs on the computer

Each task promises how often it will report in. The watchdog resets the computer if one stays silent for more than three times its period.

  • stateMachinecreated in main()

    Operational state machine · period 100 ms

    Go deeper

    10 Hz loop. Runs the boot sequence, polls the EPS at 1 Hz, checks the battery on its own and feeds the watchdog.

  • loraRXcreated in main()

    Uplink receiver · period 100 ms

    Go deeper

    Interrupt-driven. Validates and authenticates each frame, then dispatches the command.

  • loraBeaconcreated in main()

    Downlink beacon · period 1 to 16 min

    Go deeper

    A 128-byte beacon sent in three chunks of at most 64 bytes, at an interval that depends on the state. One transmission can hold the task for about 6 s.

  • watchdogcreated in main()

    Task liveness monitor · period 500 ms

    Go deeper

    Flags any task silent for more than three times its declared period, records it and resets the OBC. It also refreshes the hardware and the external watchdog.

  • seuScrubcreated in main()

    Radiation scrubber · period periodic

    Go deeper

    Reads the critical structures, votes over redundant copies and repairs them.

  • defaultTaskcreated in main()

    Housekeeping · period 1 s

    Go deeper

    Created by the code generator. Registered with the watchdog like every other task.

  • AOCSnot created yet

    Attitude control · period 20 ms

    Go deeper

    The task factory exists, but it is not created in main() in this snapshot. The OBC does not run attitude control: the AOCS board is a separate microcontroller.

  • CLOUDnot created yet

    Payload · period 90 min

    Go deeper

    The task factory exists, but it is not created in main() in this snapshot.

  • CLEARnot created yet

    Payload · period 1 s

    Go deeper

    The task factory exists, but it is not created in main() in this snapshot.

Surviving radiation

Charged particles flip bits in memory. JOS answers in layers, so that a single flip is repaired and a second one is contained instead of being trusted.

  1. Hardware parity

    A parity bit per byte in SRAM2. A corrupted read raises an interrupt.

    Go deeper

    It only fires when the bad byte is read and it cannot repair anything: the answer is always a reboot.

  2. Scrubbing and voting

    Every few moments the critical data is read and compared with a redundant copy.

    Go deeper

    Three legs vote: the live object, a shadow copy at another address and a reference CRC-32, plus a non-volatile record in FRAM. A single upset is repaired; two different ones are recorded and contained.

  3. Watchdogs

    A stalled task is detected and the computer restarts.

    Go deeper

    A software monitor at 500 ms, the internal watchdog and a separate external watchdog chip on the board.

  4. Forensic log

    Every event is written to a ring in flash that survives a reset.

    Go deeper

    Each scrub outcome is stored with the region, the flipped bits and the running counters, so the ground can trend the radiation environment.

Try it (simulated)

One protected byte with three legs. Flip a bit in any leg, then let the scrubber vote. Flip two different legs to see a fault that cannot be repaired.

The byte is healthy: live and shadow both match the reference.

Go deeper: the decision table
LiveShadowConclusionAction
live matchesshadow matchesHealthynothing to do
live matchesshadow differsShadow was hitshadow rebuilt from live
live differsshadow matchesLive was hitlive rewritten from the shadow
live differsshadow differs, both equalReference was hitreference recomputed
live differsshadow differs, not equalUnrecoverablerecorded and contained, never guessed

The demo uses one byte and a CRC-8 to show the logic. The firmware votes over whole regions with CRC-32.

How it is verified

This software is meant for flight, so nothing is merged without passing the same gates every time.

  • Firmware build

    The image is built with the pinned toolchain in CI, which is the authoritative build.

  • Static analysis

    cppcheck runs as a gate, and a canary proves the gate can still fail.

  • Unit tests with a coverage gate

    Host tests with Ceedling, Unity and CMock. The gate requires at least 90% of lines and 75% of branches. It is a threshold that must be met, not a measured result quoted here.

  • CodeQL

    Security analysis runs on every pull request and every push to the main branch.

  • Workflow linting

    The CI workflows themselves are checked.

Go deeper

Hardware-in-the-loop: a two-ESP32 harness (a development aid) simulates the neighbouring boards. The authoritative specification is a document set maintained by the team outside the repository; where code and documents disagree, the documents win.

Talk to the satellite

A simulated ground terminal using the real command names. Type help. Nothing is transmitted and there are no keys here.

What is real today

Implemented in the current snapshot

  • Five-state operational state machine with a transition log written before each commit.
  • Boot-time firmware image CRC-32, stamped at build time.
  • Software task-liveness monitor plus internal and external hardware watchdogs.
  • SRAM2 parity and a three-way vote and repair scrubber.
  • Crash and stack-overflow handlers that write to the forensic log.
  • LoRa link with chunked downlink and an authenticated uplink.
  • Fail-closed authentication: without a provisioned key every authenticated frame is rejected.

Still open

  • The dual-bank golden-image fallback is implemented but inhibited in this build, so it is not a flight fallback.
  • The EPS and AOCS frame formats are not yet defined by the project documentation, so those interfaces are still being defined.
  • The AOCS, CLOUD and CLEAR tasks are not created in main() yet.
  • The uplink has no replay protection and the authentication key and tag are short: strength must be confirmed before flight.
  • The radio initialisation result is not checked at boot.
  • SET_CONFIG and SEND_DATA are validated but not implemented, and there is no downlink path for the forensic log yet.

Source: the public repository documentation. Where the code and the project documents disagree, the documents prevail.

Artificial intelligence

AI, close to the metal

About and contact

Let us talk

Nicola Destro studies aerospace engineering with a specialisation in Space and leads the software of the J2050 PocketQube project. Computer science, and AI in particular, is the other half of the work. Open to internships, theses and projects in flight software and applied AI.

Earth imagery: NASA Visible Earth (Blue Marble and Earth at Night). Star positions: Yale Bright Star Catalogue (Hoffleit and Warren), via CDS Strasbourg. The orbit shown in the background is a simulation of a generic sun-synchronous orbit, not the real trajectory of the satellite.