If you want to run a Raspberry Pi Pico on batteries for months, you cannot rely on the onboard power routing. The default USB-to-3.3V regulator path draws a baseline quiescent current that will drain a standard AA battery pack in weeks. To achieve true ultra-low pico power operation (sub-milliamp sleep currents), you must bypass the onboard RT6150 buck-boost converter and inject regulated 3.3V directly into the 3V3(OUT) pin.
This guide walks through building a deep-sleep environmental logger that drops sleep current from ~12mA down to roughly 150µA. We will use the standard RP2040 Pico (not the Pico W), an external ultra-low-Iq LDO, and a BME280 sensor.
The Pico Power Decision Matrix: Bypassing the Onboard Regulator
The Raspberry Pi Pico features an onboard RT6150 buck-boost converter. While convenient for USB power, it introduces unacceptable overhead for battery nodes. Furthermore, if you use the Pico W, the CYW43439 WiFi/BT chip adds a hard floor of ~2mA to your sleep current unless you actively de-initialize the radio stack. For pure low-power sensor logging, the standard Pico is the correct choice.
Use this decision path to select your power injection method. Read from top to bottom and stop at the first condition that matches your project requirements.
| Condition / Requirement | Power Routing Method | Expected Sleep Current |
|---|---|---|
| If you need WiFi/BLE telemetry | Use Pico W, feed 5V to VBUS |
~2.5mA (Radio idle) |
| If you need 5V USB bench power | Feed 5V to VBUS (Uses RT6150) |
~12mA - 20mA |
| If using raw 3.7V LiPo, size constrained | Feed LiPo to VSYS (Uses RT6150) |
~10mA |
| If targeting ultra-low power (<1mA) | External LDO to 3V3(OUT) |
~150µA |
3V3(OUT) pin. Do not feed power to VBUS or VSYS simultaneously.
Hardware Spec Sheet and Pin Mapping
Before soldering, verify your exact board variants. The Earle Philhower Arduino core handles the RP2040 sleep states beautifully, but hardware mismatches will cause brownouts during wake-up.
Parts List
- Microcontroller: Raspberry Pi Pico (Standard RP2040, 2MB Flash, no wireless) - ~$4.00
- Sensor: Adafruit BME280 I2C Breakout (Product ID 2652) - ~$19.95 (Includes onboard 3.3V regulator and I2C pull-ups)
- Voltage Regulator: Microchip MCP1700-3302E/TO (3.3V, 250mA, TO-92 package) - ~$0.80
- Power Source: 3x AA Battery Holder with JST connector (Pololu 1159) paired with Eneloop Pro NiMH cells (4.5V nominal, 3.6V depleted)
- Capacitors: 2x 1µF ceramic (0805 or through-hole) for LDO input/output stability
Pin Mapping Table
| Pico Pin (RP2040) | Function | Connects To |
|---|---|---|
3V3(OUT) (Pin 36) |
Regulated 3.3V Input | MCP1700 VOUT |
GND (Pin 38) |
Common Ground | MCP1700 GND & BME280 GND |
GP4 (Pin 6) |
I2C0 SDA | BME280 SDI |
GP5 (Pin 7) |
I2C0 SCL | BME280 SCK |
GP15 (Pin 20) |
Wake Interrupt | Pushbutton to GND (Internal Pull-up) |
Wiring the Ultra-Low-Power Node
Follow these steps precisely. Mixing up VSYS and 3V3(OUT) while batteries are connected can backfeed the RT6150 and permanently damage the Pico.
- Prepare the LDO: Solder a 1µF capacitor between the MCP1700 VIN and GND pins, and another 1µF capacitor between VOUT and GND. This prevents high-frequency oscillation during the Pico's sudden current spikes when waking from sleep.
- Wire the Battery: Connect the 3x AA battery holder positive lead to the MCP1700 VIN. Connect the negative lead to the common ground rail.
- Inject Power: Run a wire from the MCP1700 VOUT directly to the Pico's
3V3(OUT)pin (Physical Pin 36). Leave VBUS (Pin 40) and VSYS (Pin 39) completely unconnected. - Wire I2C: Connect GP4 to BME280 SDA, and GP5 to BME280 SCL. Because we are using the Adafruit breakout, the 10kΩ pull-up resistors are already populated on the board. If using a raw SparkFun or generic module, you must add external 4.7kΩ pull-ups to 3.3V.
- Wire the Wake Button: Connect a momentary pushbutton between GP15 and GND. We will enable the internal pull-up in software.
Compilable Arduino Code with Error Handling
This code targets the Raspberry Pi Pico (Standard) using the Earle Philhower arduino-pico core. Do not use the official Arduino Mbed OS core; it lacks the low-level rp2040.sleep() hooks required for proper current reduction.
Install the Adafruit BME280 Library and Adafruit Unified Sensor via the Library Manager before compiling.
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <hardware/sleep.h>
// Pin definitions
#define I2C_SDA 4
#define I2C_SCL 5
#define WAKE_BUTTON 15
#define SEALEVELHPA (1013.25)
// Sleep duration in microseconds (10 seconds)
#define SLEEP_DURATION_US 10000000
Adafruit_BME280 bme;
volatile bool buttonPressed = false;
void wakeInterruptHandler() {
buttonPressed = true;
}
void setup() {
Serial.begin(115200);
// Configure wake button with internal pull-up
pinMode(WAKE_BUTTON, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(WAKE_BUTTON), wakeInterruptHandler, FALLING);
// Initialize I2C on specific pins
Wire.setSDA(I2C_SDA);
Wire.setSCL(I2C_SCL);
Wire.begin();
// Error handling for sensor initialization
if (!bme.begin(0x77, &Wire)) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
// Blink onboard LED to indicate fatal hardware fault
pinMode(LED_BUILTIN, OUTPUT);
while (1) {
digitalWrite(LED_BUILTIN, HIGH);
delay(100);
digitalWrite(LED_BUILTIN, LOW);
delay(100);
}
}
Serial.println("BME280 initialized. Starting loop.");
}
void loop() {
if (buttonPressed) {
Serial.println("Wake button pressed!");
buttonPressed = false;
}
// Read sensor data
float temp = bme.readTemperature();
float hum = bme.readHumidity();
float pres = bme.readPressure() / 100.0F;
Serial.printf("Temp: %.2f C | Hum: %.2f %% | Pres: %.2f hPa\n", temp, hum, pres);
// Shut down I2C and serial to save power during sleep
Wire.end();
Serial.end();
// Deep sleep using Earle Philhower core sleep function
// This powers down the CPU and most peripherals, dropping current to ~150uA
rp2040.sleep(SLEEP_DURATION_US);
// Re-initialize I2C and Serial upon waking
Serial.begin(115200);
Wire.setSDA(I2C_SDA);
Wire.setSCL(I2C_SCL);
Wire.begin();
// Small delay to let I2C bus stabilize after wake
delay(50);
}
Debugging: Resolving I2C Faults and Sensor Timeouts
When operating on the edge of brownout voltages with ultra-low-power LDOs, I2C communication is the first thing to fail. If your serial monitor outputs the exact error string: Could not find a valid BME280 sensor, check wiring!, do not immediately assume the sensor is dead.
Here are the first three things to check when this failure occurs, ranked by probability in low-power builds:
- LDO Dropout and I2C Pull-up Starvation (Most Likely): The MCP1700 has a dropout voltage of ~178mV at 250mA, but at low currents, it still requires headroom. If your 3x AA batteries drop below 3.8V, the LDO output might sag to 3.1V during the BME280's initialization spike. The I2C pull-ups on the breakout board won't reach the 3.3V logic high threshold required by the RP2040. Fix: Measure the
3V3(OUT)pin with a multimeter during the boot sequence. If it dips below 3.2V, switch to a fresh set of batteries or use a buck-boost like the Pololu S7V8F3. - I2C Address Mismatch: The Adafruit BME280 defaults to I2C address
0x77. Generic Amazon/eBay BME280 breakouts almost always use0x76. Fix: Run an I2C scanner sketch. If it finds the device at0x76, changebme.begin(0x77, &Wire)tobme.begin(0x76, &Wire)in the code above. - Missing Pull-up Resistors: If you swapped the Adafruit board for a bare SparkFun SEN-13676 or a generic module, it likely lacks onboard pull-ups. The RP2040's internal I2C pull-ups are weak (~50kΩ) and insufficient for reliable 400kHz operation. Fix: Solder 4.7kΩ resistors between SDA and 3.3V, and SCL and 3.3V.
pico-sdk: panic followed by a reboot loop, your code is likely failing to re-initialize the I2C bus fast enough after rp2040.sleep(). Always include a delay(50) after Wire.begin() in the wake sequence to allow the BME280's internal state machine to reset.
Extending and Simplifying the Build
Once you have the baseline 150µA sleep current verified with a bench multimeter (measuring across a 10Ω shunt resistor on the ground path), you can adapt the node for specific deployment scenarios.
How to Simplify (The Direct 2x AA Hack)
If you want to eliminate the MCP1700 LDO entirely to save board space and BOM cost, you can run 2x AA NiMH batteries (2.4V nominal) directly into the Pico's VSYS pin. The RP2040 datasheet specifies a minimum VSYS voltage of 1.8V. The internal core regulators will handle the step-down to 1.1V for the CPU. Trade-off: The BME280 requires a minimum of 1.71V to operate, so 2.4V is safe, but I2C logic levels will be 2.4V, which is safely within the RP2040's input tolerance. This removes the LDO quiescent current entirely, dropping sleep current closer to 100µA.
How to Extend (Solar Harvesting)
For permanent outdoor deployment, replace the 3x AA battery holder with an e-peas AEM10941 solar harvesting IC evaluation board. Wire a 5V/100mA mini solar panel to the AEM10941 input, and wire the AEM10941's regulated 3.3V SYS output directly to the Pico's 3V3(OUT) pin. The AEM10941 features a built-in LiPo charging path and cold-start capability down to 380mV, allowing the Pico to run indefinitely in shaded environments without the RT6150 overhead. For deeper technical specifications on the RP2040 power domains, consult the official Raspberry Pi Pico Datasheet.






