When makers search for the raspberry pico w 2, they are looking for the RP2350-based wireless board officially named the Raspberry Pi Pico 2W. Released to address the power-consumption and security limitations of the original RP2040 Pico W, the Pico 2W pairs dual ARM Cortex-M33 cores with the Infineon CYW43439 Wi-Fi/Bluetooth module. It is a massive upgrade for battery-powered IoT nodes, but its new architecture introduces specific firmware quirks.

This guide walks through building a Wi-Fi MQTT greenhouse climate node. We will cover the hardware decision matrix, exact wiring, production-ready MicroPython code, and the specific error strings you will hit when the RP2350’s wireless stack misbehaves.

The Verdict: Which Board Variant Should You Actually Buy?

Before ordering parts, you need to select the right microcontroller for your power and security constraints. The naming convention can be confusing, so here is a decision path to terminate your board selection.

Use Case Board Pick Why?
Mains-powered IoT, high GPIO count needed ESP32-S3 (WROOM-1) More raw I/O, mature ESP-IDF ecosystem, no deep-sleep battery requirements.
Ultra-low cost, simple USB serial logger Raspberry Pi Pico 1 (RP2040) $4 price point, no Wi-Fi overhead, massive community support.
Battery-powered, secure Wi-Fi, needs OTA Raspberry Pi Pico 2W (RP2350) Hardware security (OTP/secure boot), 4MB flash, deeper sleep states than ESP32.
Concrete Pick: For this build, buy the Raspberry Pi Pico 2W with pre-soldered headers (4MB Flash). Do not buy the base Pico 2 (no wireless) or the older Pico 1W if you need the RP2350's improved power gating for battery operation.

Hardware BOM and Pin Mapping

This build avoids complex I2C multiplexing by using the RP2350’s internal temperature sensor alongside an external ADC-based capacitive soil moisture probe. This keeps the BOM cheap and the code self-contained.

Parts List

  • MCU: Raspberry Pi Pico 2W (RP2350, 4MB Flash) — ~$7.00
  • Sensor: Capacitive Soil Moisture Sensor v1.2 (Analog output) — ~$2.50
  • Power: 18650 Li-ion cell + Adafruit PowerBoost 1000C (or generic 5V USB power bank for bench testing)
  • Wiring: 22 AWG solid core hookup wire, half-size breadboard

Pin Mapping Table

Pico 2W Pin RP2350 Function Connected To Notes
GP26 (Pin 31) ADC0 Soil Sensor AOUT Reads 0-3.3V analog moisture level
3V3 (Pin 36) Power Out Soil Sensor VCC Do not use VBUS (5V) for this sensor
GND (Pin 38) Ground Soil Sensor GND Common ground required for ADC
Internal ADC4 Temp Sensor N/A (On-chip) RP2350 internal die temperature
Difficulty & Time: Intermediate. 45 minutes to wire and flash. Requires basic familiarity with Thonny IDE and MicroPython REPL.

Step-by-Step Wiring Procedure

  1. De-energize the board: Ensure the Pico 2W is unplugged from USB and the battery is disconnected.
  2. Mount the MCU: Press the Pico 2W into the breadboard, straddling the center trench so pins on both sides are accessible.
  3. Wire Power: Connect a jumper from the Pico’s 3V3 pin (Physical Pin 36) to the breadboard’s positive rail. Connect GND (Physical Pin 38) to the negative rail.
  4. Connect the Soil Sensor:
    • Sensor VCC to breadboard positive rail (3.3V).
    • Sensor GND to breadboard negative rail.
    • Sensor AOUT to Pico GP26 (Physical Pin 31).
  5. Verify Voltages: Before plugging in USB, use a multimeter in continuity mode to verify there is no short between the 3V3 and GND rails. Set the meter to DC Voltage, plug in USB, and confirm 3.25V–3.35V on the positive rail.

MicroPython Code: MQTT Publishing with Error Handling

Target Board Variant: This code is written specifically for the Raspberry Pi Pico 2W (RP2350) running MicroPython v1.23.0 or newer for RP2350. Do not flash the RP2040 Pico W firmware; the Wi-Fi chip initialization sequence differs.

The script connects to Wi-Fi, reads the internal die temperature and external soil moisture ADC, and publishes to an MQTT broker. It includes robust retry logic and exception handling to prevent the watchdog from resetting the board during network hiccups.


import network
import time
import machine
import ubinascii
from umqtt.simple import MQTTClient
import sys

# --- PIN & CONFIG DEFINITIONS ---
SOIL_ADC_PIN = 26
WIFI_SSID = "YourNetworkSSID"
WIFI_PASS = "YourNetworkPassword"
MQTT_BROKER = "192.168.1.50"
MQTT_PORT = 1883
MQTT_TOPIC_SOIL = b"greenhouse/soil"
MQTT_TOPIC_TEMP = b"greenhouse/temp"
SLEEP_INTERVAL_SEC = 300  # 5 minutes

# Initialize ADC for Soil Moisture (GP26)
soil_adc = machine.ADC(SOIL_ADC_PIN)
# Initialize Internal Temperature Sensor (ADC4 on RP2350)
temp_adc = machine.ADC(4)

def connect_wifi():
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    # Prevent Wi-Fi from waking up the core unnecessarily during sleep
    wlan.config(pm = 0xa11140) 
    
    if not wlan.isconnected():
        print(f"Connecting to {WIFI_SSID}...")
        wlan.connect(WIFI_SSID, WIFI_PASS)
        
        max_retries = 15
        for i in range(max_retries):
            if wlan.isconnected():
                break
            time.sleep(1)
            print(".", end="")
            
    if not wlan.isconnected():
        raise RuntimeError("WIFI connect failed: Timeout after 15s")
        
    print(f"\nConnected! IP: {wlan.ifconfig()[0]}")
    return wlan

def read_sensors():
    # Soil Moisture (16-bit raw value, higher = wetter for most capacitive sensors)
    soil_raw = soil_adc.read_u16()
    
    # Internal Temperature calculation for RP2350
    # Formula: 27 - (ADC_voltage - 0.706)/0.001721
    temp_raw = temp_adc.read_u16()
    adc_voltage = temp_raw * 3.3 / 65535
    temp_c = 27 - (adc_voltage - 0.706) / 0.001721
    
    return soil_raw, round(temp_c, 2)

def main():
    client_id = ubinascii.hexlify(machine.unique_id())
    
    try:
        wlan = connect_wifi()
        client = MQTTClient(client_id, MQTT_BROKER, port=MQTT_PORT, keepalive=60)
        client.connect()
        print(f"Connected to MQTT Broker: {MQTT_BROKER}")
        
        soil, temp = read_sensors()
        print(f"Publishing -> Soil: {soil}, Temp: {temp}C")
        
        client.publish(MQTT_TOPIC_SOIL, str(soil))
        client.publish(MQTT_TOPIC_TEMP, str(temp))
        
        client.disconnect()
        wlan.active(False) # Shut down radio to save power
        
    except OSError as e:
        print(f"Network/MQTT OSError: {e}")
        sys.exit(1) # Let external watchdog or RTC alarm handle restart
    except Exception as e:
        print(f"Unexpected Error: {e}")
        sys.exit(1)

    # Deep sleep implementation would go here using machine.deepsleep()
    print(f"Sleeping for {SLEEP_INTERVAL_SEC}s...")
    time.sleep(SLEEP_INTERVAL_SEC) 

if __name__ == "__main__":
    main()

Debugging: Exact Error Strings and First 3 Checks

The RP2350’s wireless stack is newer than the ESP32’s, meaning MicroPython error handling can sometimes throw opaque OS-level errors. When your node fails, here are the first three things to check, mapped to the exact error strings Thonny will output.

1. Error: OSError: [Errno 113] EHOSTUNREACH

What it means: The Pico connected to Wi-Fi, but the TCP socket cannot route to the MQTT broker’s IP address.

  • Cause A (Most Likely): Your MQTT broker (e.g., Mosquitto on a Pi) is on a different VLAN or subnet, and the router is dropping the packets.
  • Cause B: The broker IP in the code is a typo, or the broker service crashed.
  • Fix: Ping the broker IP from your PC. Verify Mosquitto is listening on 0.0.0.0:1883, not just 127.0.0.1.

2. Error: RuntimeError: WIFI connect failed: Timeout after 15s

What it means: The CYW43439 chip initialized, but the WPA2 handshake failed or timed out.

  • Cause A (Most Likely): You are trying to connect to a 5GHz or Wi-Fi 6 (802.11ax) network. The Pico 2W only supports 2.4GHz 802.11n.
  • Cause B: WPA3 enterprise security is enabled on your router, which the Pico’s firmware does not support.
  • Fix: Create a dedicated 2.4GHz IoT SSID on your router with WPA2-Personal (AES) security.

3. Error: ValueError: ADC pin 4 is not valid (or similar ADC read failure)

What it means: The code is trying to read the internal temperature sensor, but the firmware doesn't recognize ADC4.

  • Cause A (Most Likely): You accidentally flashed the RP2040 (Pico 1) MicroPython firmware onto your Pico 2W. The RP2040 uses ADC4 for temp, but early RP2350 builds mapped it differently or require specific SDK flags.
  • Fix: Download the correct RP2350 specific .uf2 file from the official MicroPython downloads page. Hold the BOOTSEL button, plug in USB, and drag the correct file over.
The First 3 Checks Rule: Before rewriting code, always check:
1. Is the router broadcasting a 2.4GHz signal?
2. Is the correct RP2350 .uf2 firmware flashed?
3. Is the MQTT broker actually running and accessible on port 1883?

Extending or Simplifying the Build

Depending on your infrastructure, you may need to strip this project down or scale it up. Here is exactly how to modify the architecture.

How to Simplify (No MQTT Broker)

If you do not want to maintain a Mosquitto broker on a local server, drop the MQTT protocol entirely. Replace the umqtt logic with the urequests library to send a simple HTTP POST request to a free service like IFTTT Webhooks or a local Node-RED HTTP endpoint.
Trade-off: HTTP requires more radio-on time than MQTT, which will reduce your 18650 battery life by roughly 30% per cycle.

How to Extend (Add Deep Sleep and a 12V Pump)

To make this a true autonomous greenhouse node, you need to control a water pump and sleep between reads.

  1. Add Deep Sleep: Replace time.sleep() at the end of the script with machine.deepsleep(SLEEP_INTERVAL_SEC * 1000). The RP2350’s power gating will drop current draw from ~20mA to microamps. Note that deep sleep resets the RAM; you will need to use the RTC or write state to the onboard flash if you need to persist variables across sleeps.
  2. Add a 12V Pump: Never connect a 12V inductive load directly to the Pico’s GPIO. Use a logic-level MOSFET (like an IRLZ44N) or an opto-isolated relay module. Connect the Pico’s GP15 through a 1kΩ gate resistor to the MOSFET gate, and wire the pump across your 12V supply and the MOSFET drain. Add a flyback diode (1N4007) across the pump terminals to prevent voltage spikes from frying the RP2350 when the pump turns off.

For further reading on the RP2350's security and power architecture, refer to the official Raspberry Pi RP2350 Datasheet and the MicroPython network.WLAN documentation.