picocalc-ops
This skill should be used when the user reports PicoCalc issues (boot failures, SD card not mounting, display blank, keyboard not responding), wants to flash firmware, deploy scripts to SD card, debug errors, or manage the PicoCalc device. Activate when the user mentions "PicoCalc not booting", "boot loop", "SD card error", "display not working", "flash firmware", "UF2", "BOOTSEL", "deploy to PicoCalc", "copy to SD card", "PicoCalc error", or "debug PicoCalc".
PicoCalc Operations: Debug and Deploy
Diagnose and fix PicoCalc issues. Deploy firmware and scripts to the device.
Boot Sequence
- Power on -> Pico 2W runs boot.py from internal flash
- Initialize PicoDisplay(320, 320) and PicoKeyboard()
- Setup USB debug output (sys.stdout)
- gc.collect() + 500ms delay for stability
- SD card init: SPI0 at 1MHz, mount to /sd
- VT100 terminal:
vt.vt(display, keyboard, sd=sd) - Register on picocalc module: display, keyboard, terminal, sd
os.dupterm(terminal)redirects REPL to LCD+keyboard- boot.py finishes -> REPL becomes available over USB
- main.py runs ->
py_run.main_menu()launches script selection
IMPORTANT: main_menu() MUST be in main.py, NOT boot.py. If it's in boot.py, the REPL never starts and USB tools (Thonny, mpremote) cannot connect. Ctrl+C can interrupt main.py but not boot.py.
Common Failures
Boot Loop on Battery
Symptom: Device power-cycles repeatedly on battery power.
Cause: Keyboard MCU at 0x1F doubles as power management controller. Sending reset register (0x08) during cold boot cuts power.
Fix: Never call keyboard.reset() during initialization. Current firmware guards against this.
SD Card Mount Failure
Symptom: OSError during SD init, missing /sd directory, apps not found.
Cause: SPI0 timing sensitive at cold start.
Fix: Ensure 900ms delay before initsd(), use baudrate=1000000 (not higher), verify GPIO 16/17/18/19.
Diagnose: os.listdir('/sd') from USB REPL.
Display Blank
Symptom: Screen dark, but USB REPL responds.
Cause: Display refresh runs on Core 1. Flash write without lockout crashes Core 1.
Fix: Ensure firmware has multicore_lockout_victim_init() in core1_main().
Keyboard I2C Errors
Symptom: Intermittent OSError during keyboard reads.
Cause: I2C bus at 10kHz produces transient errors.
Fix: Wrap reads in try/except. Run i2c.scan() to verify 0x1F responds.
Flash Write Crash
Symptom: Hard fault when saving files to internal flash while display running.
Cause: Core 1 runs XIP code; flash write locks XIP.
Fix: multicore_lockout_victim_init() on Core 1 (included in current firmware).
USB REPL Not Connecting
Symptom: Device works (display, keyboard, menu) but no USB serial port appears on Mac, or Thonny/mpremote can't connect. Causes & Fixes:
- No USB serial device at all — Firmware build issue. Check with
ls /dev/tty.usbmodem*. If nothing appears:- Verify firmware was built with
BOARD=RPI_PICO2_W(notRPI_PICOorRPI_PICO2) - Verify MicroPython version is pinned to known-good (v1.25.0-preview). MicroPython master can have USB regressions on RP2350.
- Verify Dockerfile includes
lib/cyw43-driverin pico-sdk submodule init - Compare .uf2 file sizes: known-good is ~2.1MB. If new build is ~1.7MB, CYW43/WiFi stack is missing
- Use
strings firmware.uf2 | grep BUILTIN_CDCto verify USB CDC is compiled in
- Verify firmware was built with
- Port appears but can't get REPL prompt —
main_menu()is blocking. Usempremote resumeto connect without soft-reset, or press ESC on PicoCalc keyboard first. - Thonny "raw paste" error —
os.dupterm(pc_terminal)keyboard data interferes with Thonny's raw REPL protocol. Ensuremain_menu()is inmain.pynotboot.py. Diagnose:ioreg -p IOUSB | grep -i picoto check if device enumerates at all.
UF2 Comparison
Compare a known-good .uf2 with a new build:
# Size comparison (known-good ~2.1MB for Pico 2W with WiFi)
wc -c firmware.uf2
# Feature check
strings firmware.uf2 | grep -E "BUILTIN_CDC|CYW43|RPI_PICO|MicroPython-|beginDraw"
# Family ID (e48bff57 = RP2350)
xxd -s 28 -l 4 firmware.uf2
Deployment
Dashboard (Recommended)
python3 MicroPython/tools/dashboard.py
Opens a web UI at http://localhost:8265 for visual file management, deploy, diff, edit, and REPL. Auto-installs mpremote if needed.
Connecting to Device via Dashboard API
When the dashboard is running and the PicoCalc is connected via USB, you can execute code on the device directly using the dashboard's REST API. The dashboard port may vary -- check the terminal output when it starts (default 8265).
Execute code on device:
curl -s -X POST http://localhost:8265/api/exec \
-H 'Content-Type: application/json' \
-d '{"code": "print(\"hello from PicoCalc\")"}'
Returns JSON: {"rc": 0, "out": "hello from PicoCalc\n", "err": ""}
Check device status:
curl -s http://localhost:8265/api/device
Push a file to device:
curl -s -X POST http://localhost:8265/api/push \
-H 'Content-Type: application/json' \
-d '{"file": "sd/py_scripts/PicoKeyboard.py"}'
Key notes:
- The exec endpoint has a 30-second timeout -- don't use it to launch apps with main loops
- Large scripts (>1000 lines) may fail via exec due to RAM -- launch from device menu instead
- The dashboard uses mpremote internally, so only one operation can run at a time
- Check the dashboard port in the terminal output -- user may run on a non-default port
Flash UF2 Firmware
- Power off PicoCalc completely
- Hold BOOTSEL on Pico 2W, connect USB cable
- Device appears as RPI-RP2 or RP2350 removable drive
- Copy
MicroPython/firmware/picocalc_micropython_pico2w.uf2to drive - Device reboots automatically
File Placement
Device / <- boot.py, main.py
Device /modules/ <- all MicroPython/modules/*.py
Device /sd/py_scripts/ <- all MicroPython/sd/py_scripts/*.py (on SD card)
Deploy via mpremote
# Use 'resume' to avoid re-triggering main_menu on soft reset
mpremote resume cp MicroPython/boot.py :boot.py + cp MicroPython/main.py :main.py
mpremote resume cp MicroPython/modules/picocalc.py :/modules/picocalc.py
SD Card Layout
/sd/
├── py_scripts/ # App scripts (auto-discovered by menu)
├── logs/ # Log files (created by apps)
└── wifi.json # WiFi credentials (created by brad.py)
USB Serial Debug
Connect USB, open serial at 115200 baud. usb_debug() writes to sys.stdout. Ctrl+C interrupts main.py and drops to REPL.
Additional Resources
- references/boot-sequence.md - Detailed boot.py flow
- references/common-failures.md - Extended failure catalog
- references/deployment-checklist.md - Step-by-step deployment
- references/usb-serial-debug.md - Debug techniques