15. [Differences vs Other Emulators](#15-differences-vs-other-emulators)
16. [Key Files](#16-key-files)
---
## 1. Overview
The **Raspberry Pi 3B** is a full Linux single-board computer based on the **Broadcom BCM2837** SoC (4× ARM Cortex-A53, ARMv8 64-bit). Unlike the other boards in Velxio — which compile and run Arduino C++ code — the Raspberry Pi 3 emulation **boots a real Raspberry Pi OS** (Trixie) inside QEMU and lets you run Python scripts that interact with GPIO.
There is **no compilation step** for the Raspberry Pi: you write a Python script in the editor, the backend uploads it to the emulated filesystem, and the Pi OS executes it directly.
### Emulation Engine Comparison
| Board | Engine | Location | Language |
| ----- | ------ | -------- | -------- |
| Arduino Uno / Nano / Mega | avr8js | Browser | C++ (Arduino) |
| Raspberry Pi Pico | rp2040js | Browser | C++ (Arduino) |
When a Python script does `import RPi.GPIO as GPIO`, it gets this shim instead of the real hardware driver. The shim communicates over `/dev/ttyAMA1` (the second QEMU serial port) using a simple text protocol.
### GPIO Text Protocol
```text
Pi → Backend (output state change):
"GPIO <bcm_pin><0|1>\n"
Example: "GPIO 17 1\n" ← GPIO 17 driven HIGH
Backend → Pi (external input, e.g. button press from canvas):
> **PWM limitation:** The shim does not implement real PWM waveforms. It converts duty cycle to a binary state: `duty > 50` → HIGH, `duty ≤ 50` → LOW. Visual LED dimming is not supported for Pi GPIO PWM.
### Example Python Script (Blink LED)
```python
#!/usr/bin/env python3
import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.OUT)
try:
while True:
GPIO.output(17, GPIO.HIGH)
print("LED ON")
time.sleep(1)
GPIO.output(17, GPIO.LOW)
print("LED OFF")
time.sleep(1)
finally:
GPIO.cleanup()
```
---
## 7. WebSocket Protocol
All communication between the frontend and backend passes through a single WebSocket connection per board instance.
| `error` | `{ message: string }` | Error from QEMU or backend |
---
## 8. Serial Communication (UART)
The Raspberry Pi 3 exposes two UART ports through QEMU:
| Port | Device | Physical Pins | Role |
| ---- | ------ | ------------- | ---- |
| UART0 (ttyAMA0) | `/dev/ttyAMA0` | GPIO14 (TX), GPIO15 (RX) | User serial — `print()` output, `input()`, `serial.Serial()` |
| UART1 (ttyAMA1) | `/dev/ttyAMA1` | — (internal) | GPIO shim protocol — reserved, not accessible to user scripts |
### Serial Monitor Integration
Anything the Python script writes to stdout or to `/dev/ttyAMA0` appears in the Serial Monitor panel:
```python
# stdout (print) — captured automatically
print("Hello from Pi!")
# Direct ttyAMA0 (explicit serial)
import serial
port = serial.Serial('/dev/ttyAMA0', baudrate=9600, timeout=1)
port.write(b"Hello Arduino!\n")
```
### Sending Text to the Pi
Text typed in the Serial Monitor input box is sent to ttyAMA0 as a `serial_input` message, which the Pi receives via `input()` or by reading `/dev/ttyAMA0`.
---
## 9. Pin Mapping — Physical to BCM GPIO
The Raspberry Pi 3B has a standard **40-pin GPIO header** (2 rows × 20 columns). The table below shows the mapping from physical pin number to BCM GPIO number:
| Physical | BCM | Function | Physical | BCM | Function |
boardPinToNumber('raspberry-pi-3', 'GND') // → null (not a GPIO)
```
---
## 10. Virtual File System (VFS)
Each Raspberry Pi 3 board instance has its own **virtual filesystem tree** stored in the `useVfsStore` Zustand store. This lets you create and edit Python scripts directly in the Velxio editor before they are uploaded to the Pi.
### Default VFS Tree
```text
/
└── home/
└── pi/
├── script.py ← main Python script (editable)
└── hello.sh ← example shell script
```
### Default `script.py`
```python
#!/usr/bin/env python3
import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.OUT)
while True:
GPIO.output(17, GPIO.HIGH)
print("LED on")
time.sleep(1)
GPIO.output(17, GPIO.LOW)
print("LED off")
time.sleep(1)
```
### VFS API
```typescript
const vfs = useVfsStore.getState();
vfs.initBoardVfs(boardId) // create default tree
vfs.createNode(boardId, parentId, 'app.py', 'file') // add new file
Files in the VFS are uploaded to the Pi OS at boot via the WebSocket connection before the script is executed.
---
## 11. Multi-Board Integration — Pi + Arduino
The Raspberry Pi 3 can be placed on the same canvas as Arduino or other boards. When wires connect a Pi GPIO pin to an Arduino pin, the stores route data between them automatically.
### Pi → Arduino (Serial TX)
```text
Pi Python script:
port.write(b"LED_ON\n")
│
▼ ttyAMA0 byte output
serial_output WebSocket message
│
▼ useSimulatorStore (serial callback)
AVRSimulator.serialWrite("L") ← feeds byte into Arduino RX FIFO
This example (included in the gallery as `pi-to-arduino-led-control`) demonstrates bidirectional serial communication:
**Pi Script:**
```python
import serial, time
port = serial.Serial('/dev/ttyAMA0', baudrate=9600, timeout=1)
for _ in range(3):
port.write(b"LED1_ON\n")
time.sleep(0.5)
port.write(b"LED1_OFF\n")
time.sleep(0.5)
port.write(b"LED2_ON\n")
time.sleep(2)
port.write(b"LED2_OFF\n")
```
**Arduino Sketch:**
```cpp
const int LED1 = 8, LED2 = 9;
void setup() {
Serial.begin(9600);
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
}
void loop() {
if (Serial.available()) {
String cmd = Serial.readStringUntil('\n');
if (cmd == "LED1_ON") digitalWrite(LED1, HIGH);
else if (cmd == "LED1_OFF") digitalWrite(LED1, LOW);
else if (cmd == "LED2_ON") digitalWrite(LED2, HIGH);
else if (cmd == "LED2_OFF") digitalWrite(LED2, LOW);
}
}
```
---
## 12. Boot Images
QEMU needs three files from the `/img/` directory to boot the Raspberry Pi 3:
| File | Size | Description |
| ---- | ---- | ----------- |
| `kernel8.img` | ~6 MB | ARM64 Linux kernel extracted from Raspberry Pi OS Trixie |
| `bcm271~1.dtb` | ~40 KB | Device tree binary — defines CPU, RAM, peripheral base addresses |
| `2025-12-04-raspios-trixie-armhf.img` | ~5.67 GB | Full Raspberry Pi OS SD card image (root filesystem) |
> The base SD image is **never modified**. Each session creates a **qcow2 copy-on-write overlay** (`overlay_<session_id>.qcow2`) that records only the changes made during that session. The overlay is automatically deleted when the session ends.
### Creating the Overlay at Runtime
```bash
# Backend does this automatically for each session: