Skip to content

Latest commit

 

History

14 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

xio-sat

A 1U CubeSat-scale avionics research platform built around a hardware-enforced watchdog supervisor, an attitude determination pipeline, and a forensic black-box logging system. The goal is to demonstrate deterministic fault recovery in a multi-MCU embedded system where a silicon-level supervisor operates independently of the software it monitors.

Targeting VDAT (VLSI Design and Test), India.


Why This Exists

Software watchdogs (IWDG, WWDG, task monitors) share the same fault domain as the software they protect. If the OS or scheduler locks up, the watchdog lock up with it. This project moves the watchdog function into a dedicated eFPGA that runs its own clock domain and has direct access to the MCU reset line. The flight computer cannot disable or defer it.

The secondary problem this addresses is post-fault observability. When a satellite crashes and reboots, the question is always: what was happening in the last few seconds before the fault? This project builds toward a non-volatile "black box" ring buffer that captures the last 60 seconds of sensor data before any hardware-triggered reset.


Hardware Stack

+---------------------+        UART (115200)       +---------------------+
|   Shrike-Lite       | =========================> |   ESP32-C6 (Glyph)  |
|   (RP2040)          | <========================= |   (PCBCupid)        |
|                     |     [TLM_PACKET/CMD_INGEST] |                     |
|  - MicroPython      |                            |  - ESP-IDF          |
|  - MPU6050 (I2C1)   |        HB_TICK             |  - WiFi / MQTT      |
|  - Comp. Filter     |                            |  - NVS Forensics    |
|  - Flashes FPGA     |                            |  - HB to FPGA       |
+---------------------+                            +---------------------+
         |                                                  |
         | SPI (bitstream flash)              GPIO14 (5Hz toggle)
         |                                                  |
         v                                                  v
+------------------------------+              +---------------------+
|   SLG47910 eFPGA             |              |   SLG47910 eFPGA   |
|   (xio_guardian)             | <============|   hb_in (PIN 18)   |
|                              |  HW_RESET    |                     |
|  - 50 MHz internal clock     | ============>|   rst_out (PIN 17) |
|  - 5s heartbeat timeout      |    to RP2040 RUN pin              |
|  - Hard RST on flatline       +------------------------------+
+------------------------------+

+---------------------+      ESP-NOW (failover)    +---------------------+
|   ESP32-C6 (Glyph)  | =========================> |   Relay Node        |
|                     |     [SWARM_RELAY]           |   (Standalone ESP32)|
+---------------------+                            +---------------------+
         |
         | WiFi / MQTT
         |
         v
+---------------------+
|   Ground Hub        |
|   (Laptop)          |
|  - Mosquitto broker |
|  - ROS2 bridge      |
|  - SCADA terminal   |
+---------------------+
Component Part Role
Mission computer Shrike-Lite (RP2040) ADCS, IMU read, FPGA flash, UART telemetry
Comms node ESP32-C6 "Glyph" (PCBCupid) WiFi, MQTT uplink, heartbeat to FPGA, forensics
Watchdog supervisor SLG47910 eFPGA (Dialog/Renesas) Hardware-only fault detection, RST trigger
Sensor MPU6050 3-axis accel + gyro for attitude estimation
Relay node Standalone ESP32 ESP-NOW fallback when WiFi link is unavailable

Software Architecture

Shrike-Lite (MicroPython) -- watchdog_poc/main.py

Boot sequence:

  1. Flash the SLG47910 bitstream over SPI (shrike.flash("watch_4.bin"))
  2. Initialize UART1 at 115200 baud (TX: RP_IO8, RX: RP_IO9)
  3. Initialize I2C1 at 400 kHz (SDA: RP_IO14, SCL: RP_IO15), wake MPU6050
  4. Run 200-sample static calibration to compute pitch and roll offsets
  5. Enter 20 Hz main loop: read raw IMU, apply complementary filter, send pitch,roll\n over UART

Complementary filter:

pitch = 0.98 * (pitch + gyro_rate_x * dt) + 0.02 * acc_pitch
roll  = 0.98 * (roll  + gyro_rate_y * dt) + 0.02 * acc_roll

Gyroscope sensitivity set to 131 LSB/deg/s (default +/-250 dps range).


ESP32-C6 (ESP-IDF) -- watchdog_poc/esp_idf_hb/main/

Four modules:

heartbeat.c Toggles GPIO14 at 5 Hz from a pinned FreeRTOS task (priority 10). This is the signal the FPGA monitors. heartbeat_stop() kills the task and drives the pin hard LOW, guaranteeing a flatline that the FPGA detects within 5 seconds.

forensics.c On every boot:

  • Reads boot_count and last_reset from NVS partition "xio"
  • After a confirmed MQTT connection, calls forensics_record_boot() which increments the counter and captures esp_reset_reason()
  • Publishes boot count and last failure message to guardian/status

This means the NVS record is only committed on a clean, network-verified boot. A crash loop does not inflate the count until a successful recovery is confirmed.

mqtt_layer.c Connects to a Mosquitto broker. Publishes to three topics:

  • guardian/status -- boot count + failure reason JSON
  • guardian/telemetry -- {"pitch": x.xx, "roll": x.xx} at 20 Hz
  • guardian/report -- anomaly events (reserved)

Subscribes to guardian/command. The kill command writes a reason string to NVS, stops the heartbeat, and deliberately hangs -- forcing the FPGA watchdog to trigger a hardware reset. This is the fault injection mechanism.

adcs_telemetry_task (main.c) UART1 ingestion task. Reads from GPIO20 (RX from Shrike), accumulates bytes into a line buffer until \n, then sscanfs the pitch,roll pair and forwards it as JSON to MQTT. Buffer is 2048 bytes to handle the burst during Shrike boot and calibration.

Boot sequence in app_main:

forensics_init() -> heartbeat_init() -> wifi_init_sta() -> mqtt_wait() ->
forensics_record_boot() -> publish_status() -> launch adcs_task

Heartbeat starts before WiFi to prevent the FPGA from triggering a reset during the network bring-up window.


FPGA Watchdog -- avionics/supervisor_fpga/xio_guardian/ffpga/src/main.v

module xio_guardian (
    input  wire osc_clk,   // 50 MHz internal clock
    input  wire hb_in,     // Heartbeat from ESP32 (PIN 18)
    output reg  rst_out    // RST to RP2040 RUN pin (PIN 17)
);
    reg [27:0] count = 0;
    reg last_hb = 0;

    always @(posedge osc_clk) begin
        if (hb_in != last_hb) begin
            count   <= 0;
            last_hb <= hb_in;
            rst_out <= 1;       // System active
        end else if (count >= 250000000) begin
            rst_out <= 0;       // 5s elapsed, assert reset
        end else begin
            count <= count + 1;
        end
    end
endmodule

The counter is 28 bits. At 50 MHz, 250,000,000 ticks = 5 seconds. Any toggle on hb_in resets the counter. The FPGA has no dependency on the software stack it supervises. The bitstream is compiled for the SLG47910 and stored as FPGA_bitstream_MCU.bin. Shrike loads it over SPI at boot.


Ground Hub (ROS2) -- ground_hub/guardian_bridge/

A Python ROS2 node (GuardianBridge) that bridges MQTT to the ROS2 graph:

MQTT topic ROS2 topic Direction
guardian/status /guardian/status satellite -> ground
guardian/telemetry /guardian/telemetry satellite -> ground
guardian/report /guardian/report satellite -> ground
guardian/command /guardian/command ground -> satellite

Any message arriving on guardian/report triggers an error-level log in the ROS2 node, surfacing crash events to the operator terminal immediately.


Telemetry Label Schema

Intra-Satellite

Path Label Description
RP2040 -> FPGA [HB_TICK] Heartbeat toggle signal
RP2040 <-> ESP32-C6 [TLM_PACKET] IMU telemetry stream (UART)
RP2040 <-> ESP32-C6 [CMD_INGEST] Command downlink to RP2040
FPGA -> RP2040 / ESP32 [HW_RESET] Hardware reset assertion

Space to Ground

Source Label Description
ESP32-C6 [NOMINAL_HK] Housekeeping status (boot count, uptime)
Relay node [SWARM_RELAY] ESP-NOW relayed packets
RP2040 [ADCS_DATA] Pitch / roll telemetry
RP2040 [URGENT: CRASH] Fault report with pre-crash context
RP2040 [EVT_ANOMALY] Non-fatal anomaly event
ALL [CMD_ACK] Command acknowledgement

Repository Structure

xio_sat/
|
+-- watchdog_poc/
|   +-- main.py                  # Shrike-Lite MicroPython firmware (entry point)
|   +-- esp_idf_hb/
|   |   +-- main/
|   |       +-- main.c           # ESP32-C6 app_main, ADCS task
|   |       +-- heartbeat.c/h    # 5Hz GPIO heartbeat to FPGA
|   |       +-- forensics.c/h    # NVS boot count + reset reason
|   |       +-- mqtt_layer.c/h   # MQTT publish/subscribe, kill command
|   |       +-- wifi.c/h         # Station mode WiFi, event group sync
|   +-- esp_hb/                  # Earlier Arduino/micro-ROS prototype (superseded)
|   +-- IMU_test/                # Standalone IMU validation sketch
|   +-- watchdog/                # Early watchdog Arduino sketch (superseded)
|   +-- fpga_logic/              # Early FPGA logic tests
|
+-- avionics/
|   +-- supervisor_fpga/
|       +-- xio_guardian/
|           +-- ffpga/
|               +-- src/main.v   # Watchdog Verilog (xio_guardian module)
|               +-- build/       # Compiled bitstreams (MCU, FLASH, OTP variants)
|
+-- ground_hub/
    +-- guardian_bridge/
        +-- guardian_bridge/
            +-- guardian_bridge_node.py  # ROS2 <-> MQTT bridge node

Build and Flash

Shrike-Lite (MicroPython)

  1. Flash MicroPython firmware to the RP2040 via UF2 (hold BOOTSEL, drag UF2)
  2. Copy watchdog_poc/main.py and the shrike library to the device filesystem
  3. Copy watch_4.bin (FPGA bitstream) to the device root
  4. The script runs automatically on power-up

ESP32-C6 (ESP-IDF)

cd watchdog_poc/esp_idf_hb
idf.py set-target esp32c6
idf.py build
idf.py flash monitor

Set WiFi credentials and MQTT broker IP in wifi.h and mqtt_layer.c before building.

FPGA (SLG47910)

The bitstream is pre-compiled. shrike.flash("watch_4.bin") loads it over SPI from Shrike at boot. To recompile, open xio_guardian.ffpga in the Dialog/Renesas GreenPAK Designer tool and synthesize from ffpga/src/main.v.

Ground Hub (ROS2)

cd ground_hub
colcon build
source install/setup.bash
ros2 run guardian_bridge bridge_node

Requires a Mosquitto broker running on localhost (sudo apt install mosquitto).


Current Status

Working

  • FPGA watchdog operational: 5s timeout, hard RST via RP2040 RUN pin
  • ESP32-C6 heartbeat task: 5 Hz GPIO toggle, survives WiFi bring-up window
  • Forensics layer: NVS boot count + reset reason, only committed on clean recovery
  • Fault injection via kill command: MQTT -> heartbeat stop -> FPGA triggers reset -> NVS records reason -> next boot publishes it
  • Shrike MicroPython: FPGA flash at boot, MPU6050 wake and calibration, 20 Hz complementary filter, UART stream
  • ESP32-C6 UART ingestion: parses pitch,roll\n from Shrike, forwards as JSON to MQTT
  • ROS2 ground bridge: routes status, telemetry, report topics; command relay to ESP32

In Progress / Partially Implemented

  • Flash ring buffer: architecture defined (60s non-volatile sensor log before any FPGA reset), not yet implemented in firmware
  • [URGENT: CRASH] pipeline: heartbeat_stop() and NVS write exist, but the pre-crash data capture and guardian/report publish are not wired up
  • [EVT_ANOMALY] events: label defined in schema, no publisher yet

Planned

  • Relay node (standalone ESP32): ESP-NOW failover when WiFi link is unavailable, [SWARM_RELAY] publish
  • [CMD_INGEST] on RP2040: command parser on the Shrike side, currently one-directional
  • SGP4 orbital propagation on RP2040
  • Latency measurement tooling: time delta from [EVT_ANOMALY] to [HW_RESET]
  • Reliability heatmap: [CMD_ACK] loss rate during fault injection phases
  • Relay success rate benchmark: [NOMINAL_HK] vs [SWARM_RELAY] signal strength vs. distance

Applications

  • Deterministic fault recovery for satellite avionics
  • Uninterrupted edge nodes (agricultural IoT, remote industrial)
  • Hardware-in-the-loop (HIL) testing rig for fault-tolerant system validation
  • Resilient swarm constellations with mesh relay fallback

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages