Search › A-Z › R › Raspberry Pi Pico
Raspberry Pi Pico
The real Raspberry Pi Pico, in the browser. The RP2040's two ARM Cortex-M0+ cores run on the vendored rp2040js SoC — a complete Raspberry Pi Pico with XIP flash, 264 KB SRAM, GPIO, timers, UART and a USB device controller. It boots the official MicroPython firmware to an interactive REPL over USB-CDC, and also runs bare-metal firmware, with the on-board LED (GPIO25) and serial console on screen and the whole ARM Cortex-M0+ wired into this site's shared Thumb debugger. Being a bare Pico, it is naturally silent — there is no on-board audio.
Visit the official Raspberry Pi Pico page ↗
Runs on: Web browser
Raspberry Pi Pico Online Emulator
Play Raspberry Pi Pico using JavaScript directly in your browser.
Controls
Configurations
| Configuration | Emulator | Machine | OS | Legal | |
|---|---|---|---|---|---|
| MicroPython REPL | Raspberry Pi Pico | Raspberry Pi Pico | open | Open ⛶ | |
| Blink (on-board LED) | Raspberry Pi Pico | Raspberry Pi Pico | open | Open ⛶ | |
| Hello UART (serial) | Raspberry Pi Pico | Raspberry Pi Pico | open | Open ⛶ |
Chips
Notes
Embedding
The RP2040 SoC is Uri Shaked's rp2040js (MIT), a complete in-browser Raspberry Pi Pico: two Cortex-M0+ cores, XIP flash, 264 KB SRAM, the SIO/GPIO block, timers, UART, a USB device controller with a USB-CDC class, and the PIO. It is a TypeScript/ESM project; it was bundled once to a single file-safe browser global (window.rp2040js) with esbuild and is used unmodified — no core was reinvented.
// esbuild entry.ts --bundle --format=iife --platform=browser -> window.rp2040js
var R = window.rp2040js;
var sim = new R.Simulator(); // owns the RP2040 + a simulation clock
var mcu = sim.rp2040;
mcu.loadBootrom(R.bootromB1); // the real RP2040 B1 boot ROM
loadUF2(firmwareBytes, mcu); // decode UF2 blocks straight into mcu.flash
mcu.core.PC = 0x10000000; // enter the flashed image (XIP)
var cdc = new R.USBCDC(mcu.usbCtrl); // USB-CDC = the MicroPython REPL serial port
cdc.onSerialData = function(bytes){ /* -> console */ };
mcu.uart[0].onByte = function(b){ /* UART0 TX -> console */ };
We own the run loop so the debugger can drive it. Each frame runs a time-boxed budget of mcu.core.executeInstruction() (ticking the simulation clock by the returned cycle count); when the core is waiting (WFI/WFE) the clock is advanced to the next alarm, exactly like rp2040js's own Simulator. Everything is plain JavaScript state:
| Member | Kind | What it does |
|---|---|---|
mcu.core.executeInstruction() | method | Execute one Thumb instruction on core 0, advancing core.PC; returns a cycle estimate. The single-step primitive. |
mcu.core.registers (Uint32Array 16) | field | r0-r12, SP(13), LR(14), PC(15). core.xPSR, core.N/Z/C/V, core.PM round out the programmer's model — all readable and writable. |
mcu.flash / mcu.sram / mcu.bootrom | field | The real memories as typed arrays — read side-effect-free for the hex + disassembly views. |
mcu.writeUint8/16/32(addr,v) | method | The bus write path used by every store — wrapped to implement write-watchpoints. |
mcu.gpio[25].value | getter | The on-board LED pin (High/Low) — polled each frame to draw the LED. |
cdc.sendSerialByte(b) / mcu.uart[0].feedByte(b) | method | Serial RX in — the keyboard drives the MicroPython REPL through the USB-CDC port. |
Debugger integration
The plug-in (pico-debug.js) describes the RP2040 to the shared debugger and nothing more — rp2040js is not patched:
- Reused decoder. The Cortex-M0+ runs ARMv6-M Thumb, a subset of the existing
/debugger/src/cpus/cortex-m7.jsdisassembler (written for the Playdate-ARM Cortex-M7). It is reused as-is under the display name "ARM Cortex-M0+"; no new decoder was needed. - Registers are read live each refresh: r0-r12, SP, LR, PC, xPSR, the APSR flags N Z C V, and PRIMASK / nPRIV. Each has a
set()that writes straight back intocore.registers/ the core's flag fields. - Memory is exposed as three chips, all read side-effect-free directly off the typed arrays, each with its real base address so the disassembly lines up with PC: the XIP flash at
0x10000000(disassembled), the SRAM at0x20000000, and the 16 KB boot ROM at0x00000000(also disassembled — you can read the real Raspberry Pi bootrom). - Single step is one
executeInstruction(). Breakpoints are host-side checks ofcore.PCagainst a set of addresses, run instruction-by-instruction when any are set. Write-watchpoints wrapmcu.writeUint8/16/32and pause the loop the moment a watched bus address is written.
Architecture
The Raspberry Pi Pico (2021) is Raspberry Pi's first in-house microcontroller board, built around their RP2040:
- Dual ARM Cortex-M0+ — two 32-bit ARMv6-M cores at up to 133 MHz (modelled here at 125 MHz), Thumb instruction set, sharing the bus through the SIO single-cycle I/O block.
- Memory — 264 KB on-chip SRAM at
0x20000000; code executes in place (XIP) from external QSPI flash mapped at0x10000000; a 16 KB mask boot ROM at0x00000000. - On-board LED — a single user LED on GPIO25, driven through the SIO GPIO block. This is the Pico's only on-board indicator; there are no other lights, displays, buttons or audio on the bare board.
- Serial — the RP2040 exposes two PL011 UARTs plus a full-speed USB 1.1 device controller. Firmware talks to a host either over UART0 or, as MicroPython and CircuitPython do, over a USB-CDC virtual serial port — which is where the MicroPython REPL appears.
The default firmware is the official MicroPython for RP2040 (.uf2) — real, unmodified firmware. It boots through the same path as on hardware and drops you at an interactive >>> prompt on the USB-CDC console. Because it is real ARM machine code on a real SoC model, every instruction single-steps in the debugger, and Python that touches machine.Pin(25) visibly toggles the on-board LED.
Honest limits. rp2040js is cycle-approximate, not cycle-exact; the second core, PIO, DMA and the fuller USB/peripheral surface are present but not all exercised by these firmwares; and the bare Pico has no on-board sound hardware (PWM audio needs an external speaker), so this emulator is naturally, deliberately silent.