Bridges Claude to PicoCalc hardware running MicroPython over USB serial. Exposes file operations (push, pull, diff), REPL execution with 30 second timeouts, and safe eject sequences. Same tooling that powers the web dashboard, now accessible directly from AI conversations. Useful when you're iterating on MicroPython scripts for the device and want Claude to write code, deploy it to flash or SD card, run test commands, and check diffs without switching windows. Works alongside the code-skills repo which teaches Claude the hardware APIs and app patterns. Requires mpremote and a connected Pico 2W running the custom firmware.
A MicroPython firmware and script collection for the Clockwork Pi PicoCalc handheld device, powered by the Raspberry Pi Pico 2W. Features:
MicroPython/firmware/picocalc_micropython_pico2w.uf2 to that drive.The easiest way -- a local web UI that handles everything:
python3 MicroPython/tools/dashboard.py
This opens a browser dashboard where you can:
.py files to uploadFirst run will prompt to install
mpremoteif needed. Requires Python 3.7+.Adding new files: Scripts in
modules/andsd/py_scripts/are auto-discovered by the dashboard. Root-level files (likeboot.py) must be added toFILE_MAPindashboard.pyto appear.
Let your AI coding assistant talk directly to the PicoCalc -- write code, push it, test on device, iterate, all from the AI conversation.
MCP Server (Recommended)
MCP gives AI tools native access to the device with no dashboard running. Install from PyPI:
pip install picocalc-mcp
Then add to your .mcp.json or Claude Desktop config:
{
"mcpServers": {
"picocalc": {
"command": "picocalc-mcp"
}
}
}
Or run directly from the repo: "args": ["/path/to/PicoCalc/mcp/mcp_server.py"]
Works with Claude Code, Claude Desktop, Cursor, and any MCP-compatible tool. Full setup guide: MCP_README.md
See it in action: MCP & Dashboard Demo
Claude Code Skills (Optional)
For Claude Code users, the code-skills repo provides PicoCalc-specific skills that teach the AI how to write correct apps, use the hardware APIs, review code, and handle device operations. The MCP server gives the AI hands to interact with the device; the skills give it the knowledge to build for it.
# With skill-deployer installed, just paste a URL:
"install this skill https://github.com/LofiFren/code-skills/tree/main/skills/picocalc-app"
Dashboard REST API (Legacy)
If you already have the dashboard running, AI tools can also use its HTTP endpoints:
curl -s http://localhost:8265/api/device # Device status
curl -s -X POST http://localhost:8265/api/exec -H 'Content-Type: application/json' \
-d '{"code": "print(1+1)"}' # Run Python on device
curl -s -X POST http://localhost:8265/api/push -H 'Content-Type: application/json' \
-d '{"file": "sd/py_scripts/my_app.py"}' # Push file to device
curl -s "http://localhost:8265/api/diff?file=sd/py_scripts/synth.py" # Diff local vs device
curl -s -X POST http://localhost:8265/api/eject # Safe disconnect
All endpoints: /api/device, /api/exec, /api/push, /api/pull, /api/files, /api/diff, /api/local/tree, /api/eject, /api/reset, /api/cleanup
Use Thonny, mpremote, or rshell to copy files to the Pico's internal flash.
Copy these to the Pico root (/):
MicroPython/boot.py --> /boot.py
MicroPython/main.py --> /main.py
MicroPython/modules/ --> /modules/ (entire directory)
Copy SD card scripts:
MicroPython/sd/py_scripts/*.py --> SD card /py_scripts/
Do NOT copy these to the device (they run on your computer, not the PicoCalc):
tools/ <-- Dashboard web UI (runs on your computer)
picocalcdisplay/ <-- C source, compiled into firmware
vtterminal/ <-- C source, compiled into firmware
firmware/ <-- UF2 images and Dockerfile
py_scripts folder on the SD card..py files from MicroPython/sd/py_scripts/ into that folder.Or use the Dashboard -- click Deploy Scripts to push all scripts to the SD card over USB.
Power cycle the PicoCalc. The boot splash shows initialization progress, then the main menu appears with arrow-key navigation.
MicroPython/
|-- boot.py --> Copy to device /
|-- main.py --> Copy to device / (launches menu)
|-- boot_thonny.py --> Deploy as /boot.py for Thonny users
|-- boot_dev.py --> Deploy as /boot.py for dev mode (REPL only)
|-- cleanup.py --> (Optional) one-time cleanup, or use dashboard Cleanup button
|-- modules/ --> Copy to device /modules/
| |-- picocalc.py Hardware abstraction (display + keyboard)
| |-- vt.py VT100 terminal emulator
| |-- py_run.py Categorized app menu (auto-hides libraries)
| |-- enhanced_sd.py SD card initialization
| |-- picocalc_system.py System utilities
| |-- sdcard.py SD card driver
| |-- checksd.py SD card verification
| |-- pye.py Built-in text editor
| |-- colorer.py Terminal color support
| |-- default_style.py Syntax highlighting styles
| |-- highlighter.py Code syntax highlighting
| |-- flush.py Module cache flushing
| \-- mkdir.py Directory creation utility
|-- sd/py_scripts/ --> Copy contents to SD card /py_scripts/
| |-- tetris.py Tetris with sound effects
| |-- snake.py Snake with high scores
| |-- synth.py 4-instrument synthesizer with piano keyboard
| |-- strudel.py Strudel mini-notation parser + sequencer (library)
| |-- strudel_live.py Live-coding music UI (color themes, playhead)
| |-- strudel_demo.py Interactive tutorial: learn the mini-notation
| |-- ProxiScan.py BLE proximity scanner & fox hunt tool
| |-- WiFiManager.py WiFi scanning & connection manager
| |-- picocalc_ollama.py Local LLM client (Ollama)
| |-- brad.py WiFi utility library
| |-- demo.py Visual display showcase (grayscale, animation)
| \-- editor.py On-device file browser + code editor
|-- sd/samples/ --> Copy to SD card /samples/ (Strudel drum kit)
| \-- bd/sd/hh/cp.raw 8-bit PCM one-shots for the picosampler engine
|-- firmware/ Prebuilt UF2 firmware images
| |-- picocalc_micropython_pico2w.uf2 (v1.25.0, stable)
| |-- picocalc_v127_pico2w.uf2 (v1.27.0, patched)
| |-- picocalc_v128_pico2w.uf2 (v1.28.0, native USB + audio engine)
| |-- Dockerfile Build for v1.25.0
| |-- Dockerfile.v127 Build for v1.27.0 + USB fix
| |-- Dockerfile.v128 Build for v1.28.0 (no USB patch)
| \-- USB_REGRESSION_FIX.md RP2350 USB bug report
|-- micropython.cmake Top-level build config for C modules
|-- picocalcdisplay/ C display driver (compiled into firmware)
| |-- picocalcdisplay.c
| |-- picocalcdisplay.h
| |-- font6x8e500.h
| \-- micropython.cmake
|-- vtterminal/ C terminal emulator (compiled into firmware)
| |-- vtterminal.c
| |-- vtterminal.h
| |-- font6x8.h
| \-- micropython.cmake
|-- picosampler/ C audio engine (DMA-PWM sampler, compiled in)
| |-- picosampler.c 8-voice 8-bit PCM mixer, stereo PWM out
| |-- micropython.cmake
| |-- make_samples.sh wav -> 8-bit PCM converter (ffmpeg)
| \-- generate_drums.py synthesize a starter drum kit (no deps)
|-- tools/ Development tools
| |-- dashboard.py Web UI server (run this!)
| |-- bottle.py Vendored web framework (zero install)
| \-- static/
| |-- index.html Dashboard frontend
| \-- vendor/ CodeMirror editor (vendored, offline)
mcp/ MCP server for AI assistants
|-- mcp_server.py MCP server (pip install picocalc-mcp or run directly)
\-- MCP_README.md Full setup guide
| App | Description |
|---|---|
| Tetris | Classic Tetris with 7 pieces, ghost piece, sound effects, level progression |
| Snake | Snake with high score tracking, speed levels, sound |
| Synth | 4-instrument synthesizer (Piano, Organ, Strings, Synth) with QWERTY piano keyboard, ADSR envelope, arpeggiator, 16-step sequencer, LFO effects, presets |
| Strudel Live | On-device Strudel live-coding: type mini-notation patterns (bd*4 , ~ sd ~ sd , hh*8) with per-layer effects (| lpf 600 r 40), hear them update live through the native picosampler audio engine (resonant lpf/hpf + ADSR), sweeping playhead, 5 color themes (SonicPink, Dracula, Monokai, Nord, Terminal) |
| Strudel Basics | Interactive color tutorial -- 11 lessons that teach the mini-notation (sequences, *, [], <>, euclid, layering) and effects (filters, envelope) with a syntax-colored pattern, a live rhythm timeline, and audio. A fun way to learn before jumping into Strudel Live |
| ProxiScan | BLE proximity scanner, fox hunt tool with compass, signal tracking, competition timer, waypoints, antenna calibration |
| WiFiManager | WiFi scanner with VT100 UI, signal bars, channel analysis, signal monitor |
| SSH Client | Secure shell client -- ECDH-SHA2-NISTP256 key exchange, AES-128-CTR + HMAC-SHA2-256 encryption, RSA host key verification (TOFU), saved connection profiles with PIN-encrypted passwords, interactive VT100 terminal (53x40) |
| SSH Server | SSH into the PicoCalc for a MicroPython REPL -- ECDH-SHA2-NISTP256 key exchange, ECDSA-nistp256 host key, AES-128-CTR + HMAC-SHA2-256, password (salted-hash) and public-key (authorized_keys) auth |
| Ollama Client | Chat with local LLMs over WiFi via Ollama |
| Demo | Visual display showcase: grayscale palette, bouncing boxes, scrolling gradient, device info |
| Editor | On-device file browser and code editor -- browse, create, edit, delete scripts without a computer |
This is a small reimplementation of Strudel's pattern idea that runs natively
in MicroPython + C -- not a full port. There is no JavaScript engine and not
the full Strudel/TidalCycles function library; it's a focused subset. Sounds are
samples only (the bundled bd/sd/hh/cp kit, plus any 8-bit pcm_u8
.raw you drop in /sd/samples/ -- the sample name is the file name).
Mini-notation supported
| Syntax | Meaning |
|---|---|
bd sd hh | sequence -- splits the cycle evenly |
~ | rest (silence) |
[bd sd] | sub-group -- packs sounds into one step |
bd*N | repeat N times (faster) |
bd/N | play once every N cycles |
<bd sd> | alternate -- one per cycle |
a , b (or one layer per line) | stack / layer together |
bd(k,n) / bd(k,n,rot) | euclidean rhythm (+ rotation) |
// ... or # ... | comment (inline or whole line) |
Effects supported (per layer, after \|): lpf hpf res gain a d
s r dur -- see Strudel Effects below.
Not supported (by design, for now): the JavaScript API and method chains;
synth/oscillator voices (note/n); pitch / speed / accelerate; pan;
time/character effects (delay, room/reverb, crush, coarse, shape);
transform functions (rev, jux, every, sometimes, off, degrade, ...);
per-event (rather than per-layer) effects; and sample banks beyond what you place
in /sd/samples/.
The picosampler audio engine does real per-voice DSP in C: a resonant 2-stage
state-variable filter (highpass -> lowpass) and an ADSR amplitude envelope, all
mixed in float for full output resolution. Add effects to a pattern line after a
| bar (they apply to every sound on that line):
bd*4 , hh*8 | lpf 600 res 150 # dark, squelchy lowpass
~ sd ~ sd | hpf 1200 # thin, highpassed snare
bd ~ | a 3 r 180 dur 90 # shaped attack/release
| Effect | Meaning |
|---|---|
lpf <hz> / hpf <hz> | lowpass / highpass cutoff (0 = off) |
res <n> | filter resonance / Q*100 (70 ~= 0.7, higher = more peak) |
a d r | envelope attack / decay / release, milliseconds |
s <0-255> | envelope sustain level |
dur <ms> | gate length before release (0 = play the whole sample) |
gain <0-256+> | per-layer level (256 = unity) |
The SSH client supports modern OpenSSH 10.x servers (which dropped legacy DH key exchange) via ECDH-SHA2-NISTP256, with automatic fallback to diffie-hellman-group14 for older servers. Key exchange completes in ~1.3 seconds on the RP2350.
Required files:
sd/py_scripts/ssh_client.py -- the SSH client applicationsd/py_scripts/secure_creds.py -- PIN-based credential encryption librarysd/wifi.json -- WiFi credentials (created by WiFiManager)Auto-created on first use:
/sd/ssh_profiles.json -- saved connection profiles (passwords encrypted)/sd/ssh_known_hosts.json -- trusted host key fingerprintsUsage:
ssh -p 2222 user@<ip> command for SSHing back intop, you may need to press Ctrl+C rapidly a few times since the keyboard is polled between SSH packetsRun SSH Server to log in to the PicoCalc from another machine and get a live MicroPython REPL over the network. Reuses the SSH client's transport/crypto and adds the server side: an ECDSA-nistp256 host key, key-exchange signing, and password + publickey authentication.
Connect from your computer:
ssh -p 2222 <user>@<pico-ip> # password auth
ssh -i ~/.ssh/id_ecdsa -p 2222 <user>@<pico-ip> # public-key auth
The app's status screen shows the exact command (with the device IP) and the host-key fingerprint. Default port is 2222.
Files (on the SD card):
sd/py_scripts/ssh_server.py -- the SSH server application/sd/ssh_host_ecdsa.json -- ECDSA host key (auto-generated on first run)/sd/ssh_server.json -- username + salted password hash (set on first run)/sd/authorized_keys -- optional OpenSSH-format public keys (one per line) for key authNotes:
ecdsa-sha2-nistp256 and ssh-rsa/rsa-sha2-256. Ed25519 client keys are not yet supported -- use an ECDSA/RSA key or password auth.eval/exec with output streamed to your terminal); exit() or Ctrl-D disconnects.The main menu groups apps by category (Music, Games, Network, Tools...) with one-line descriptions, and auto-hides library/helper files. Apps opt in to a category with a header comment: # picocalc-app: Name | Category | description.
| Key | Action |
|---|---|
| Up/Down | Navigate apps |
| Enter | Run selected script |
| ESC | Exit to REPL |
| R | Reload script list |
| F | Flush module cache |
| M | Show memory/storage info |
| T | File management tools |
The prebuilt UF2 files in firmware/ are ready to flash -- building from source is not required. Only do this if you want to modify the C display/terminal drivers or experiment with different MicroPython versions.
Requires: Docker Desktop.
Stable, known-good USB. Used by the prebuilt picocalc_micropython_pico2w.uf2.
# 1. Build the Docker image (one-time, ~5 min)
docker build -t picocalc-build MicroPython/firmware/
# 2. Compile firmware with PicoCalc C modules (~3 min)
docker run --rm \
-v $(pwd)/MicroPython:/picocalc \
-v $(pwd)/MicroPython/firmware:/out \
picocalc-build \
bash -c "make BOARD=RPI_PICO2_W USER_C_MODULES=/picocalc/micropython.cmake -j\$(nproc) && \
cp build-RPI_PICO2_W/firmware.uf2 /out/picocalc_micropython_pico2w.uf2"
Newer MicroPython with BLE pairing APIs enabled. Includes a two-line patch for the RP2350 USB regression (see firmware/USB_REGRESSION_FIX.md).
# 1. Build the Docker image (one-time, ~5 min)
docker build -t picocalc-v127 -f MicroPython/firmware/Dockerfile.v127 MicroPython/firmware/
# 2. Compile firmware (~3 min)
docker run --rm \
-v $(pwd)/MicroPython:/picocalc \
-v $(pwd)/MicroPython/firmware:/out \
picocalc-v127 \
bash -c "make BOARD=RPI_PICO2_W USER_C_MODULES=/picocalc/micropython.cmake -j\$(nproc) && \
cp build-RPI_PICO2_W/firmware.uf2 /out/picocalc_v127_pico2w.uf2"
MicroPython v1.28.0 fixes the RP2350 USB-CDC regression upstream, so no USB patch is needed -- USB enumerates natively. This build keeps the BLE pairing APIs. Recommended if the patched v1.27 build fails to enumerate on your USB host (see issue #11).
# 1. Build the Docker image (one-time, ~5 min)
docker build -t picocalc-v128 -f MicroPython/firmware/Dockerfile.v128 MicroPython/firmware/
# 2. Compile firmware (~3 min)
docker run --rm \
-v $(pwd)/MicroPython:/picocalc \
-v $(pwd)/MicroPython/firmware:/out \
picocalc-v128 \
bash -c "make BOARD=RPI_PICO2_W USER_C_MODULES=/picocalc/micropython.cmake -j\$(nproc) && \
cp build-RPI_PICO2_W/firmware.uf2 /out/picocalc_v128_pico2w.uf2"
| File | MicroPython | Notes |
|---|---|---|
picocalc_micropython_pico2w.uf2 | v1.25.0-preview | Stable default, all apps work |
picocalc_v127_pico2w.uf2 | v1.27.0 (patched) | BLE pairing APIs, manual USB wrap (marginal on some hosts) |
picocalc_v128_pico2w.uf2 | v1.28.0 | Native USB (no patch), BLE pairing APIs -- recommended |
Dockerfile | v1.25.0-preview | Standard build |
Dockerfile.v127 | v1.27.0 + patches | USB fix + BLE security enabled |
Dockerfile.v128 | v1.28.0 + BLE patch | Native USB, BLE security enabled |
USB_REGRESSION_FIX.md | -- | Bug analysis and patch details |
| Component | Details |
|---|---|
| Display | 320x320 ILI9488 LCD, SPI1, 4-bit grayscale |
| Keyboard | Membrane keypad, I2C MCU at 0x1F |
| SD Card | SPI0, FAT32, mounted at /sd |
| Audio | PWM stereo, GPIO 27 (right) + GPIO 28 (left) |
| WiFi/BLE | CYW43 via Pico 2W |
| MCU | RP2350 (Raspberry Pi Pico 2W) |
"no module named 'picocalc'" after flashing new firmware:
picocalcdisplay/, vtterminal/) may be on the device filesystem, shadowing the C modules compiled into firmware. Delete them from the device -- they should only exist in the repo, not on the Pico./modules/ directory with picocalc.py is on the device.Boot doesn't auto-run:
boot.py is at the root of the Pico filesystem (/boot.py)./modules/ contains all .py files.SD card not mounting:
Display blank but REPL responds:
USB REPL not connecting (no serial port appears):
BOARD=RPI_PICO2_W. Default RPI_PICO is the wrong chip.--wrap=dcd_event_handler patch, but that workaround is marginal on some USB host controllers (see issue #11). v1.28.0 fixes the regression natively -- if v1.27 fails to enumerate, use picocalc_v128_pico2w.uf2. The v1.25.0-preview firmware also has no regression.ls /dev/tty.usbmodem* (macOS) or ls /dev/ttyACM* (Linux).Thonny shows KeyboardInterrupt traceback on connect:
main.py's menu loop. You'll see a traceback followed by >>>. This means Thonny connected successfully.Thonny shows "Unexpected read during raw paste":
boot_thonny.py as /boot.py on the device (meaning rename boot_thonny.py as boot.py). This skips os.dupterm() which conflicts with Thonny's raw paste protocol. The PicoCalc screen and keyboard still work for apps and the menu -- only REPL output moves to Thonny's shell panel instead of the device screen.boot.py as /boot.py.boot.py and don't need the Thonny variant.picosampler native C audio engine -- a DMA-paced PWM sampler that mixes up to 8 voices of 8-bit PCM streamed from the SD card on both audio channels, so the device plays real sampled drums instead of pulse-wave beeps. Compiled into the v1.28 firmware as a third C module.bd*4 , hh*8 | lpf 600 res 150 r 40 (see Strudel Effects).strudel.py) -- parses a Strudel subset (bd sd hh*2, [bd sd], <bd cp>, euclid bd(3,8), comma-stacked layers, // and # comments) and schedules it in real time.strudel_live.py) -- edit patterns on the device and hear them update at the next cycle (like Strudel's Ctrl-Enter); sweeping playhead, event timeline, and 5 real color themes -- SonicPink, Dracula, Monokai, Nord, Terminal.strudel_demo.py) -- an interactive 11-lesson color tutorial that teaches the mini-notation and effects with live audio and a rhythm timeline.picocalcdisplay.setLUT -- no extra framebuffer RAM. Themes become instant palette swaps.py_run.py now groups apps into sections with one-line descriptions and auto-hides library/helper files (no more stray modules in the list). Apps opt in via a # picocalc-app: Name | Category | description header.picocalc_claude.py) has been retired.ssh -p 2222 user@pico-ip)claude-opus-4-8 default, and a choice of API-key or Max/Pro OAuth auth with the credential PIN-encrypted on the SD cardfirmware/Dockerfile.v128). The patched v1.27.0 build remains available as an alternative.enhanced_sd.initsd() retries the mount with backoff and validates capacity, so a slow card on first boot no longer comes up as internal flashpython3 MicroPython/tools/dashboard.py) with FTP-style dual-pane file manager, in-browser editor, REPL, drag-and-drop deploy, file diff, and macOS junk cleanupbeginDraw() blocks Core 1 during screen clear to prevent white flash artifactsboot_thonny.py variant for Thonny users (skips dupterm)Built on PicoCalc-micropython-driver by zenodante (LCD driver, keyboard logic, original 1.0 UF2 image).