A minimal, crash-resistant operating system for Raspberry Pi Pico with embedded SageLang scripting 🔥 NEW: Production Filesystem!
F2FS-inspired filesystem with crash recovery, hierarchical directories, and full shell integration:
> fs init 128 # 65KB RAM FS
> fs mount
> fs mkdir /app/data
> fs touch /app/config.txt
> fs write /app/config.txt "device_id=001"
> fs cat /app/config.txt
device_id=001
> fs ls /app
ino=4 type=dir
ino=5 type=file
> fs sync # Crash-safe checkpoints
> reboot
> fs mount # Auto-recovers! ✓
> fs ls /app # Data preserved! ✓See CHANGELOG.md for complete filesystem details
littleOS brings high-level scripting to bare-metal RP2040 with SageLang—a clean language with classes, generators, and automatic memory management.
| Feature | Status |
|---|---|
| 🛡️ Watchdog Protection | ✅ 8s auto-recovery |
| 💾 Production Filesystem | ✅ NEW F2FS-style, crash-safe |
| 🎯 GPIO + Hardware | ✅ Direct pin control |
| 👤 Users & Permissions | ✅ Multi-user capability system |
| 🧠 Memory Management | ✅ Heap tracking + diagnostics |
| 💾 Flash Storage | ✅ Scripts + config persistence |
| 📟 System Monitoring | ✅ Temp/RAM/uptime |
| 🔄 Auto-boot Scripts | ✅ Run on startup |
| 🧑💻 Interactive Shell | ✅ History + arrow keys |
# Ubuntu/Debian
sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential
# macOS
brew install cmake
brew tap ArmMbed/homebrew-formulae
brew install arm-none-eabi-gcc
# Arch Linux
sudo pacman -S cmake arm-none-eabi-gcc arm-none-eabi-newlib# Clone with SageLang
git clone --recursive https://github.com/Night-Traders-Dev/littleOS.git
cd littleOS
# Build with default settings (root-only)
export PICO_SDK_PATH=/path/to/pico-sdk
./build.sh
# OR: Build with interactive user account setup
./build.sh
# Flash (hold BOOTSEL button, connect USB)
cp littleos.uf2 /media/$USER/RPI-RP2/
# Connect
screen /dev/ttyACM0 115200Welcome to littleOS Shell!
Type 'help' for available commands
> version
littleOS v0.4.0 - RP2040
With SageLang v0.8.0
Supervisor: Active
> fs init 128
fs: formatted RAM FS (128 blocks, 65536 bytes)
> fs mount
fs: mounted (mount_count=1)
> fs mkdir /test
fs: created directory '/test'
> fs touch /test/hello.txt
fs: created '/test/hello.txt'
> fs write /test/hello.txt "Hello littleOS FS!"
fs: wrote 18 bytes to '/test/hello.txt'
> fs cat /test/hello.txt
Hello littleOS FS!
> fs ls /test
Listing '/test':
ino=3 type=file
> fs sync
fs: sync OK (checkpoints written)
> fs info
Superblock:
magic = 0xF2FE
version = 1
block_size = 512
segment_size = 4096
total_blocks = 128
total_segments = 16
total_inodes = 256
root_inode = 2
nat_start = 3 (blocks=4)
sit_start = 7 (blocks=1)
main_start = 8
mount_count = 1
Runtime:
free_blocks = 118
active_cp = 0
mounted = yes
backend = 128 blocksF2FS-inspired design with enterprise-grade features:
| Feature | Implementation |
|---|---|
| Size | 65KB (128×512B blocks) configurable |
| Crash Recovery | Dual checkpoints + mount_count |
| Directories | Hash lookup (djb2) + slack space |
| Inodes | 256 max, NAT log-structured |
| Files | 10 direct blocks (5KB max, expandable) |
| Commands | init/mount/ls/mkdir/touch/cat/write/sync/info |
| Recovery | fs mount auto-recovers after reboot |
| Memory | ~15KB RAM for NAT/SIT tables |
Crash Test:
fs mkdir /important; fs sync
[power loss / reboot]
fs mount
fs ls /important # WORKS! ✓Architecture:
Block 0: Superblock (CRC32 protected)
Block 1: Checkpoint 0 (active)
Block 2: Checkpoint 1 (backup)
Block 3-6: NAT (256 inodes × 8 bytes)
Block 7: SIT (16 segments × 4 bytes)
Block 8+: Data blocks (119 free)
Automatic crash recovery – System reboots after 8 seconds of inactivity:
# This will trigger watchdog reset
while(true) {} # System reboots after 8s
On recovery:
*** RECOVERED FROM CRASH ***
System was reset by watchdog timer
Protection active in:
- Shell command loop
- SageLang REPL
- Script execution
- All core operations
Capability-based permission system with multi-user support:
Default:
- Root user (UID 0) with all capabilities
- Optional non-root user (configurable at build)
Build with custom user:
./build.sh
# Interactive prompts:
# Enable non-root user account? [Y/n] y
# Username [appuser]: myapp
# UID [1000]: 1000
# Capability bitmask [0]: 0Shell commands:
# List users
> users list
# Get user info
> users get appuser
> users get 1000
# Check permissions
> perms decode 0755
Permission Mode: 0755
Rwx: rwxr-xr-x
Owner: rwx (7)
Group: r-x (5)
Other: r-x (5)
# Check permission for UID
> perms check 1000 0644 read
Permission Check:
UID: 1000
Mode: 0644 (rw-r--r--)
Action: read
Result: ALLOWEDGPIO Control:
# Blink LED on GPIO 25
gpio_init(25, true)
while(true):
gpio_toggle(25)
sleep(500)
System Monitoring:
# Real-time dashboard
while(true):
let info = sys_info()
print "Temp: " + str(info["temperature"]) + "°C"
print "Free RAM: " + str(info["free_ram"]) + " KB"
print "Uptime: " + str(sys_uptime()) + "s"
sleep(5000)
Configuration Storage:
# Persistent key-value storage
config_set("device_name", "my_pico")
let name = config_get("device_name")
config_save() # Write to flash
Central memory management core:
Heap Allocator:
- 32 KB heap (configurable in
include/memory.h) - Linked-list allocator with coalescing
- Guard bytes detect corruption
- Fragmentation tracking
Diagnostics:
> memory stats
=== Memory Statistics ===
Total Heap: 32768 bytes
Current Usage: 2048 bytes (6.3%)
Available: 30720 bytes (93.7%)
Peak Usage: 4096 bytes (12.5%)
Allocation Stats:
Total Allocated: 8192 bytes
Total Freed: 6144 bytes
Num Allocations: 32
Num Frees: 24
Fragmentation: 12%
Heap Usage: [=== ] 6%
> memory leaks
Checking for memory leaks...
No suspected leaks found
> memory test 256 10
Test: Allocating 10 blocks of 256 bytes each
Allocated: 10/10 blocks
Memory used: 2816 bytes
Freeing allocated blocks...
Memory used after free: 0 bytes
Test completeSave scripts to flash:
> storage save blink
# Paste or type script, end with Ctrl+D
gpio_init(25, true)
while(true):
gpio_toggle(25)
sleep(500)
^D
> storage list
Scripts:
0: blink (72 bytes)
> storage run blink
# LED starts blinking
> storage autoboot blink
# Will run on next boot
> storage noboot
# Disable auto-bootCore 1 supervisor monitors system health:
> supervisor status
Supervisor: Active (Core 1)
> health
System Health Report
Status: OK
Uptime: 45230 ms
Memory Used: 2048 bytes (6.3%)
Peak: 4096 bytes (12.5%)
Temperature: 28.5°C (Peak: 32.1°C)
Watchdog: Fed 150 ms ago
Core 0: Responsive (heartbeat 10 ms ago)
> stats
[Extended metrics with full system statistics]Real-time kernel logging:
> dmesg # View all messages
> dmesg -f # Follow (tail -f style)
> dmesg -l warning # Warnings and above
> dmesg -c # Clear buffer
> dmesg -s # StatisticsOutput example:
[00001] INFO: RP2040 littleOS kernel starting
[00002] INFO: Watchdog timer initialized (8s timeout)
[00009] INFO: SageLang interpreter initialized
[00010] INFO: Filesystem support loaded
[00011] INFO: Watchdog enabled - monitoring for system hangs
[00012] INFO: Boot sequence complete - entering shell
Variables & Types:
let x = 42
let name = "RP2040"
let pi = 3.14159
let active = true
let items = [1, 2, 3]
let data = {"key": "value"}
Functions:
proc calculate(x, y):
return x * y + 10
let result = calculate(5, 3)
Classes:
class Sensor:
proc init(self, pin):
self.pin = pin
gpio_init(pin, false)
proc read(self):
return gpio_read(self.pin)
let button = Sensor(15)
if button.read():
print "Button pressed!"
Generators:
proc fibonacci(n):
let a = 0
let b = 1
for i in range(n):
yield a
let temp = a + b
a = b
b = temp
for fib in fibonacci(10):
print fib
GPIO:
gpio_init(pin, is_output)– Initialize GPIO pingpio_write(pin, value)– Set output levelgpio_read(pin)– Read input levelgpio_toggle(pin)– Toggle outputgpio_set_pull(pin, mode)– 0=none, 1=up, 2=down
System Info:
sys_version()– OS version stringsys_uptime()– Seconds since bootsys_temp()– CPU temperature (°C)sys_clock()– Clock speed (MHz)sys_free_ram()– Free RAM (KB)sys_total_ram()– Total RAM (KB)sys_board_id()– Unique board IDsys_info()– Dictionary of all metricssys_print()– Print formatted report
Timing:
sleep(ms)– Delay millisecondssleep_us(us)– Delay microsecondstime_ms()– Milliseconds since boottime_us()– Microseconds since boot
Configuration:
config_set(key, value)– Set config valueconfig_get(key)– Get config value (or null)config_has(key)– Check if key existsconfig_remove(key)– Remove keyconfig_save()– Write to flashconfig_load()– Read from flash
Watchdog:
wdt_enable(timeout_ms)– Enable watchdogwdt_disable()– Disable watchdogwdt_feed()– Reset timerwdt_get_timeout()– Get timeout value
help # Show available commands
version # System and SageLang version
reboot # Software reboot
clear # Clear screen
history # Show command historyhealth # Quick system health check
stats # Detailed system statistics
supervisor # Supervisor control (start|stop|status|alerts)
dmesg # Kernel message buffer (see: dmesg --help)users # User account commands (list|get|exists|help)
perms # Permission utilities (check|decode|presets|help)memory # Memory diagnostics
# Subcommands:
# stats - Show memory statistics
# available - Show free memory
# leaks - Check for memory leaks
# test <sz> <cnt> - Test allocations
# defrag - Compact heap
# threshold <%> - Set warning levelfs init <blocks> # Format RAM filesystem
fs mount # Mount filesystem (auto-recovery)
fs mkdir <path> # Create directory
fs touch <path> # Create empty file
fs cat <path> # Read file contents
fs write <path> <str> # Write string to file
fs ls <path> # List directory (default /)
fs sync # Persist checkpoints
fs info # Show superblock + runtime stats
fs fsck # Validate filesystemsage # Start interactive REPL
sage -e "code" # Execute inline code
sage --help # SageLang helpstorage save <name> # Save new script
storage list # List all scripts
storage run <name> # Execute script
storage delete <name> # Delete script
storage show <name> # Display script content
storage autoboot <name> # Set auto-boot script
storage noboot # Disable auto-bootlittleOS/
├── boot/
│ └── boot.c # System entry & initialization
├── src/
│ ├── kernel/
│ │ ├── kernel.c # Kernel main loop
│ │ └── dmesg.c # Kernel message ring buffer
│ ├── drivers/
│ │ ├── uart.c # UART driver
│ │ ├── watchdog.c # Watchdog timer
│ │ ├── supervisor.c # Supervisor core (Core 1)
│ │ └── fs/ # 🆕 Filesystem module
│ │ ├── fs_core.c # Metadata + lifecycle
│ │ ├── fs_inode.c # Inode I/O + mapping
│ │ ├── fs_dir.c # Directory operations
│ │ └── fs_file.c # File operations
│ ├── sys/
│ │ ├── system_info.c # System monitoring
│ │ ├── permissions.c # Capability + permissions
│ │ ├── users_config.c # User database
│ │ └── memory.c # Memory management core
│ ├── storage/
│ │ ├── config_storage.c # Persistent config
│ │ └── script_storage.c # Script flash storage
│ ├── hal/
│ │ └── gpio.c # GPIO hardware abstraction
│ ├── sage/
│ │ ├── sage_embed.c # SageLang runtime glue
│ │ ├── sage_gpio.c # GPIO bindings
│ │ ├── sage_system.c # System bindings
│ │ ├── sage_time.c # Timing bindings
│ │ ├── sage_config.c # Config bindings
│ │ ├── sage_watchdog.c # Watchdog bindings
│ │ └── sage_multicore.c # Multi-core support
│ └── shell/
│ ├── shell.c # Shell main loop
│ ├── cmd_sage.c # Sage commands
│ ├── cmd_script.c # Script storage commands
│ ├── cmd_dmesg.c # Dmesg commands
│ ├── cmd_supervisor.c # Supervisor commands
│ ├── cmd_users.c # User management
│ ├── cmd_perms.c # Permission commands
│ ├── cmd_memory.c # Memory diagnostics
│ └── cmd_fs.c # 🆕 Filesystem commands
├── include/
│ ├── watchdog.h
│ ├── system_info.h
│ ├── config_storage.h
│ ├── script_storage.h
│ ├── permissions.h
│ ├── users_config.h
│ ├── memory.h
│ ├── sage_embed.h
│ ├── fs.h # 🆕 Filesystem API
│ └── hal/
│ └── gpio.h
├── third_party/
│ └── sagelang/ # SageLang submodule
├── docs/ # Detailed documentation
├── examples/ # Example scripts
├── CMakeLists.txt # Build configuration
├── build.sh # Interactive build script
└── README.md # This file
- CHANGELOG.md – Version history (v0.4.0: Filesystem!)
- docs/QUICK_REFERENCE.md – Command cheat sheet
- docs/BOOT_SEQUENCE.md – Boot process and initialization
- docs/SHELL_FEATURES.md – Shell capabilities
- docs/SYSTEM_INFO.md – System monitoring API
- docs/SCRIPT_STORAGE.md – Flash storage management
- docs/FILESYSTEM.md – 🆕 FS architecture and usage
- docs/GPIO_INTEGRATION.md – Hardware control
- docs/SAGELANG_INTEGRATION.md – Language embedding
- docs/PERMISSIONS_SECURITY.md – User system and capabilities
- docs/MEMORY_MANAGEMENT.md – Heap management
- third_party/sagelang/README.md – Full language docs
- third_party/sagelang/examples/ – Language tutorials
- Board: Raspberry Pi Pico or any RP2040-based board
- USB: For programming and serial communication
- Optional: UART adapter for dedicated serial (GPIO 0/1)
Pin Reference (Raspberry Pi Pico):
GPIO 25 - Built-in LED
GPIO 0 - UART TX (optional)
GPIO 1 - UART RX (optional)
GPIO 0–29 - Available for general use
RP2040 RAM (264 KB total):
├─ littleOS Kernel ~100 KB
├─ Stack ~100 KB
├─ SageLang Heap ~64 KB
└─ FS Tables (NAT/SIT) ~15 KB
Flash (2 MB):
├─ littleOS Binary ~150 KB
├─ Script Storage 4 KB
├─ Configuration 4 KB
└─ Free Space ~1.8 MB
| Operation | Latency |
|---|---|
| Boot time | <1s to shell |
| GPIO ops | <10μs |
| FS read/write | ~2ms |
| Temperature | ~100μs |
| Watchdog feed | <5μs |
| Script exec | Direct interpretation |
proc blink_pattern(pin, pattern):
gpio_init(pin, true)
for duration in pattern:
gpio_toggle(pin)
sleep(duration)
# Morse code "SOS"
let sos = [200, 200, 200, 200, 200, 600,
600, 200, 600, 200, 600, 600,
200, 200, 200, 200, 200, 1000]
blink_pattern(25, sos)
proc log_to_file(interval_ms):
while(true):
let temp = sys_temp()
let uptime = sys_uptime()
let log = str(uptime) + "s: " + str(temp) + "°C\n"
# Append to log file
let fd = fs_open("/logs/temp.log", FS_O_APPEND | FS_O_CREAT)
fs_write(fd, log)
fs_close(fd)
if temp > 50.0:
print "WARNING: High temperature!"
sleep(interval_ms)
log_to_file(10000) # Every 10 seconds
class LED:
proc init(self, pin):
self.pin = pin
gpio_init(pin, true)
proc on(self):
gpio_write(self.pin, true)
proc off(self):
gpio_write(self.pin, false)
class Button:
proc init(self, pin):
self.pin = pin
gpio_init(pin, false)
gpio_set_pull(pin, 1) # Pull-up
proc is_pressed(self):
return gpio_read(self.pin) == false
let led = LED(25)
let button = Button(15)
while(true):
if button.is_pressed():
led.on()
else:
led.off()
sleep(50)
# Device configuration
config_set("name", "HomeMonitor")
config_set("location", "Kitchen")
config_set("sample_interval", 30000)
config_save()
# Read back
let name = config_get("name")
let interval = config_get("sample_interval")
print "Device: " + name + " (samples every " + str(interval) + "ms)"
- Hold BOOTSEL and reconnect USB to enter bootloader
- Re-flash the UF2 file
- Check serial connection (115200 baud, 8N1)
- Try:
screen /dev/ttyACM0 115200
- Normal for infinite loops without
sleep()orwdt_feed() - Add
sleep(10)in tight loops - Monitor with
dmesgto see reset messages - Disable with
wdt_disable()for debugging
- Run
fs fsckto validate structure - Check mount_count with
fs info - Reinitialize with
fs init <blocks>if corrupted - After reboot,
fs mountauto-recovers
- Check syntax (indentation matters!)
- Verify GPIO pins are valid (0–29)
- Monitor memory with
memory stats - Use
storage show <name>to debug scripts
- Linux: Check
/dev/ttyACM*permissions:sudo chmod 666 /dev/ttyACM0 - Windows: Verify COM port in Device Manager
- macOS: Look for
/dev/tty.usbmodem*or/dev/cu.usbmodem*
- Check fragmentation with
memory stats - Run
memory defragto compact heap - Monitor for leaks with
memory leaks - Set warning threshold with
memory threshold 80
Contributions welcome! See CONTRIBUTING.md for guidelines.
Areas for Contribution:
- Flash backend for filesystem (SPI NOR/NAND)
- Indirect block support (files >5KB)
- Hardware drivers (I2C, SPI, PWM, ADC)
- Additional SageLang examples
- Documentation improvements
- Task scheduler (currently in development)
- Testing and bug reports
- Platform ports (ESP32, STM32)
Current (v0.4.0):
- ✅ Production filesystem (RAM-backed)
- ✅ Memory management core
- ✅ User & permission system
- ✅ Watchdog protection
- ✅ SageLang integration
- 🔄 Task scheduler (in development)
Planned:
- Flash backend for FS (SPI NOR/NAND)
- Indirect blocks (large files)
- Task-aware memory accounting
- Real-time scheduling preemption
- IPC mechanisms (message passing)
- I2C/SPI/PWM/ADC drivers
- Networking support
- Over-the-air updates
MIT License – see LICENSE for details.
Built for embedded education | Powered by SageLang and littleOS Core
v0.4.0 - Now with production-grade filesystem! 🚀