18. [Differences vs Other Emulators](#18-differences-vs-other-emulators)
19. [Key Files](#19-key-files)
---
## 1. Overview
The **Raspberry Pi Pico** and **Pico W** boards use the **RP2040** microcontroller — a dual-core **ARM Cortex-M0+** chip designed by Raspberry Pi. Unlike the ESP32 (which requires QEMU running in the backend), RP2040 emulation runs **entirely in the browser** using the [rp2040js](https://github.com/wokwi/rp2040js) library (a local clone in `wokwi-libs/rp2040js/`).
### Emulation Engine Comparison
| Board | CPU | Engine |
| ----- | --- | ------ |
| Arduino Uno / Nano / Mega | AVR ATmega | avr8js (browser) |
| Raspberry Pi Pico / Pico W | RP2040 ARM Cortex-M0+ | **rp2040js (browser, no backend)** |
| Raspberry Pi Pico | `rp2040:rp2040:rpipico` | GPIO 25 | Standard Pico |
| Raspberry Pi Pico W | `rp2040:rp2040:rpipicow` | GPIO 25 (via CYW43) | WiFi chip not emulated |
> **Pico W note:** The wireless chip (Infineon CYW43439) is not emulated. GPIO 25 drives the on-board LED in the same way as the standard Pico for simulation purposes.
| Timers | Used internally for `delay()`, `millis()` |
| Watchdog | Present (reads return 0) |
| PLL / Clock | Simulated at fixed 125 MHz |
### Peripherals NOT Emulated
- **WiFi/BLE** (Pico W: CYW43439 wireless chip)
- **USB device stack** (USB device enumeration)
- **DMA transfers** (DMA control registers return 0)
- **PIO state machines** (PIO registers return 0)
> PIO in particular is a significant limitation — `PicoLED`, `WS2812` (NeoPixel), `DHT`, and other timing-critical libraries that rely on PIO will not function.
---
## 5. Full Flow: Compile and Run
### 5.1 Compile the Sketch
The backend compiles for the Pico using the earlephilhower arduino-pico core:
```bash
# First-time setup — install the RP2040 board manager:
The Velxio backend automatically finds `sketch.ino.bin` (or `sketch.ino.uf2` as fallback), encodes it as base64, and sends it to the frontend in `CompileResponse.binary_content`.
### 5.2 Serial Redirection (Important)
The backend **automatically prepends**`#define Serial Serial1` to `sketch.ino` for RP2040 boards:
```python
# backend/app/services/arduino_cli.py
if "rp2040" in board_fqbn and write_name == "sketch.ino":
content = "#define Serial Serial1\n" + content
```
**Why?** The RP2040 bootrom uses UART0 for its own communication. To avoid conflicts, the Arduino `Serial` object is remapped to UART1. This means `Serial.print()` in your sketch goes through UART1, which the emulator also captures and shows in the Serial Monitor.
### 5.3 Minimal Sketch for Raspberry Pi Pico
```cpp
// Blink the built-in LED (GPIO 25)
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
Serial.println("Pico started!");
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
Serial.println("LED ON");
delay(500);
digitalWrite(LED_BUILTIN, LOW);
Serial.println("LED OFF");
delay(500);
}
```
### 5.4 Analog Read Example
```cpp
// Read a potentiometer on GPIO 26 (ADC channel 0)
void setup() {
Serial.begin(115200);
}
void loop() {
int raw = analogRead(A0); // GPIO26 → ADC ch0, returns 0–4095
float voltage = raw * 3.3f / 4095.0f;
Serial.print("ADC: ");
Serial.print(raw);
Serial.print(" → ");
Serial.print(voltage);
Serial.println(" V");
delay(200);
}
```
---
## 6. Binary Format and Loading
The RP2040 uses a **raw binary format** (`.bin`), not the Intel HEX format used by AVR:
```typescript
// RP2040Simulator.loadBinary(base64: string)
loadBinary(base64: string): void {
const binary = Uint8Array.from(atob(base64), c => c.charCodeAt(0));
// Copy directly into flash at offset 0
this.rp2040.flash.set(binary, 0);
// Load bootrom and reset PC to 0x10000000
this.rp2040.loadBootrom(bootromB1);
}
```
**Flash memory layout** (as seen by the CPU):
```text
0x10000000 ← FLASH_START_ADDRESS — firmware entry point (PC reset here)
0x10000100 ... program .text section
0x10xxxxxx ... .rodata, .data, constants
0x20000000 ← SRAM — stack, heap, .bss
```
The bootrom B1 (`rp2040-bootrom.ts`) contains the RP2040's factory-programmed ROM code, required for correct flash XIP (eXecute In Place) initialization.
---
## 7. GPIO
All 30 GPIO pins (GPIO0–GPIO29) are emulated. Each pin has an event listener attached at startup:
PWM is available on any GPIO pin through the RP2040's hardware PWM slices. The rp2040js library emulates the PWM peripheral registers, so `analogWrite()` and `ledcWrite()` work at the firmware level.
Visual PWM feedback (LED dimming) in the simulator canvas uses the `onPwmChange` callback from `PinManager`, which receives the duty cycle as a 0.0–1.0 float and sets `el.style.opacity` on the LED element.
---
## 13. Simulation Execution Loop
The simulation runs at **60 FPS** using `requestAnimationFrame`. Each frame executes enough CPU cycles to match a 125 MHz clock:
When the ARM core executes a **WFI** (Wait For Interrupt) instruction — which Arduino uses during `delay()` and `sleep()` — the loop skips ahead to the next pending timer alarm instead of executing millions of NOP-equivalent cycles:
```typescript
while (cyclesDone <cyclesTarget){
if (core.waiting) {
// CPU is sleeping — jump to next alarm
const jump = clock.nanosToNextAlarm;
if (jump <= 0) break;
clock.tick(jump);
cyclesDone += Math.ceil(jump / CYCLE_NANOS);
} else {
// Execute one ARM instruction
const cycles = core.executeInstruction();
clock.tick(cycles * CYCLE_NANOS);
cyclesDone += cycles;
}
}
```
This allows `delay(1000)` to complete in microseconds of real time instead of simulating all 125 million cycles.
### Variable Speed
The simulation speed can be adjusted:
```typescript
simulator.setSpeed(speed: number): void
// speed: 0.1 (10% = very slow, for debugging) to 10.0 (10× faster)
```
---
## 14. Pin Mapping
The Pico has 40 physical pins. Below is the mapping from board pin names to GPIO numbers:
| Board Pin | GPIO | Function | Board Pin | GPIO | Function |
> All GPIO pins support PWM. GPIO 0–22 and 26–29 are available for general-purpose digital I/O. GPIO 23–25 are internal (power control, LED, SMPS mode).
---
## 15. Oscilloscope / Logic Analyzer
The RP2040 emulator provides timestamps for every GPIO transition using the internal simulation clock:
This enables the built-in logic analyzer to display accurate waveforms for signals like PWM, UART bit patterns, and I2C clock/data.
---
## 16. Known Limitations
| Limitation | Detail |
| ---------- | ------ |
| Single-core only | RP2040 is dual-core; emulator runs core 0 only. Code that uses `multicore_launch_core1()` will not execute on core 1 |
| No PIO | PIO state machines return 0. Libraries that depend on PIO (WS2812 NeoPixels, DHT sensors, I2S audio, quadrature encoders) will not work |
| No WiFi (Pico W) | The CYW43439 wireless chip is not emulated; `WiFi.begin()` will not connect |
| No USB device | USB HID, CDC, and MIDI device modes are not emulated |
| No DMA | DMA transfers return without moving data; memcpy-based alternatives work fine |
| No hardware FPU | ARM Cortex-M0+ has no floating-point unit; float operations are emulated in software (slower, but correct) |
| Timing accuracy | Emulation runs at variable speed; `micros()` and `millis()` track simulated time, not wall-clock time |
| Flash writes | `LittleFS`, `EEPROM.commit()`, and other flash-write operations may not persist across simulated resets |
| Serial redirect | `Serial` is mapped to `Serial1` (UART1) at compile time; code that uses `Serial1` directly alongside `Serial` may behave unexpectedly |
---
## 17. Tests
RP2040 emulation tests are in `frontend/src/__tests__/`: