RPCortex is a CLI operating system for the Raspberry Pi Pico series (RP2040 / RP2350), written in C++ and running natively on the metal. It turns a microcontroller into something that behaves like a computer — a real interactive shell with pipes and chaining, user accounts with hashed passwords, a package system, WiFi, and a structured boot with hardware checks.
This is the successor to RPCortex-OS, which shipped through v1.0 "Vela" on MicroPython. Same shell, same commands, same package system — without an interpreter underneath. Why the rewrite happened.
It runs on hardware with 264 KB of RAM. That constraint is still the point.
Early alpha. It boots, sets itself up, logs in, joins a network, installs and runs packages, and updates itself over the air — all confirmed on a Pico 2 W. It is still changing week to week. What is NOT done is listed at the bottom of this file rather than buried.
Hold BOOTSEL, plug the board in, drag one .uf2 onto the drive that appears.
Open a serial terminal at 115200. The logo comes up in its gradient, POST reports
what it found, and five short questions follow — root password, owner, device
name, WiFi, timezone. Then:
root@vela:/>
From there it is a shell that works. ls shows type, size, modified time and
name in columns. Arrows scroll the history and move the cursor mid-line; Ctrl+←
and Ctrl+→ jump by word; Tab completes commands, aliases and paths, and lists the
candidates when there is more than one. Ctrl+C stops whatever is running, at any
point.
Pipes (cat log | grep ERROR | wc), chaining (wifi connect && ping 1.1.1.1),
redirection (sysinfo > boot.txt) and quoting all work. ll, dir, more,
del, free, gc behave as they did on v1.
It isn't trying to be Linux. It's a $6 microcontroller. But within those constraints, it behaves like a real system — and now there's no interpreter between it and the hardware.
A filesystem. littlefs, in the same on-disk format a v1 device already uses.
ls cd pwd cat mkdir rm mv cp rename touch tree df du.
Copies stream rather than loading a file into RAM. tree draws the box
connectors. Files carry modification times, kept as a littlefs attribute since
littlefs has no timestamps of its own.
Text processing. grep wc head tail find sort uniq hex
basename dirname echo. All of them read from a pipe when given one.
Accounts. Salted SHA-256, admin roles, a NOPASS guest, protected root.
whoami users mkacct passwd usermod rmuser logout. Wrong passwords
back off on an escalating delay; three misses return to the username prompt
rather than locking the device.
Wireless. wifi scan connect add autoconnect list forget auto,
with saved networks in the registry and an SSID that can contain spaces without
quoting. Plus ping, nslookup and ntp.
System. sysinfo meminfo uptime date ver pulse freeup which
history sleep env reg reboot bootloader. meminfo reports the
largest allocatable block and a fragmentation percentage, not just free memory —
free bytes are not the number that predicts whether the next allocation works.
Packages. A package is a compiled relocatable object the OS loads at runtime,
relocates, resolves against a firmware symbol table, and runs. It registers shell
commands, which go live immediately and are swept when it unloads. pkg install
pkg remove pkg list apps unload run.
git clone --depth 1 --branch 2.3.0 https://github.com/raspberrypi/pico-sdk.git sdk
git clone --depth 1 --branch v2.11.1 https://github.com/littlefs-project/littlefs.git littlefs
./build.sh
build.sh builds both boards into out/, fetches the SDK submodules wireless
needs, and runs the host tests. ./build.sh pico2_w builds one; --clean wipes
first.
pico-sdk 2.x is required — 1.5.x has no RP2350 support. littlefs is pinned to v2.11 because that is what MicroPython's rp2 port builds, which keeps the on-disk format readable by a v1.0 device.
| Board | Chip | Notes |
|---|---|---|
| Pico 2 W | RP2350 | Primary target |
| Pico W | RP2040 | Builds and fits — the board v1.0 had to drop |
os/ the operating system
core/ pure logic — no hardware headers, all host-tested
kernel/ boot, logging, heap accounting
shell/ the command set, grouped by area
host/ host test suite; run os/host/run_all.sh
apps/ example packages
loader-spike/ the runtime package loader, and the experiment that proved it
tools/ host-side helpers (rpc-push.sh copies a package to a device)
Anything with real logic lives in os/core/ and is compiled by the host tests
with sanitizers on. That split is deliberate: there is usually no board attached,
so a bug that can only be caught on hardware is a bug that ships.
#include "rpc_app.h"
RPC_APP_VER("greet", "1.0");
static int cmd_greet(int argc, char **argv) {
fw_printf("hello from a package\n");
return 0;
}
extern "C" int app_main(int) {
rpc_register_command("greet", "say hello", cmd_greet);
return 0;
}Build it with rpc_add_app(greet) in the OS CMakeLists.txt, then:
tools/rpc-push.sh build/apps/greet.app /dev/ttyACM0
and pkg install greet.app on the device. os/apps/greet/ is the worked
example; os/include/rpc_app.h is the only header a package includes.
Kept here rather than in a status file nobody outside this repository reads.
- No Python. Packages are compiled. That is the point of the rewrite and it is still a real loss of convenience against v1.
- Fourteen packages, against v1's twenty. The rest need rewriting in C, and that is the main distance left to parity.
- No SD card support, and no ESP32-S3 port. The portable core moves unchanged; the context switch, storage and network layers do not.
- No package sandbox on RP2040. ARMv6-M protection regions are power-of-two
sized and aligned to their own size, so the five a package needs cost more RAM
than those boards have. Packages run privileged there, and
mpusays which a board is doing. - Bluetooth is new — scanning works, both LE and Classic, and little else has been exercised.
CHANGELOG.md has what v2 gained, what it matches and what it
does differently on purpose.
Proprietary — the RPCortex License. The source is public to read and learn from;
any other use needs written permission. See LICENSE.
Built by dash1101 · rpc.novalabs.app · Discord