WeAct Studio RP2350A Core Board (RP2350A)
A Pico-2-shaped RP2350A core board that ships in two incompatible
revisions under one name. V2.0 is a pin-for-pin Pico 2 replacement; V1.0
brings out every GPIO instead, and pays for it by moving the power pin, the
ADC reference and three GPIO functions. Firmware built for the wrong one
compiles cleanly and then drives the wrong pins — so establish the revision
before writing any code, and read the reference files rather than assuming
Pico 2 behaviour.
reference/board-hardware.md — the complete board reference: how to tell
the revisions apart, the 40-pin map for both, the off-header GPIO, power
trees, clocks, memory map, vendor-material inventory plus a development
guide (Part II: §8 toolchain, §10 peripheral cookbook, §11 RP2350 gotchas
including erratum E9 and the PWM slice map, §12 flashing, §13 a
symptom → cause → fix table).
reference/recipes.md — copy-paste code: platformio.ini with all four
envs, the revision guard, reading the real flash size out of the chip,
LEDs, the KEY button, V2.0 power sensing, ADC, Wire1 remapping, PWM,
EEPROM, LittleFS, core 1.
template/ — a project that builds clean for both revisions and both
flash sizes, in two variants, plus a scaffold script. See
template/README.md.
Orientation
|
|
| MCU |
RP2350A, QFN-60 — 2 × Cortex-M33 @ 150 MHz (or 2 × Hazard3 RISC-V, not reachable from this toolchain), 30 GPIO |
| Memory |
520 KB SRAM, 8 KB OTP, no internal flash — 4 MB or 16 MB QSPI, XIP from 0x10000000. Max sketch 4,190,208 B / 16,773,120 B (last 4 KB = emulated EEPROM) |
| Clock |
12 MHz crystal → 150 MHz. No 32.768 kHz crystal fitted |
| LED |
V1.0: GP25 green + GP24 blue · V2.0: GP25 green only. All active-HIGH through 5.1 kΩ — deliberately dim, and not on the header |
| Button |
RESET (→ RUN, not readable) and BOOT (→ QSPI_SS, readable at runtime as if (BOOTSEL)) on both · V1.0 only: 23@KEY on GP23, active-LOW, 5.1 kΩ pull-up |
| USB |
USB-C, FS device/host. Serial = USB CDC; 1200-baud touch reboots into BOOTSEL; enumerates 2E8A:000F, BOOTSEL drive is named RP2350 (not RPI-RP2) |
| ADC |
12-bit: A0=GP26, A1=GP27, A2=GP28, A3=GP29 — free input on V1.0, VSYS sense on V2.0 + channel 4 = die temp. analogRead is 10-bit by default. ADC_VREF on header pin 35 (V2.0) or test point T1 only (V1.0) |
| PWM |
analogWrite on any GPIO, 8-bit @ 1 kHz. 12 slices exist but only slices 0–7 are reachable: 4 GPIOs per slice, and GPn / GPn+16 share a channel |
| Bus defaults |
Wire = 4/5 · Wire1 = 26/27 (= A0/A1!) · SPI = 16/19/18/17 · SPI1 = 12/15/14/13 · Serial1 = 0/1 · Serial2 = 8/9 |
| Power |
V1.0: pin 39 5V → LDO, 3.6–6.5 V → 3.3 V @ 800 mA · V2.0: pin 39 VSYS → buck-boost, 1.8–5.5 V. Pin 40 is silkscreened VIN on both and is USB VBUS, not an input |
| Debug |
4 pads at the far end: 3V3 / DIO / CLK / GND. Boot ROM is mask ROM — unbrickable |
| Size |
51.00 × 21.00 mm, rows 17.78 mm apart — same footprint as a Pico 2 |
| Toolchain |
PlatformIO + raspberrypi 1.20.0 + arduino-pico 6.0.0 (board = rpipico2, board_build.core = earlephilhower) |
Rules that prevent the expensive mistakes
Each of these produces a failure that looks like something else.
- Establish the revision before writing code. V1.0 and V2.0 differ on
GP23, GP24, GP29 and on what pin 39 accepts. Ask the user, or have them
look at the top row of pins:
VREF in the ADC-reference slot and one LED
means V2.0; 29 in that slot, two LEDs and a third button marked
23@KEY means V1.0. Then build with -DBOARD_REV=10 or =20 — the
template's board.h refuses to compile without it, on purpose.
board = rpipico2, never weact_rp2350b. There is no PlatformIO
board definition for this board. weact_rp2350b exists in arduino-pico
and is the RP2350B core board — QFN-80, 48 GPIO, 8 ADC channels, a
different pinout. It builds and then everything above GP29 is fiction.
rpipico2 is exactly right for V2.0 and the best available base for V1.0.
board_build.core = earlephilhower in platformio.ini. Plain
framework = arduino selects the Arduino Mbed core, which does not
support RP2350 at all. The failure reads as a broken toolchain, not a
missing ini line.
- The pin silkscreened
VIN is USB VBUS, on both revisions. The supply
input is the pin next to it, and the two boards do not accept the same
voltage there: V1.0's 5V is an LDO input wanting 3.6–6.5 V, V2.0's
VSYS is a buck-boost input wanting 1.8–5.5 V. 6.5 V into a V2.0 is
over the absolute maximum; a 3.7 V Li-ion into a V1.0 browns out as the
cell discharges. Only V2.0 runs a single cell properly.
Serial is USB CDC, not UART0. A USB-serial adapter on GP0/GP1 sees
nothing from Serial.print — those pins are Serial1. The CDC port
exists only once the host opens it: earlier bytes are dropped, and
while (!Serial) blocks startup until a monitor connects. Never gate
bring-up on the port being open.
analogRead returns 10-bit values (0–1023) by default on a 12-bit
ADC. Symptom: "pinned at 1023 at full scale", or a calibration that is off
by exactly 4×. analogReadResolution(12) before sampling.
- GP29 is not the same pin on the two boards. On V1.0 it is ADC3 on
header pin 35, a free analog input. On V2.0 it is the VSYS sense (test pad
only) and header pin 35 is
ADC_VREF instead. Code that reads "the fourth
ADC channel" measures the supply on one board and your sensor on the
other.
- The V2.0 VSYS sense is nominally ÷3, but through a FET. R23/R24 100 kΩ
into R25 100 kΩ works out to VSYS/3 — the same ×3 math as a Pico — except
there is a pass FET with its gate at 3V3 in the path, whose resistance
varies with VSYS. Use it for trends and thresholds; calibrate against a
meter before quoting an absolute battery voltage.
INPUT_PULLDOWN is not usable on RP2350 (erratum RP2350-E9). An input
whose pad sits between logic levels leaks up to ~120 µA, which overwhelms
the internal pull-down: the pin latches at ~2 V and reads HIGH forever.
Wire buttons to GND with INPUT_PULLUP, or fit an external pull-down of
8.2 kΩ or less. The same leakage costs ~120 µA per floating input — on a
battery build that is the whole sleep budget. V1.0's 23@KEY is
active-LOW with a 5.1 kΩ pull-up and is unaffected.
- PWM aliasing is four-wide, not two. Slice =
(gpio >> 1) & 7, so
slices 0–7 each serve four pins (0/1/16/17, 2/3/18/19, …) and only
GP14/GP15 are unaliased. Pins on one slice share the frequency; pins 16
apart share the channel, so analogWrite(2, x) and analogWrite(18, y)
write the same duty register and the last one wins for both. A
servo/stepper pair that glitches like a power problem is usually this.
- Nothing on the board says whether it has 4 MB or 16 MB of flash. The
build believes
board_upload.maximum_size. Claiming 16 MB on a 4 MB
board puts the EEPROM sector and any filesystem past the end of the chip.
Read the JEDEC ID at boot (recipe 3 — the template prints both the real
size and the one the build assumed), then pick the matching env.
picotool info -d in BOOTSEL is a cross-check, but on a third-party board
its flash-size field comes from unprogrammed OTP and may say nothing.
board_build.f_flash is an ESP-ism and does nothing on this platform.
- Wire1 defaults to GP26/GP27 — the same pins as A0/A1. I2C corruption
that starts the moment
analogRead is called is this, not noise.
Wire1.setSDA()/setSCL() to another i2c1 pair (2/3, 6/7, 10/11, 14/15,
18/19) before begin().
- The LEDs run through 5.1 kΩ. ~0.25 mA: they are visible in shade and
almost invisible in daylight. Do not debug a blink sketch that is working.
- EEPROM lives in flash and only persists on
commit(), and flash
writes stall both cores because code runs from the same QSPI chip via
XIP. A sketch that never commits loses everything on reboot, silently;
one that commits inside a timing-critical loop hiccups in a way that looks
like a scheduler bug.
flash_do_cmd() must run during startup, interrupts off. It stops XIP
to talk to the flash chip. Called from a running application — or with
core 1 executing — it hangs the board. That is why the template reads the
JEDEC ID on the first line of setup().
- RP2350 UF2 files are not RP2040 UF2 files. Different family ID, and
the BOOTSEL drive is named
RP2350. An RP2040 image dropped on it is
rejected rather than run, and a .uf2 from a Pico project will not boot
here.
- GP26–GP29 are not 5 V tolerant and carry a reverse diode to 3V3.
Above ~3.6 V they damage the chip, and any voltage on them while the board
is unpowered back-powers the whole board through the pin.
When the task is powering it from something other than USB
This is where the two revisions actually diverge, so answer the revision
question first.
V2.0 behaves like a Pico 2: VSYS (pin 39) takes 1.8–5.5 V into a
buck-boost, so a single Li-ion cell, 3 × AA, or any 2–5 V supply works
directly and stays working as the cell sags. GP24 tells you whether USB is
present and GP29 gives you approximately VSYS/3, so battery monitoring and
USB/battery switchover need no extra parts. If USB is the only source you may
short VBUS to VSYS to remove the Schottky drop. USB host mode needs 5 V fed
to the VIN pin (pin 40), not to VSYS.
V1.0 has an LDO instead: pin 39 (5V) wants 3.6–6.5 V and drops out
below ~3.6 V, so a 1S Li-ion works only down to ~3.6 V — most of the cell is
unusable, and the brown-out looks like a firmware crash. There is no VSYS or
VBUS sense on any GPIO, so battery monitoring means wiring a divider of your
own into one of the four ADC inputs (GP29/ADC3 is free on this revision, which
is convenient). And because it is linear, input current equals output current:
6.5 V in at 300 mA out dissipates about a watt in a SOT-23-5 package.
On both: 3V3 (pin 36) is an output — never back-feed it — and EN (pin 37)
pulled low kills the rail and makes the board look bricked with USB attached.
Starting a new project
Do not hand-assemble one. template/ builds clean for both revisions;
scaffold from it:
~/.claude/skills/rp2350a-weact/template/variants/new-project.sh <dir> \
[--full|--minimal] [--v20|--v10] [--16mb]
cd <dir> && pio run -t upload -t monitor
--minimal — blink on GP25, which is a user LED on both revisions.
32,916 B flash / 8,504 B RAM. Flash this first on an unfamiliar board:
if it does not blink after upload, the problem is the cable, the BOOT dance
or the env choice, not the code. (The floor is TinyUSB CDC; a bare blink
costs ~33 KB here.)
--full (default) — non-blocking heartbeat, USB-CDC report, the real flash
size read from the JEDEC ID, A0 at 12 bits, die temperature, EEPROM boot
counter, plus the per-revision extras: VSYS + VBUS sense and the SMPS mode
pin on V2.0, the second LED and the KEY button on V1.0. 37,676 B flash
(V2.0) / 9,824 B RAM.
All four envs — weact_rp2350a_v20, _v20_16mb, _v10, _v10_16mb —
build as-is with platform-raspberrypi 1.20.0 + arduino-pico 6.0.0 (verified).
Nothing is generated and no paths are embedded, so copying the tree by hand
works identically; template/README.md maps files to subsystems.
When the user already has a project, prefer fixing their platformio.ini
(board = rpipico2, board_build.core = earlephilhower, the right
board_upload.maximum_size) and adding the BOARD_REV guard over rewriting
their code — that is usually the whole fix.
Flashing
Over the USB-C cable, nothing pressed:
pio run -t upload -t monitor
PlatformIO opens the CDC port at 1200 baud, the running sketch reboots itself
into BOOTSEL, picotool flashes and reboots the board; the LED is blinking
again within a second of SUCCESS. The board enumerates as a CDC port
(/dev/cu.usbmodem*, COMx) at VID:PID 2E8A:000F; the monitor's baud rate
is ignored.
When the firmware cannot respond — crashed, USB disabled, power-only cable, or
a blank board — the sequence is: hold BOOT (the button beside the USB
connector) → plug or replug USB → release → a drive named RP2350
appears → either re-run pio run -t upload (picotool picks up the BOOTSEL
device) or drag .pio/build/<env>/firmware.uf2 onto the drive. Without
unplugging: hold BOOT, tap RESET, release BOOT. BOOT is sampled at power-up
only — pressing it on a running board does nothing.
That BOOT route always works — the boot ROM is mask ROM and nothing you flash
can disable it, so the board cannot be bricked. Note that pio run -t erase
copies PlatformIO's bundled flash_nuke.uf2, which carries the RP2040
and absolute UF2 family IDs and no RP2350 family: do not count on it here
(untested on this board), and the picotool 2.0.0 that ships with the platform
has no erase subcommand. For a genuine clean slate, flash a flash_nuke
built for RP2350 from pico-examples; for the ordinary case, flashing a
known-good sketch over the top is enough. SWD on the four end pads
(3V3/DIO/CLK/GND, with a Debug Probe or
upload_protocol = cmsis-dap) is for live debugging, not for recovery.
Reporting
State honestly what was verified on hardware and what came from the schematic.
In this skill: only RP2350A_V20 was run on hardware — the --minimal
blink on GP25 and the USB-C upload path. All four template envs and every
recipe marked as such build clean against platform-raspberrypi 1.20.0 +
arduino-pico 6.0.0. Everything specific to V1.0 — the GP24/GP25 LEDs, the
GP23 KEY, the free ADC3 on GP29, the LDO's 3.6–6.5 V range — is transcribed
from V10_SCH.pdf and has not been run. No voltage, current, ADC or timing
figure anywhere in this skill was measured: the ADC reference network, the
VSYS ÷3 factor, the LED currents and the regulator ratings are
schematic- and datasheet-derived. Core-behaviour claims (read-resolution
default, Wire1 pins, the PWM slice formula, the 1200-baud touch) come from
the arduino-pico 6.0.0 and pico-sdk sources. Anything above 150 MHz that a
user asks for is overclocking — label it as outside spec.
1---2name: rp2350a-weact3description: WeAct Studio RP2350A Core Board (RP2350A)4---56# WeAct Studio RP2350A Core Board (RP2350A)78A Pico-2-shaped RP2350A core board that ships in **two incompatible9revisions under one name**. V2.0 is a pin-for-pin Pico 2 replacement; V1.010brings out every GPIO instead, and pays for it by moving the power pin, the11ADC reference and three GPIO functions. Firmware built for the wrong one12compiles cleanly and then drives the wrong pins — so establish the revision13before writing any code, and read the reference files rather than assuming14Pico 2 behaviour.1516- `reference/board-hardware.md` — the complete board reference: how to tell17 the revisions apart, the 40-pin map for both, the off-header GPIO, power18 trees, clocks, memory map, vendor-material inventory **plus** a development19 guide (Part II: §8 toolchain, §10 peripheral cookbook, §11 RP2350 gotchas20 including erratum E9 and the PWM slice map, §12 flashing, §13 a21 symptom → cause → fix table).22- `reference/recipes.md` — copy-paste code: `platformio.ini` with all four23 envs, the revision guard, reading the real flash size out of the chip,24 LEDs, the KEY button, V2.0 power sensing, ADC, `Wire1` remapping, PWM,25 EEPROM, LittleFS, core 1.26- `template/` — a **project that builds clean** for both revisions and both27 flash sizes, in two variants, plus a scaffold script. See28 `template/README.md`.2930## Orientation3132| | |33|---|---|34| MCU | RP2350A, QFN-60 — 2 × Cortex-M33 @ **150 MHz** (or 2 × Hazard3 RISC-V, not reachable from this toolchain), 30 GPIO |35| Memory | 520 KB SRAM, 8 KB OTP, **no internal flash** — 4 MB **or** 16 MB QSPI, XIP from `0x10000000`. Max sketch 4,190,208 B / 16,773,120 B (last 4 KB = emulated EEPROM) |36| Clock | 12 MHz crystal → 150 MHz. No 32.768 kHz crystal fitted |37| LED | **V1.0**: GP25 green + GP24 blue · **V2.0**: GP25 green only. All **active-HIGH** through 5.1 kΩ — deliberately dim, and not on the header |38| Button | RESET (→ RUN, not readable) and BOOT (→ QSPI_SS, readable at runtime as `if (BOOTSEL)`) on both · **V1.0 only**: `23@KEY` on GP23, **active-LOW**, 5.1 kΩ pull-up |39| USB | USB-C, FS device/host. `Serial` = **USB CDC**; 1200-baud touch reboots into BOOTSEL; enumerates `2E8A:000F`, BOOTSEL drive is named **`RP2350`** (not `RPI-RP2`) |40| ADC | 12-bit: A0=GP26, A1=GP27, A2=GP28, **A3=GP29 — free input on V1.0, VSYS sense on V2.0** + channel 4 = die temp. `analogRead` is **10-bit by default**. ADC_VREF on header pin 35 (V2.0) or test point T1 only (V1.0) |41| PWM | `analogWrite` on any GPIO, 8-bit @ 1 kHz. 12 slices exist but only **slices 0–7 are reachable**: 4 GPIOs per slice, and GP*n* / GP*n+16* share a channel |42| Bus defaults | Wire = 4/5 · **Wire1 = 26/27 (= A0/A1!)** · SPI = 16/19/18/17 · SPI1 = 12/15/14/13 · Serial1 = 0/1 · Serial2 = 8/9 |43| Power | **V1.0**: pin 39 `5V` → LDO, **3.6–6.5 V** → 3.3 V @ 800 mA · **V2.0**: pin 39 `VSYS` → buck-boost, **1.8–5.5 V**. Pin 40 is silkscreened `VIN` on both and is **USB VBUS**, not an input |44| Debug | 4 pads at the far end: `3V3` / `DIO` / `CLK` / `GND`. Boot ROM is mask ROM — **unbrickable** |45| Size | 51.00 × 21.00 mm, rows 17.78 mm apart — same footprint as a Pico 2 |46| Toolchain | PlatformIO + `raspberrypi` **1.20.0** + arduino-pico **6.0.0** (`board = rpipico2`, `board_build.core = earlephilhower`) |4748## Rules that prevent the expensive mistakes4950Each of these produces a failure that looks like something else.51521. **Establish the revision before writing code.** V1.0 and V2.0 differ on53 GP23, GP24, GP29 and on what pin 39 accepts. Ask the user, or have them54 look at the top row of pins: `VREF` in the ADC-reference slot and one LED55 means V2.0; `29` in that slot, two LEDs and a third button marked56 `23@KEY` means V1.0. Then build with **`-DBOARD_REV=10` or `=20`** — the57 template's `board.h` refuses to compile without it, on purpose.582. **`board = rpipico2`, never `weact_rp2350b`.** There is no PlatformIO59 board definition for this board. `weact_rp2350b` exists in arduino-pico60 and is the **RP2350B** core board — QFN-80, 48 GPIO, 8 ADC channels, a61 different pinout. It builds and then everything above GP29 is fiction.62 `rpipico2` is exactly right for V2.0 and the best available base for V1.0.633. **`board_build.core = earlephilhower` in `platformio.ini`.** Plain64 `framework = arduino` selects the Arduino **Mbed** core, which does not65 support RP2350 at all. The failure reads as a broken toolchain, not a66 missing ini line.674. **The pin silkscreened `VIN` is USB VBUS, on both revisions.** The supply68 input is the pin *next* to it, and the two boards do not accept the same69 voltage there: V1.0's `5V` is an LDO input wanting **3.6–6.5 V**, V2.0's70 `VSYS` is a buck-boost input wanting **1.8–5.5 V**. 6.5 V into a V2.0 is71 over the absolute maximum; a 3.7 V Li-ion into a V1.0 browns out as the72 cell discharges. Only V2.0 runs a single cell properly.735. **`Serial` is USB CDC, not UART0.** A USB-serial adapter on GP0/GP1 sees74 nothing from `Serial.print` — those pins are `Serial1`. The CDC port75 exists only once the host opens it: earlier bytes are dropped, and76 `while (!Serial)` blocks startup until a monitor connects. Never gate77 bring-up on the port being open.786. **`analogRead` returns 10-bit values (0–1023) by default** on a 12-bit79 ADC. Symptom: "pinned at 1023 at full scale", or a calibration that is off80 by exactly 4×. `analogReadResolution(12)` before sampling.817. **GP29 is not the same pin on the two boards.** On V1.0 it is ADC3 on82 header pin 35, a free analog input. On V2.0 it is the VSYS sense (test pad83 only) and header pin 35 is `ADC_VREF` instead. Code that reads "the fourth84 ADC channel" measures the supply on one board and your sensor on the85 other.868. **The V2.0 VSYS sense is nominally ÷3, but through a FET.** R23/R24 100 kΩ87 into R25 100 kΩ works out to VSYS/3 — the same ×3 math as a Pico — except88 there is a pass FET with its gate at 3V3 in the path, whose resistance89 varies with VSYS. Use it for trends and thresholds; calibrate against a90 meter before quoting an absolute battery voltage.919. **`INPUT_PULLDOWN` is not usable on RP2350** (erratum RP2350-E9). An input92 whose pad sits between logic levels leaks up to ~120 µA, which overwhelms93 the internal pull-down: the pin latches at ~2 V and reads HIGH forever.94 Wire buttons to GND with `INPUT_PULLUP`, or fit an **external pull-down of95 8.2 kΩ or less**. The same leakage costs ~120 µA per floating input — on a96 battery build that is the whole sleep budget. V1.0's `23@KEY` is97 active-LOW with a 5.1 kΩ pull-up and is unaffected.9810. **PWM aliasing is four-wide, not two.** Slice = `(gpio >> 1) & 7`, so99 slices 0–7 each serve four pins (0/1/16/17, 2/3/18/19, …) and only100 GP14/GP15 are unaliased. Pins on one slice share the frequency; pins 16101 apart share the *channel*, so `analogWrite(2, x)` and `analogWrite(18, y)`102 write the same duty register and the last one wins for both. A103 servo/stepper pair that glitches like a power problem is usually this.10411. **Nothing on the board says whether it has 4 MB or 16 MB of flash.** The105 build believes `board_upload.maximum_size`. Claiming 16 MB on a 4 MB106 board puts the EEPROM sector and any filesystem past the end of the chip.107 Read the JEDEC ID at boot (recipe 3 — the template prints both the real108 size and the one the build assumed), then pick the matching env.109 `picotool info -d` in BOOTSEL is a cross-check, but on a third-party board110 its flash-size field comes from unprogrammed OTP and may say nothing.111 `board_build.f_flash` is an ESP-ism and does nothing on this platform.11212. **Wire1 defaults to GP26/GP27 — the same pins as A0/A1.** I2C corruption113 that starts the moment `analogRead` is called is this, not noise.114 `Wire1.setSDA()/setSCL()` to another i2c1 pair (2/3, 6/7, 10/11, 14/15,115 18/19) before `begin()`.11613. **The LEDs run through 5.1 kΩ.** ~0.25 mA: they are visible in shade and117 almost invisible in daylight. Do not debug a blink sketch that is working.11814. **EEPROM lives in flash and only persists on `commit()`,** and flash119 writes stall *both* cores because code runs from the same QSPI chip via120 XIP. A sketch that never commits loses everything on reboot, silently;121 one that commits inside a timing-critical loop hiccups in a way that looks122 like a scheduler bug.12315. **`flash_do_cmd()` must run during startup, interrupts off.** It stops XIP124 to talk to the flash chip. Called from a running application — or with125 core 1 executing — it hangs the board. That is why the template reads the126 JEDEC ID on the first line of `setup()`.12716. **RP2350 UF2 files are not RP2040 UF2 files.** Different family ID, and128 the BOOTSEL drive is named `RP2350`. An RP2040 image dropped on it is129 rejected rather than run, and a `.uf2` from a Pico project will not boot130 here.13117. **GP26–GP29 are not 5 V tolerant and carry a reverse diode to 3V3.**132 Above ~3.6 V they damage the chip, and any voltage on them while the board133 is unpowered back-powers the whole board through the pin.134135## When the task is powering it from something other than USB136137This is where the two revisions actually diverge, so answer the revision138question first.139140**V2.0** behaves like a Pico 2: VSYS (pin 39) takes 1.8–5.5 V into a141buck-boost, so a single Li-ion cell, 3 × AA, or any 2–5 V supply works142directly and stays working as the cell sags. GP24 tells you whether USB is143present and GP29 gives you approximately VSYS/3, so battery monitoring and144USB/battery switchover need no extra parts. If USB is the only source you may145short VBUS to VSYS to remove the Schottky drop. USB *host* mode needs 5 V fed146to the `VIN` pin (pin 40), not to VSYS.147148**V1.0** has an LDO instead: pin 39 (`5V`) wants **3.6–6.5 V** and drops out149below ~3.6 V, so a 1S Li-ion works only down to ~3.6 V — most of the cell is150unusable, and the brown-out looks like a firmware crash. There is no VSYS or151VBUS sense on any GPIO, so battery monitoring means wiring a divider of your152own into one of the four ADC inputs (GP29/ADC3 is free on this revision, which153is convenient). And because it is linear, input current equals output current:1546.5 V in at 300 mA out dissipates about a watt in a SOT-23-5 package.155156On both: `3V3` (pin 36) is an output — never back-feed it — and `EN` (pin 37)157pulled low kills the rail and makes the board look bricked with USB attached.158159## Starting a new project160161Do not hand-assemble one. `template/` builds clean for both revisions;162scaffold from it:163164```sh165~/.claude/skills/rp2350a-weact/template/variants/new-project.sh <dir> \166 [--full|--minimal] [--v20|--v10] [--16mb]167cd <dir> && pio run -t upload -t monitor168```169170- `--minimal` — blink on GP25, which is a user LED on both revisions.171 **32,916 B** flash / 8,504 B RAM. Flash this first on an unfamiliar board:172 if it does not blink after upload, the problem is the cable, the BOOT dance173 or the env choice, not the code. (The floor is TinyUSB CDC; a bare blink174 costs ~33 KB here.)175- `--full` (default) — non-blocking heartbeat, USB-CDC report, the real flash176 size read from the JEDEC ID, A0 at 12 bits, die temperature, EEPROM boot177 counter, plus the per-revision extras: VSYS + VBUS sense and the SMPS mode178 pin on V2.0, the second LED and the KEY button on V1.0. **37,676 B** flash179 (V2.0) / 9,824 B RAM.180181All four envs — `weact_rp2350a_v20`, `_v20_16mb`, `_v10`, `_v10_16mb` —182build as-is with platform-raspberrypi 1.20.0 + arduino-pico 6.0.0 (verified).183Nothing is generated and no paths are embedded, so copying the tree by hand184works identically; `template/README.md` maps files to subsystems.185186When the user already has a project, prefer fixing their `platformio.ini`187(`board = rpipico2`, `board_build.core = earlephilhower`, the right188`board_upload.maximum_size`) and adding the `BOARD_REV` guard over rewriting189their code — that is usually the whole fix.190191## Flashing192193Over the USB-C cable, nothing pressed:194195```sh196pio run -t upload -t monitor197```198199PlatformIO opens the CDC port at 1200 baud, the running sketch reboots itself200into BOOTSEL, picotool flashes and reboots the board; the LED is blinking201again within a second of `SUCCESS`. The board enumerates as a CDC port202(`/dev/cu.usbmodem*`, `COMx`) at VID:PID `2E8A:000F`; the monitor's baud rate203is ignored.204205When the firmware cannot respond — crashed, USB disabled, power-only cable, or206a blank board — the sequence is: hold **BOOT** (the button beside the USB207connector) → plug or replug USB → release → a drive named **`RP2350`**208appears → either re-run `pio run -t upload` (picotool picks up the BOOTSEL209device) or drag `.pio/build/<env>/firmware.uf2` onto the drive. Without210unplugging: hold BOOT, tap RESET, release BOOT. BOOT is sampled at power-up211only — pressing it on a running board does nothing.212213That BOOT route always works — the boot ROM is mask ROM and nothing you flash214can disable it, so the board cannot be bricked. Note that `pio run -t erase`215copies PlatformIO's bundled `flash_nuke.uf2`, which carries the **RP2040**216and absolute UF2 family IDs and no RP2350 family: do not count on it here217(untested on this board), and the picotool 2.0.0 that ships with the platform218has no `erase` subcommand. For a genuine clean slate, flash a `flash_nuke`219built for RP2350 from pico-examples; for the ordinary case, flashing a220known-good sketch over the top is enough. SWD on the four end pads221(`3V3`/`DIO`/`CLK`/`GND`, with a Debug Probe or222`upload_protocol = cmsis-dap`) is for live debugging, not for recovery.223224## Reporting225226State honestly what was verified on hardware and what came from the schematic.227In this skill: **only RP2350A_V20 was run on hardware** — the `--minimal`228blink on GP25 and the USB-C upload path. All four template envs and every229recipe marked as such build clean against platform-raspberrypi 1.20.0 +230arduino-pico 6.0.0. Everything specific to **V1.0 — the GP24/GP25 LEDs, the231GP23 KEY, the free ADC3 on GP29, the LDO's 3.6–6.5 V range — is transcribed232from `V10_SCH.pdf` and has not been run.** No voltage, current, ADC or timing233figure anywhere in this skill was measured: the ADC reference network, the234VSYS ÷3 factor, the LED currents and the regulator ratings are235schematic- and datasheet-derived. Core-behaviour claims (read-resolution236default, `Wire1` pins, the PWM slice formula, the 1200-baud touch) come from237the arduino-pico 6.0.0 and pico-sdk sources. Anything above 150 MHz that a238user asks for is overclocking — label it as outside spec.