The Raspberry Pi Pico 2 WH: Pre-Soldered RP2350 Power
The Raspberry Pi Pico 2 WH is the pre-soldered header variant of the RP2350-based wireless microcontroller, featuring the Infineon CYW43439 chip for Wi-Fi 4 and Bluetooth 5.2. Priced around $7 to $8, the 'WH' designation means it ships with 0.1-inch male headers already attached. This eliminates the risk of thermal damage to the dense QFN-60 RP2350 package and the nearby RF components during hand soldering, making it the definitive choice for rapid breadboard prototyping.
Unlike the original RP2040, the RP2350 introduces a dual-core, dual-architecture setup: you can boot into either Arm Cortex-M33 or Hazard3 RISC-V mode. Combined with 520KB of SRAM (up from 264KB) and 4MB of QSPI flash, the Pico 2 WH provides enough headroom to run TLS-encrypted MQTT connections and local I2C displays simultaneously without hitting memory walls.
Pico 2 Variant Decision Tree: Which Board to Buy?
Choosing the right Pico 2 variant depends entirely on your physical integration method and wireless requirements. Use this decision matrix to terminate your selection process.
| Board Variant | Wireless | Headers | Best Use Case | Concrete Pick When... |
|---|---|---|---|---|
| Pico 2 | No | No | Custom PCB design, SMD soldering | You are designing a custom carrier board and need to minimize BOM cost ($4). |
| Pico 2 H | No | Yes | Breadboarding offline logic | You need immediate breadboard access but zero RF requirements. |
| Pico 2 W | Yes | No | Permanent soldered IoT installs | You are soldering directly to a perfboard and have strict height constraints. |
| Pico 2 WH | Yes | Yes | Wireless IoT prototyping | You are breadboarding a Wi-Fi/BLE project today and want to avoid SMD rework. |
Hardware Spec Sheet and Pin Mapping
Before wiring, verify your components against the RP2350's electrical characteristics. The Pico 2 WH outputs 3.3V logic. Never feed 5V I2C signals into the GP pins without a level shifter.
RP2350 & CYW43439 Core Specifications
| Parameter | Specification | Notes |
|---|---|---|
| Processor | Dual-core Arm Cortex-M33 / Hazard3 RISC-V @ 150MHz | Selectable at boot via OTP/flash metadata |
| Memory | 520KB SRAM, 4MB QSPI Flash | Striped SRAM banks for concurrent bus access |
| Wireless | Infineon CYW43439 (802.11 b/g/n, BT 5.2) | Requires specific pico2_w MicroPython build |
| Power (Active) | ~25mA (Core) + ~120mA (Wi-Fi TX spike) | Ensure 500mA+ USB supply for TX bursts |
Project Pin Mapping (I2C0 Bus)
This project maps both the BME280 environmental sensor and the SSD1306 OLED to the same I2C0 bus. The RP2350 allows flexible pin muxing, but we use the default I2C0 pins for clean breadboard routing.
| Pico 2 WH Pin | Function | BME280 Breakout | SSD1306 OLED |
|---|---|---|---|
| Pin 1 (GP0) | I2C0 SDA | SDI / SDA | SDA |
| Pin 2 (GP1) | I2C0 SCL | SCK / SCL | SCL |
| Pin 36 (3V3) | Power Out | VIN / VCC | VCC |
| Pin 38 (GND) | Ground | GND | GND |
Build Guide: Wi-Fi MQTT Environmental Node
This build pushes temperature, humidity, and pressure data to an MQTT broker every 10 seconds, with a local OLED fallback for network-down scenarios.
Parts List
- MCU: Raspberry Pi Pico 2 WH (Pre-soldered headers)
- Sensor: Adafruit BME280 I2C/SPI Breakout (Product ID: 2652) or generic 3.3V BME280 module
- Display: 0.96-inch SSD1306 128x64 I2C OLED (4-pin variant)
- Prototyping: 830-point solderless breadboard, 22 AWG solid core jumper wires
- Power: 5V 2A USB-C power supply (to handle CYW43439 TX current spikes)
Wiring Steps
- Seat the MCU: Press the Pico 2 WH into the breadboard, ensuring the USB-C port overhangs the edge. Verify that the GP0 (Pin 1) and GP1 (Pin 2) align with your designated I2C rails.
- Wire the I2C Power Rails: Connect the breadboard's left red rail to Pico 3V3 (Pin 36) and the left blue rail to Pico GND (Pin 38). Do not use the VBUS (5V) pin for the sensors.
- Connect the BME280: Route GP0 to BME280 SDA, GP1 to BME280 SCL. Connect power and ground from the breadboard rails.
- Connect the SSD1306: Route the same GP0/GP1 lines to the OLED SDA/SCL. Connect power and ground.
- Add Pull-ups (If needed): Most Adafruit and quality generic breakouts include 4.7kΩ pull-up resistors. If using barebones modules, add 4.7kΩ resistors between SDA/SCL and 3.3V to prevent I2C bus hanging.
MicroPython Firmware and Complete Code
Target Board Variant: This code targets the Raspberry Pi Pico 2 W / WH. You must flash the specific pico2_w MicroPython UF2 file (v1.24.0 or newer) from the official MicroPython download page. The standard pico2 UF2 lacks the CYW43439 wireless drivers.
Before running the main script, open the Thonny or mpremote REPL and install the required packages via the MicroPython package manager:
import mip
mip.install('bme280')
mip.install('ssd1306')
mip.install('umqtt.simple')
main.py
import network
import time
import sys
from machine import Pin, I2C
import bme280
import ssd1306
from umqtt.simple import MQTTClient
import ubinascii
# --- PIN DEFINITIONS ---
I2C_SDA_PIN = 0
I2C_SCL_PIN = 1
I2C_FREQ = 400000
# --- NETWORK & MQTT CONFIG ---
WIFI_SSID = 'YourNetworkSSID'
WIFI_PASS = 'YourNetworkPassword'
MQTT_BROKER = '192.168.1.100'
MQTT_PORT = 1883
CLIENT_ID = ubinascii.hexlify(machine.unique_id()).decode()
MQTT_TOPIC = b'home/lab/environment'
# --- HARDWARE INIT ---
try:
i2c = I2C(0, sda=Pin(I2C_SDA_PIN), scl=Pin(I2C_SCL_PIN), freq=I2C_FREQ)
devices = i2c.scan()
if not devices:
raise RuntimeError('I2C scan returned 0 devices. Check wiring and pull-ups.')
# Assume BME280 is at 0x76 or 0x77, OLED is at 0x3C
bme_addr = 0x76 if 0x76 in devices else 0x77
oled_addr = 0x3C
sensor = bme280.BME280(i2c=i2c, address=bme_addr)
oled = ssd1306.SSD1306_I2C(128, 64, i2c, addr=oled_addr)
print(f'Hardware OK: BME at {hex(bme_addr)}, OLED at {hex(oled_addr)}')
except Exception as e:
print(f'FATAL HARDWARE ERROR: {e}')
sys.exit(1)
def connect_wifi():
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
# Prevent Wi-Fi LED from burning out / save power if desired, but keep on for debug
wlan.config(pm = 0xa11140)
if not wlan.isconnected():
print(f'Connecting to {WIFI_SSID}...')
wlan.connect(WIFI_SSID, WIFI_PASS)
timeout = 15
while not wlan.isconnected() and timeout > 0:
time.sleep(1)
timeout -= 1
if wlan.isconnected():
ip = wlan.ifconfig()[0]
print(f'Wi-Fi Connected. IP: {ip}')
return ip
else:
raise OSError('Wi-Fi connection timed out.')
def update_oled(ip, temp, hum, pres, status):
oled.fill(0)
oled.text(f'IP:{ip}', 0, 0)
oled.text(f'T:{temp}C', 0, 16)
oled.text(f'H:{hum}%', 0, 32)
oled.text(f'P:{pres}hPa', 0, 48)
oled.show()
# --- MAIN LOOP ---
try:
ip_addr = connect_wifi()
mqtt = MQTTClient(CLIENT_ID, MQTT_BROKER, MQTT_PORT, keepalive=30)
mqtt.connect()
print(f'MQTT Connected to {MQTT_BROKER}')
update_oled(ip_addr, '--', '--', '--', 'MQTT OK')
while True:
try:
temp = sensor.temperature[:-1] # Strip 'C' character
hum = sensor.humidity[:-1]
pres = sensor.pressure[:-3]
payload = f'{{"temp":{temp},"hum":{hum},"pres":{pres}}}'
mqtt.publish(MQTT_TOPIC, payload.encode())
print(f'Published: {payload}')
update_oled(ip_addr, temp, hum, pres, 'TX OK')
time.sleep(10)
except Exception as loop_err:
print(f'Loop Error: {loop_err}. Reconnecting MQTT...')
try:
mqtt.connect()
except:
machine.reset()
except OSError as net_err:
print(f'FATAL NETWORK ERROR: {net_err}')
update_oled('0.0.0.0', 'ERR', 'ERR', 'ERR', str(net_err))
except Exception as fatal_err:
print(f'FATAL SYSTEM ERROR: {fatal_err}')
sys.exit(1)
Debugging the CYW43439 and RP2350
The CYW43439 wireless chip on the Pico 2 WH communicates with the RP2350 over a dedicated high-speed SDIO/SPI bus. When wireless or I2C operations fail, MicroPython throws specific lwIP and runtime errors. Here is how to diagnose them.
The First 3 Things to Check When It Fails
- Verify the UF2 Build: Ensure you flashed
pico2_wand notpico2. The non-W build lacks the CYW43439 firmware blob, causing immediate boot panics whennetwork.WLANis called. - Check USB Power Delivery: The CYW43439 draws up to 150mA during Wi-Fi transmission bursts. If your PC USB port or wall wart limits current to 100mA, the Pico 2 WH will brownout and reset during
wlan.connect(). Use a dedicated 5V 2A supply. - I2C Address Conflicts: Run
i2c.scan()in the REPL. If it returns an empty list[], your SDA/SCL wires are swapped, or you are missing 4.7kΩ pull-up resistors on a barebones module.
Exact Error Strings and Ranked Causes
| Exact Error String | Ranked Causes & Fixes |
|---|---|
RuntimeError: no SDIO device found |
1. Wrong UF2 flashed (Fix: Flash pico2_w build).2. Corrupted flash filesystem (Fix: Hold BOOTSEL, flash flash_nuke.uf2, then re-flash MicroPython). |
OSError: [Errno 113] EHOSTUNREACH |
1. MQTT broker IP is incorrect or offline (Fix: Ping broker from PC). 2. Pico connected to a 5GHz-only Wi-Fi network (Fix: CYW43439 is 2.4GHz only; connect to 2.4GHz SSID). |
OSError: [Errno 118] EHOSTDOWN |
1. Broker rejected connection due to ACL/firewall (Fix: Check Mosquitto/broker logs). 2. Wi-Fi dropped during MQTT handshake (Fix: Move Pico closer to AP or add external 2.4GHz antenna mod). |
Extending and Simplifying the Build
Once the baseline MQTT node is stable, you can scale the project up for production or strip it down for ultra-low-power edge deployment.
How to Extend the Build
- Enable TLS Encryption: Swap
umqtt.simpleforumqtt.robustand pass SSL context parameters to the MQTTClient. The RP2350's Cortex-M33 includes cryptographic accelerators that handle AES-GCM handshakes with minimal CPU overhead compared to the RP2040. - Add BLE Beaconing: The CYW43439 supports concurrent Wi-Fi and Bluetooth. Use the
bluetoothmodule to broadcast iBeacon or Eddystone telemetry alongside your MQTT payloads without adding a second radio. - Implement RP2350 Deep Sleep: Utilize the new always-on power domain. Configure a GPIO pin or RTC alarm to wake the core, dropping idle current from ~25mA down to roughly 1.8mA between sensor reads.
How to Simplify the Build
- Drop the OLED: If the node is mounted in a ceiling or attic, remove the SSD1306. This frees up I2C bus bandwidth, eliminates the 4.7kΩ pull-up requirement if your BME280 has them onboard, and reduces code complexity by removing the
ssd1306frame buffer allocation (saving ~1KB of SRAM). - Switch to HTTP POST: If you don't want to maintain a local Mosquitto broker, replace the MQTT block with a standard
urequests.post()call to a free-tier InfluxDB or Home Assistant webhook. This removes the need for persistent socket keep-alives.
Code & Safety Caveat: The Raspberry Pi Pico 2 WH operates at safe DC voltages (3.3V/5V). However, when integrating this node into mains-powered HVAC systems to read duct temperatures, ensure the sensor breakout is galvanically isolated from mains voltage. Always defer to local electrical codes (NEC/IEC) when crossing the boundary from low-voltage DC to line-voltage AC equipment.
For deeper architectural details on the dual-core RP2350 memory striping and the CYW43439 SDIO interface, refer to the official Raspberry Pi Pico 2 documentation and the MicroPython RP2 quick reference. For MQTT payload structuring standards, consult the OASIS MQTT specification.






