Index · 01

picodroid

A stripped-down, FreeRTOS-based Android-like runtime for the Raspberry Pi Pico. Java apps run on a JVM interpreter written in Rust, directly on bare-metal hardware.

Status
Active
Commits
900+
Started
2026-03
Licence
GPL-3.0
[Photo of a Pico running a picodroid app]

Why

Android's app model is a good way to structure small interactive programs: activities with a lifecycle, a back stack, views, preferences, background threads. It also assumes a phone. picodroid asks how much of that model survives on a four-dollar microcontroller with a few hundred kilobytes of RAM.

The answer so far is: enough to write real apps in Java against an Android-compatible API, install them over USB without reflashing the firmware, and run them on both cores under FreeRTOS.

What runs today

  • ActivitiesFull lifecycle with a back stack
  • UILVGL-backed widgets: Toast, AlertDialog, Keyboard, gestures, property animations
  • StorageFiles on LittleFS, SharedPreferences
  • NetworkingTCP, UDP and HttpURLConnection over WiFi on the Pico 2 W
  • SensorsSensorManager with a BME688 environmental sensor
  • ThreadsThread.start() creates FreeRTOS tasks; concurrency utilities
  • MemoryMark-sweep garbage collector, runs every 256 allocations
  • ToolingHot-swap installs and a device monitor over USB, plus a desktop simulator with mouse-as-touch

Build log · month by month

Newest first

Shrinking the image

Method and field names join the shrinker: 868 framework names remapped, and a --shrink image no longer carries a single original Java name. Dead .class debug attributes stripped from device firmware. Measured together on the RP2040 image: 969,452 bytes down to 959,911.

14 commits so far · shrink maps v0.15, v0.16

WiFi for real, then Kotlin

The cyw43 port finally links and runs: a PIO gSPI transport with the WiFi task on core 1, typed java.net exceptions, HttpURLConnection headers and timeouts, and an Enviro+ board on the Pico 2 W. The picoenvmon app grew a live web dashboard, an NTP clock and internet weather. Runtime flash writes fixed on both chips. Then Kotlin became a first-class app language in eight sessions, alongside compile-time dependency injection with @Inject, @Singleton and @Module, synchronized, wait/notify, and a pure-Java java.util.concurrent core. A CI size ratchet stops the binary creeping back up.

236 commits · v0.12.0, v0.13.0, v0.14.0

Memory diagnostics and a shared core

An opt-in diagnostics suite: heap monitor with a growth sentinel, per-class allocation histogram, offensive heap checks, and the JVM heap block readable over USB. Objects moved to a 32-bit-clean layout with 12-byte slots, and the simulator started modelling the device heap for real. Sensor I2C moved to its own FreeRTOS sampler task. Framework code was extracted out of the firmware binary into picodroid-core behind HAL, RTOS and host seams, and the PAPK and PDB wire formats became crates of their own.

105 commits · v0.11.0

Android parity

v0.10.0 brought typed listener interfaces, the Adapter pattern, and keypad and focus navigation. Then a long sweep to match Android's shapes exactly: SharedPreferences, HttpURLConnection casing, AlertDialog into picodroid.app, IBinder into picodroid.os, the Gravity, InputType and sensor constant sets. The JVM gained Integer.parseInt, getClass, clone, System.arraycopy, Comparator sorting and suppressed exceptions. New widgets: NumberPicker, RadioGroup, TextWatcher, SeekBar tracking, AlertDialog lists, long-click, and startActivityForResult.

98 commits · v0.10.0

A second chip family, smaller objects

Relicensed to GPL-3.0, and the tree reorganised into platforms/ with a shared core, so an ESP32-S3 board could join: helloworld ran on a Lilygo T-Deck Plus. PAPK 1.1 bundles image assets mapped straight from flash. Tier C widgets landed: Snackbar, DatePicker, TimePicker, SwipeRefreshLayout. The Pimoroni Enviro+ Pack was brought up. JVM objects shrank to 24 bytes or less, cutting helloworld's peak heap from 51 KB to 25 KB, with a GC-starvation fix for callback-driven apps and new speed and graphics benchmarks.

94 commits · v0.7.0, v0.8.0, v0.9.0

A display, an app model, and a filesystem

The busiest month. LVGL on an ST7789 with XPT2046 touch, then an Android-like View and widget API on top. Activity and Application lifecycles, a back stack, Toast, AlertDialog, gestures, property animations, themes and a soft keyboard. The interpreter picked up lambdas, synchronized, static initialisers, enums, HashMap, iterators and lazy class loading, plus a garbage collector and an arena allocator. LittleFS arrived with File streams and Preferences, sockets got a Java API, a BME688 SensorManager and hardware buttons went in, PDB moved to USB CDC, and a nightly hardware-in-the-loop harness started running. Services, dependency injection, Executors, Gradle for 37 apps, and the first tagged release.

218 commits · v0.1.0 to v0.6.0

Bring-up

An LED blink on 5 March. FreeRTOS-rust ported to the Cortex-M0+ the next day, and Java Hello World running on the RP2040 by the 8th. The rest of the month was the interpreter: arrays, inheritance, interfaces, floats, exceptions, threads, statics, switch, long and double, strings, ArrayList, Math. Around it: RP2350 support, UART, I2C, SPI, PWM and ADC with Android-inspired APIs, the JVM split into its own pico-jvm crate, the PAPK package format, PDB as picodroid's answer to adb, a host simulator, SMP on both cores, a system monitor, and a porting guide.

160 commits · pre-release