Releases: PicoDeck/picodeck
Release list
v0.5.0
PicoDeck v0.5.0
A logical gamepad you can rebind, an OS frame counter, gapless video audio, and MP3 playback without skips.
PicoDeck runs on a PicoCalc fitted with a Pimoroni Pico Plus 2 W. On v0.3.0 or later, System Update installs this release; from v0.2.0 or earlier, install picodeck.uf2 with BOOTSEL (see the v0.3.0 notes).
Gamepad
- A gamepad for games. Twelve buttons (D-pad, A, B, X, Y, L, R, Start, Select) that games read instead of raw keys. Each is an alias for keys, so the keys still work as themselves. Defaults: arrows, A = F4, B = F5, X = Delete, Y = Backspace, L = F2, R = F3, Start = F1, Select = Tab. Lua reads it through
picocalc.gamepad, native apps throughapi->gamepad. See API Gamepad. The bundled games move onto it in a later release. - Settings → Controls. Rebind any button to a primary and an alternate key, for all games or, from inside a game, just that one. Bindings are saved in
/system/gamepad.jsonand/data/<app id>/gamepad.json. - Delete key. Delete now registers on the device (
BTN_DEL); it used to work only in the simulator.
System menu
- Show FPS. A new Settings item shows a frame counter, drawn by the OS, in any corner for every app, Lua or native. Off by default.
- The app gets its screen back. Closing the menu restores the app's screen at once. Apps that redraw only when something moves, such as Panels Demo, no longer keep a dimmed screen or the closed menu until the next redraw.
- More room for settings. The system settings store holds more keys, and uses less memory doing it.
Audio and video
- MP3 skips fixed. MP3 playback dropped a frame about every 1.3 s at 128 kbps (every 5 s at 96 kbps), in music and video soundtracks alike. Every frame now plays.
- Video audio. A looping video keeps its sound across the loop instead of restarting it, resuming after a seek while paused has sound, the volume you set is kept across loops, seeks and
load(), and low-frame-rate videos no longer lose part of their audio. A short gap remains when you seek (#20). - FilePlayer.
setLoopRange(start, end)now loops that range;load()returnsniland the reason for a file it can't play;didUnderrun()andsetStopOnUnderrun()work; newgetSampleRate().
Graphics
- Pixel access from Lua.
image.newplusimg:getPixel/setPixel, bulkimg:getPixels/setPixels, anddisplay.getPixel, so apps can build textures at runtime (for example fordrawPlaneanddrawTexturedColumn). drawScaledrotates in radians on the device, as documented (it used degrees), draws without a stray wrong-coloured edge, and rejects a scale that would draw beyond 4096 px with an error saying it takes a multiplier.drawanddrawStretchedaccept a positional source rectangle,{x, y, w, h}.- Fonts:
display.loadFontreturnsniland the reason on failure, andgraphics.font.newnames the actual problem. - Animations:
animator.newworks with its documented four- and five-argument forms, including a start delay.
For app developers
- Native API version 9 adds the
gamepadtable (checkapi->version >= 9). - Native changes:
zip->readreturns -2 (PCZIP_ERR_TOO_SMALL) when the buffer is too small, so test for< 0, not== -1.mp3PlayerPlay(mp, 0)now loops until stopped, like the other players.http->newConnwith port 0 uses the scheme's default port.
- Lua
fs.seek(file, offset, whence)takes"set","cur"or"end". - Pacing: an app no longer inherits the previous app's frame pacing.
- SDK:
sdk/lua/picocalc.luais corrected (about 40 wrong signatures and returns), and CI now checks it against the firmware. The docs gain the Lua HTTP flow, the ZIP archive object, the terminal's native-only calls, the Lua 5.4 removals (unpack,math.atan2, …), everyapplyEffecteffect and the transparent-colour rules. The SDK's sample app now handles Exit App.
Known issues
- Seeking a video leaves a short audio gap (#20).
- With Shift held, the keyboard sends nothing for Left, Right, Backspace and Space. This comes from the keyboard's own firmware.
- Plain buttons (not the gamepad) can stay held if Shift is pressed or released while a key is down.
v0.4.0
PicoDeck v0.4.0
A new look for the launcher: a status header with icons, and 24×24 app icons.
PicoDeck runs on a PicoCalc fitted with a Pimoroni Pico Plus 2 W. On v0.3.0, System Update installs this release; from v0.2.0 or earlier, install picodeck.uf2 with BOOTSEL (see the v0.3.0 notes).
Launcher
- Status header. Grey icons on a navy gradient bar replace the green "WiFi Bat:100%" text; colour now only flags a problem. The WiFi icon is bright when online, dim while connecting, crossed out in red on failure and hidden when WiFi is off. The battery shows a fill bar, turns red at 15% or below, and gets a bolt while charging. The clock appears once the time is set.
- Battery %. A new Settings toggle shows the charge as a number inside the battery instead of the bar.
- App icons. Icons are 24×24 (were 20×20) in roomier rows, 8 to a screen. Apps can ship an
icon.pngin their folder; those without one get a pixel-art cartridge in their category colour with their first letter. 3D Test, App Store, Burn-In Fixer, C64, Calculator and DOS/86 now have icons. See "Add an icon" in Creating your first app.
Browser demo
- picodeck.net/try now opens on 3D Test, block.exe, Calculator, Text Editor, Neurogram and Snake. Hello World, Graphics Test and Font Test are no longer in the demo.
/try/?app=<folder>starts that app directly; picodeck.net's home page uses it to launch apps.- Keys reach the simulator only while its screen has focus, so the page around it scrolls and types normally.
picodeck-web-sim.zipalso carriesshell.js(the page glue) andlauncher.png(the launcher, captured from the simulator), which picodeck.net uses.
Fixes
- C64 Emulator: looks for its ROMs under the new
/data/net.picodeck.c64/folder; v0.3.0's build still used the old id. - GBC Emulator: updated for the PicoDeck rename.
- The README explains how to move an SD card over from v0.2.0 (apps,
/datafolders, native rebuilds).
For app developers
drawHeadernow returns the rows it covers (21), andperf.drawFPS()defaults to the top-right corner below the header.- The simulator's control socket adds
set_batteryand theset_wifi_statestatesconnectingandfailed.
PicoDeck v0.3.0
PicoDeck v0.3.0
PicOS is now PicoDeck: same firmware and app model, new name and home.
- Code: https://github.com/PicoDeck/picodeck (old github.com/jeffory/PicOS links redirect)
- Website and docs: https://picodeck.net · https://picodeck.net/docs/ (the GitHub wiki is retired)
- Try it in your browser: https://picodeck.net/try/
- App Store: https://store.picodeck.net (picos.jeffory.dev is gone)
Upgrading a device from PicOS v0.2.0
Flashing the firmware does not update the SD card, so do all four steps:
- Flash
picodeck.uf2(hold BOOTSEL while connecting USB, copy the file to the drive that appears). v0.2.0's store and updater can't do this: they look for the old host and file names. - Replace the app folders in
SD:/apps/with this release'sapps/folder (from the source code zip). The old App Store looks forpicos.jeffory.dev, which is gone, and the old apps use the oldcom.picos.*ids. - Rename each
SD:/data/com.picos.<app>folder toSD:/data/net.picodeck.<app>so the apps find their saves. - Native apps compile their data path in, so rebuild them:
make -C apps/c64(the C64 ELF in this release's source zip still reads the old path; fixed ondevelop),make -C apps/gbc, and C-Dogs fromPicoDeck/cdogs.
Changes that affect you
- Release files are now
picodeck.{uf2,bin,sha256,sig}andpicodeck-simulator-linux, pluspicodeck-docs.zipandpicodeck-web-sim.zip(the website is built from them). - First-party app IDs moved from
com.picos.*tonet.picodeck.*(see step 3 above). - Store listings use the GitHub topic
picodeck-app(waspicos-app). - The native SDK entry point is
picodeck_main. Existing ELF apps still load (the loader starts at the ELF entry address), but any app with a/data/<id>path compiled in needs rebuilding to find its data. - The USB device is "PicoDeck Device" (
/dev/serial/by-id/usb-Raspberry_Pi_PicoDeck_Device_*).
New
- Browser simulator at https://picodeck.net/try/: the launcher and Lua apps run in the browser with no install (no networking or native apps); saves stay in your browser.
- Docs now live in this repo (
docs/) and are published at https://picodeck.net/docs/, updated with each release. The wiki's pending corrections from the v0.2.0 hardening are applied, and a new Simulator and Testing page covers the simulator's control protocol and dev commands.
PicOS v0.2.0
Highlights
Signed firmware updates. OTA images must carry an ECDSA P-256 signature (picocalc_os.sig, published with every release) that verifies against the key built into the running firmware. sys.applyUpdate also needs an on-screen confirm that apps cannot pre-answer, and the boot flashes an image only with a one-shot token set after that confirm. Refused images are renamed update.bin.rejected and never retried.
TLS certificates are verified. HTTPS and tls:// connections now check the server's chain (built-in CA bundle), validity dates and host name, and wait for SNTP to set the clock. Randomness comes from a TRNG-seeded CTR_DRBG. setInsecure(true) (Lua) or PCTCP_TLS_INSECURE (native) opts one connection out, for self-signed dev servers.
App sandbox enforced in C. Permissions come from the launcher's record of the running app, not the writable APP_* globals. The sound, MOD, video and sha256File loaders are path-checked like picocalc.fs; picocalc.sysconfig needs the "sysconfig" requirement and sys.applyUpdate needs "system-update".
Apps clean up after themselves. Native apps get every handle they leak (files, images, players, connections, fonts, crypto contexts, menu items, zip handles) freed on exit. Lua file handles are real objects closed by __gc or <close>, and files an app leaves open are closed after it, so FatFS's 16-file limit no longer wears down until a reboot.
Stack and memory hardening. MSPLIM/PSPLIM stack guards on the RP2350; Lua and native apps share one guarded app stack; dev commands run on their own. The Lua heap packs small objects into slab pools instead of spending a whole 200-byte block on each, so apps with many small tables no longer run the heap out of memory. A dirty-exit marker, heap fragmentation figures in /system/error.log, min_psram_kb in app.json, and boot-loop protection (after three failed boots the device halts instead of looping).
New APIs (native API v5 → v8)
- Fonts (v6): 4 built-in and 8 loadable proportional
.pfnfonts (setFont,loadFont,textWidth, transparent text…);tools/mkfont.pybuilds them from BDF/TTF/PNG. - Video (v7): time-based seek and position, a progress OSD,
hasEnded. - ZIP read-in-place archive handles (v5), plus on-device
unzip/rmdev commands. - TLS (v8):
http->setInsecure,tcp->connectEx. - Display: clip rect and mode-7
drawPlane(v4), LCD hardware scrolling,fillHLine,fillTriangle, transparent-background text. - Input:
pollEvent()ordered key events,isKeyDown(k), button auto-repeat. picocalc.json, withgame.saverebuilt on it.- Store: catalog from picos.jeffory.dev, streaming downloads with mandatory SHA-256 (and signature, for firmware).
Fixes and performance
- Unified 44.1 kHz audio mixer: no more PWM whistle, and sound effects no longer cut the music.
- HTTP/TCP: a cross-core close protocol with one lock per pool, correct chunked/close-delimited bodies, timeouts that run with the link down, ARP requests retried.
- Configs are saved atomically; display primitives clip once (huge coordinates cost only the visible pixels); an adaptive Lua service hook.
Changes that can affect apps
- HTTPS to an untrusted or self-signed server now fails with
TLS: certificate not trusted …; a request made before the clock is set fails withclock not set(retry after a few seconds). picocalc.sysconfigisnilunless the app declares"sysconfig", andget("wifi_pass")returnsnil.- App ids must be 1-79 characters of
[A-Za-z0-9._-]and are folded to lower case (data lives in/data/<lower-case id>); an invalid id is refused at launch. game.saveslots moved to/data/<app_id>/saves/; a legacy/saves/<name>.jsonis copied into the slot on first read.- Lua
loadaccepts text only (bytecode is rejected);dofile/loadfileare gone. - Numeric arguments to display, audio, video, input and
sys.sleepround to nearest instead of truncating; volumes and colour components clamp. sys.qmiPsramAllocreturns a bounds-checked buffer handle, and Lua PIO PSRAM access starts at0x48000.- A held key repeats; it is never a new press edge.
Firmware
picocalc_os.uf2: drag onto the Pico (Pimoroni Pico Plus 2 W, RP2350) in BOOTSEL mode.picocalc_os.bin+.sha256+.sig: OTA through the updater or store.
This is the first release built with the release signing key. Devices on v0.1.0 can update through the updater or store as usual. A device running a local development build (which embeds the repository's test key) will refuse this image over OTA: flash the .uf2 once.
PicOS v0.1.0
Highlights
Text editor word wrap — wrap is now computed by the editor itself (word-boundary wrapping, visual-row scrolling with mid-line anchoring, correct gutter numbering with blank continuation rows). Fixes wrapped text scrolling off the top of the screen. The review also fixed chunked file loading corrupting files over 512 bytes, the cursor vanishing in scrolled files, line numbers ignoring scroll, trailing-newline preservation, and physical Ctrl+letter shortcuts.
Reliable key injection + chords — injected keys are now published for a full poll cycle so apps always observe them (previously the app's own input.update usually wiped them first on hardware). Modifier chords work end-to-end: keydown/keyup dev commands, hold/release RPC actions in the simulator, and ctrl+s-style combos in the MCP keypress tool.
Idle screen dimming — the backlight dims after 60 seconds without input to protect the IPS panel from burn-in; any key wakes it (the waking press is swallowed). Configurable via dim_timeout_s ("0" disables). Video playback keeps the screen awake, and Lua apps get picocalc.sys.resetIdleTimer().
Modal dialogs honor dev exit — confirm/text-input/file-browser/timezone/system-menu dialogs now unwind on a pending dev exit instead of deferring it until closed by hand.
Firmware
picocalc_os.uf2 attached — drag onto the Pico in BOOTSEL mode (Pimoroni Pico Plus 2 W, RP2350). Existing devices on a recent build can flash over the serial OTA path instead.
v0.0.9
Firmware - QMI PSRAM quad-mode (QPI) driver — 4-6x bandwidth over the previous 1-bit serial access; CS1 timing retimed on clock change, which was the root cause of native-app crashes at 300MHz - OTA flash write fixed and verified on hardware across versions - Native app loader: PSP stack trampoline, XIP cache-clean handling, split SRAM/PSRAM loading - MP3 decode-burst staging fix (mid-burst refill, 8KB staging, pre-roll) - HardFault handler: SFSR/IPSR capture, per-core stack bounds Apps - GBC: ROM picker, save states, direct ROM/cart-RAM fast paths - Doom: audio enabled (FEATURE_SOUND) - C-Dogs: boot-to-menu fixes, graphics accounting Tooling / CI - Simulator: headless software-renderer fallback, per-process crash log, stdout/stderr drained by the test harness - CI restored end to end: submodule gitlinks made reachable, dependency drift fixed, E2E suite green for the first time
v0.0.8
What's Changed
- Expands platform with simulator and enhanced APIs
Known issues
- OTA updater is broken
- 3D Fill SDK renders incorrectly
- Wifi connection isn't robust
- USB Disk Mode can cause keyboard I2C to lock up when copying large files
- 44100Hz MP3s sound incorrect and corrupt
Full Changelog: v0.0.7...v0.0.8
v0.0.7
v0.0.6
Merge branch 'feature/example-applications-performance-fixes'
v0.0.5
Introduces row-based display flushing Enables partial screen updates by flushing specific rows from the back buffer to the LCD. This improves efficiency for use cases like status bars or emulator viewports, avoiding a full screen refresh. The operation does not swap display buffers.