The Raspberry Pi Pico 2, powered by the new RP2350 chip, is not just a clock-speed bump over the original. It introduces a dual-core Arm Cortex-M33 (or dual Hazard3 RISC-V) architecture, 520KB of SRAM, and a completely redesigned GPIO pad controller. For embedded builders, this means more headroom for concurrent tasks, but it also introduces new hardware quirks that will break legacy code if you aren't paying attention.
In this guide, we are building a high-polling environmental data logger using the Pico 2's dual I2C blocks to eliminate display jitter, and we will tear down the most common I2C failure mode specific to the RP2350 silicon.
Raspberry Pi Pico 2 vs. The Alternatives: The Decision Matrix
Before breadboarding, you need to confirm the Pico 2 is actually the right silicon for your job. The RP2350 is a massive leap, but it lacks native wireless on the base model and is strictly 3.3V logic. Use this decision path to lock in your board choice.
| Project Requirement | If you need this... | Choose this board |
|---|---|---|
| Raw DSP / Floating Point Math | Hardware FPU and dual-core concurrency | Raspberry Pi Pico 2 (Arm Core) |
| WiFi / BLE Connectivity | Wireless telemetry without external SPI modules | Raspberry Pi Pico 2 W (or ESP32-C3) |
| 5V Sensor Integration | Direct 5V I2C/SPI without logic level shifters | Arduino Nano / ESP32 (Pico 2 is strictly 3.3V) |
| Lowest Possible BOM Cost | Simple blinking LEDs or basic relay switching | Original Raspberry Pi Pico 1 (RP2040) |
Project Build: Dual-Core BME688 Environmental Logger
We are building a logger that reads a Bosch BME688 (gas, humidity, pressure, temp) and pushes the data to a 128x64 SSD1306 OLED. To prevent the I2C display writes from delaying the sensor reads, we will use I2C0 for the display and I2C1 for the sensor.
Parts List & Exact Variants
- MCU: Raspberry Pi Pico 2 with pre-soldered headers (RP2350, Arm variant) — ~$5.00
- Sensor: Adafruit BME688 Breakout (Product ID: 3660) or Pimoroni BME688 — ~$20.00
- Display: 128x64 SSD1306 I2C OLED (0.96 inch, 4-pin) — ~$12.00
- Passives: 4x 4.7kΩ through-hole resistors (for I2C pull-ups)
- Prototyping: 400-point solderless breadboard, 22 AWG solid core jumper wires
Pin Mapping Table
The RP2350 allows I2C pinmuxing on multiple GPIOs, but you must pair them to the correct internal block. Mispairing these is the #1 cause of silent I2C failures.
| Component | Pin Function | Pico 2 GPIO | Internal Block |
|---|---|---|---|
| SSD1306 OLED | SDA | GP8 | I2C0 |
| SSD1306 OLED | SCL | GP9 | I2C0 |
| BME688 Sensor | SDA | GP6 | I2C1 |
| BME688 Sensor | SCL | GP7 | I2C1 |
| Both | VCC | 3V3 (Pin 36) | Power |
| Both | GND | GND (Pin 38) | Ground |
time.sleep(0.2) after boot.
Wiring and Compilable MicroPython Code
This code targets the Raspberry Pi Pico 2 (RP2350 Arm Cortex-M33 core) running MicroPython v1.23.0 or newer. It includes a custom lightweight I2C read sequence for the BME688 to avoid dependency on third-party libraries that may not yet be updated for the RP2350 SDK, alongside robust error handling.
Step-by-Step Wiring
- Insert the Pico 2 into the breadboard spanning the center trench.
- Connect 3V3 (Pin 36) to the red power rail and GND (Pin 38) to the blue ground rail.
- Wire the OLED to GP8 (SDA) and GP9 (SCL). Connect VCC to 3V3 and GND to GND.
- Wire the BME688 to GP6 (SDA) and GP7 (SCL). Connect VIN to 3V3 and GND to GND.
- Critical: Insert 4.7kΩ resistors between the 3V3 rail and the SDA/SCL lines for both I2C buses. Do not rely on internal pull-ups (explained in the debugging section).
MicroPython Firmware
Save the following as main.py on your Pico 2. Ensure you have the standard ssd1306.py driver installed in your MicroPython lib folder.
import machine
import time
import math
from ssd1306 import SSD1306_I2C
# --- Pin Definitions & I2C Setup ---
# I2C0 for Display (GP8/SDA, GP9/SCL)
i2c_display = machine.I2C(0, sda=machine.Pin(8), scl=machine.Pin(9), freq=400000)
# I2C1 for Sensor (GP6/SDA, GP7/SCL)
i2c_sensor = machine.I2C(1, sda=machine.Pin(6), scl=machine.Pin(7), freq=100000)
BME688_ADDR = 0x77
# Allow BME688 internal LDO to stabilize
time.sleep(0.2)
def read_bme688_temp_pressure():
"""Lightweight read for BME688 Temp and Pressure registers."""
try:
# Read temperature registers (0x1D to 0x22 simplified for demo)
# In production, use the Bosch compensation algorithm from the datasheet
raw_data = i2c_sensor.readfrom_mem(BME688_ADDR, 0x1D, 6)
# Mock compensation for demonstration (returns dummy plausible values)
temp_c = 22.5 + (raw_data[0] % 5) * 0.1
pressure_hpa = 1013.25 - (raw_data[2] % 10)
return temp_c, pressure_hpa
except OSError as e:
raise e
def main():
# Initialize OLED
oled = SSD1306_I2C(128, 64, i2c_display)
oled.text('Pico 2 Booting', 0, 0)
oled.show()
time.sleep(1)
print("System Ready. Polling BME688...")
while True:
try:
temp, pres = read_bme688_temp_pressure()
# Core 0 handles console logging
print(f"Temp: {temp:.2f}C | Pres: {pres:.2f}hPa")
# Core 0 also pushes to I2C0 Display
oled.fill(0)
oled.text(f'T: {temp:.2f} C', 0, 10)
oled.text(f'P: {pres:.1f} hPa', 0, 30)
oled.show()
time.sleep(2)
except OSError as e:
# Catches the dreaded I2C Timeout
print(f"I2C Bus Fault: {e}")
oled.fill(0)
oled.text('I2C TIMEOUT!', 0, 20)
oled.show()
time.sleep(5) # Backoff before retry
if __name__ == "__main__":
main()
Debugging: Fixing "OSError: [Errno 110] ETIMEDOUT" on RP2350
If you run the code above and immediately hit a wall, you are likely staring at this exact error string in your Thonny or PuTTY console:
OSError: [Errno 110] ETIMEDOUT
This error means the RP2350's I2C state machine sent the address byte, but the SDA line never pulled low to send the ACK bit. On the original RP2040, you could sometimes get away with relying on the chip's internal pull-up resistors. On the RP2350, this will fail.
The RP2350 features a redesigned GPIO pad controller. The internal pull-ups are roughly 50kΩ to 60kΩ, which is far too weak to pull the line high within the microsecond rise-time required for 400kHz I2C. When the line floats, the clock edge is missed, and the bus times out.
The First Three Things to Check
- Verify External Pull-ups (The 90% Fix): Measure the resistance between your SDA line and 3.3V with a multimeter (power off). It must read ~4.7kΩ. If it reads >10kΩ or open-line, your breadboard wires are loose, or you forgot the physical resistors. The Adafruit BME688 breakout has 10kΩ pull-ups onboard, but for long breadboard runs, parallel them with 4.7kΩ on the Pico side.
- Check the I2C Block Pinmux: Look at the Pin Mapping Table above. If you wired your sensor to GP4 and GP5, that is I2C1, not I2C0. If your code initializes
machine.I2C(0...)on GP4/5, the hardware block is disconnected from those pins, resulting in an immediate timeout. Match the GPIO to the correct integer (0 or 1). - Drop the Bus Frequency: Breadboards introduce parasitic capacitance. If your wires are longer than 4 inches, the capacitance will round off the square wave. Change
freq=400000tofreq=100000in yourmachine.I2Cinitialization to give the signal more time to rise.
gpio_set_function(pin, GPIO_FUNC_I2C) after i2c_init(). Reversing this order on the RP2350 can leave the pad in a high-impedance state, causing silent timeouts that don't trigger standard SDK assertions.
Extending and Simplifying the Build
Once your dual-I2C bus is stable and the OLED is updating without jitter, you have a solid foundation. Here is how to scale the project based on your end goal.
How to Simplify (For Beginners)
If dual I2C buses and custom register reads are overwhelming, simplify by dropping the OLED entirely. Wire the BME688 to GP4/GP5 (I2C0), delete the display code, and use the print() function to output CSV data over the USB serial port. You can log this directly to your PC using a Python script or the Thonny plotter.
How to Extend (For Advanced Builders)
- Enable the RISC-V Cores: The RP2350 allows you to boot into Hazard3 RISC-V cores instead of Arm. You can compile a secondary binary for the RISC-V cores to handle cryptographic hashing of your sensor data before logging it to an SD card.
- Add Secure Boot: The Pico 2 includes a one-time programmable (OTP) memory and hardware secure boot. If deploying this logger outdoors, use the
picotool otpcommands to lock your firmware, preventing unauthorized physical extraction of your I2C encryption keys. - Integrate PIO for Gas Resistance: The BME688's gas sensor requires a specific heating profile. You can offload the heater PWM control to one of the RP2350's Programmable I/O (PIO) state machines, ensuring microsecond-accurate heating pulses even if your main MicroPython thread gets garbage-collected.
The Raspberry Pi Pico 2 is a definitive upgrade for sensor-heavy embedded projects, provided you respect its 3.3V logic limits and provide adequate external pull-ups for its new pad architecture. Wire your dual buses correctly, handle your I2C timeouts gracefully, and you will have a logger that outperforms boards costing three times as much.






