# RPi Pico WAV Player Power Management Features: Deep Sleep, Battery Monitoring, and Peripheral Control

> Explore RPi Pico WAV Player power management features: deep sleep, battery monitoring, USB detection, and peripheral control for extended battery life. Optimize your project's power usage.

- Repository: [Elehobica/rpi_pico_wav_player](https://github.com/elehobica/rpi_pico_wav_player)
- Tags: deep-dive
- Published: 2026-03-01

---

**The RPi Pico WAV Player firmware implements comprehensive power management including dynamic backlight control, dual-mode battery monitoring with low-voltage protection, USB power detection, audio DAC muting, and ultra-low-power dormant sleep with configurable wake-up sources.**

The `rpi_pico_wav_player` repository provides a dedicated power management subsystem centered in [`src/power_manage.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/power_manage.cpp) that handles everything from display dimming to deep sleep recovery. These power management features ensure efficient battery operation for portable audio playback on the Raspberry Pi Pico and Waveshare RP2040-LCD-0.96 boards.

## Hardware Initialization and Configuration

The power management subsystem initializes through `pm_init()` in [`src/power_manage.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/power_manage.cpp), which configures GPIO pins, detects hardware variants, and establishes the battery monitoring timer.

### GPIO Configuration and Power Control Pins

The initialization routine sets up critical power control pins including **GPIO 24** for USB power detection, **GPIO 19** for the power-keep MOSFET, **GPIO 27** for audio DAC mute control, and **GPIO 8** for the active battery check enable circuit. The code also configures **GPIO 21** for button pull-up control and sets the DCDC-PSM mode based on the board variant.

### Active vs. Static Battery Circuit Detection

The firmware automatically detects whether the board includes the optional active battery-check circuit by probing voltage levels on GPIO 28 (ADC2) with the enable pin (GPIO 8) toggled high and low. If the measured voltage behaves as expected, the global `_use_active_batt_check` flag is set to `true`, directing subsequent voltage readings to use the active circuit rather than the static divider on GPIO 29 (ADC3).

### DCDC Mode Selection and Timer Initialization

For the Raspberry Pi Pico, the RT6150B-33GQW regulator is configured to PWM mode to minimize audio ripple, while the Waveshare board's TPS63000 is set to power-save off mode. The initialization concludes by starting a repeating timer that calls `pm_monitor_battery_voltage()` at 20 Hz to maintain real-time battery status.

## Dynamic Backlight Brightness Management

The firmware implements intelligent display power management through `pm_backlight_update()`, which adjusts OLED backlight PWM levels based on user activity timeouts.

### Idle-Time-Based Dimming

The function checks `ui_get_idle_count()` against the configured `DISPLAY_TIME_TO_BACKLIGHT_LOW` threshold (converted to loop cycles based on the 50 ms UI update rate). When idle time exceeds the threshold, the backlight drops to `DISPLAY_BACKLIGHT_LOW_LEVEL`; otherwise, it maintains `DISPLAY_BACKLIGHT_HIGH_LEVEL`. The hardware PWM driver `OLED_BLK_Set_PWM()` applies the calculated value.

### UI Integration

Every UI redraw cycle, particularly in `UIMode::draw()` and `UIFileViewMode::draw()`, invokes `pm_backlight_update()` to ensure the display brightness remains synchronized with user interaction patterns, significantly reducing power consumption during passive listening sessions.

## Battery Voltage Monitoring and Protection

The power management subsystem provides comprehensive battery state monitoring through dual-mode ADC measurement and configurable low-voltage protection.

### Dual-Mode Voltage Measurement

The `_get_battery_voltage()` function in [`src/power_manage.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/power_manage.cpp) selects between active and static measurement modes based on the `_use_active_batt_check` flag. For static mode, it reads ADC3 (GPIO 29); for active mode, it enables the external circuit via GPIO 8 and reads ADC2 (GPIO 28). Raw ADC values are converted to real-world voltages using calibrated coefficients (`COEF_A_*` and `COEF_B_*`) that account for voltage divider ratios and diode drops.

### Periodic Monitoring and Low-Battery Detection

The `pm_monitor_battery_voltage()` timer callback executes at 20 Hz, periodically sampling the battery voltage and storing the result in the global `_battery_voltage` variable. The `pm_get_low_battery()` function compares this value against the `LOW_BATT_LVL` threshold (2.9 V), returning `true` when the battery requires immediate attention. This compile-time flag can be disabled via `NO_BATTERY_VOLTAGE_CHECK` for hardware configurations without battery monitoring.

## Peripheral Power Control

The firmware manages power states for external peripherals including the audio DAC, USB detection, and button interfaces through dedicated GPIO control functions.

### USB Power Detection

The `pm_usb_power_detected()` function reads GPIO 24 to determine if the board is connected to USB host power or a wall charger. This status drives UI decisions in `UIInitialMode::update()`, determining whether the device enters charging mode or normal playback mode upon startup.

### Audio DAC Mute Control

To prevent power-on pops and save energy, `pm_set_audio_dac_enable()` controls GPIO 27 to enable or mute the external I²S audio DAC. The UI registers this function as a callback via `audio_codec_set_dac_enable_func()` during `UIOpeningMode::entry()`, allowing playback code to toggle DAC power without hardware-specific knowledge.

### Power-Keep and Button Control

The `pm_set_power_keep()` function drives GPIO 19 to control a MOSFET that maintains board power during USB charging, even after the user releases the physical power button. Complementing this, `pm_enable_button_control()` toggles pull-ups on GPIO 21 to enable or disable button detection for wake-up purposes, conserving power when the device is in sleep states.

## Deep Sleep and Wake Management

The firmware implements ultra-low-power dormant mode for battery conservation during charging idle periods, with full system restoration upon wake.

### Entering Dormant Mode

The `pm_enter_dormant_and_wake()` function orchestrates the sleep sequence: it turns off the backlight, sets the DCDC converter to PFM mode for maximum efficiency, de-initializes USB CDC, and configures the wake-up source (typically the center button via `ui_set_center_switch_for_wakeup()`). The function waits for button release (>500 ms) to prevent immediate re-triggering, then calls `pm_enter_dormant_and_wake_core()` which disables interrupts, switches the clock source to XOSC, and enters dormant mode until the specified GPIO goes low.

### Clock Restoration and PLL Reconfiguration

Upon wake, the system must restore the 96 MHz PLL required for I²S audio. The `pw_set_pll_usb_96MHz()` function (lines 309-353 in [`power_manage.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/power_manage.cpp)) reinitializes the PLL, updates `clk_usb`, `clk_sys`, and `clk_peri`, then restores UART and USB CDC drivers. This ensures audio peripherals receive stable clocks after exiting dormant mode.

## System Reset and Reboot Handling

The firmware provides controlled reboot capabilities with cause detection to optimize startup behavior.

### Watchdog-Based Soft Reboot

The `pm_reboot()` function utilizes `watchdog_reboot()` to restart the Pico while preserving USB-CDC connection parameters via `PICO_STDIO_USB_RESET_RESET_TO_FLASH_DELAY_MS`. This allows the system to restart cleanly without physical power cycling.

### Reboot Cause Detection

The `pm_is_caused_reboot()` function queries the watchdog status flag to determine if the current boot resulted from a watchdog-triggered reset. The UI uses this in `UIMode::initialize()` and `UIInitialMode::update()` to skip the charging screen when recovering from a software-initiated reboot, streamlining the user experience.

## Summary

The RPi Pico WAV Player firmware delivers a **complete power management solution** optimized for portable battery operation:

- **Dynamic backlight control** reduces display power based on user idle time.
- **Dual-mode battery monitoring** supports both static and active voltage sensing with 2.9 V low-battery protection.
- **Peripheral power control** manages USB detection, audio DAC muting, power-keep MOSFETs, and button pull-ups.
- **Ultra-low-power dormant mode** enables sub-milliamp sleep currents with full system restoration including 96 MHz PLL reconfiguration.
- **Intelligent reboot handling** distinguishes between cold boots and watchdog resets to optimize startup flow.

## Frequently Asked Questions

### How does the firmware detect low battery conditions?

The firmware samples battery voltage at 20 Hz through `pm_monitor_battery_voltage()` and stores the result in a global variable. The function `pm_get_low_battery()` compares this value against the `LOW_BATT_LVL` threshold of **2.9 V**, returning `true` when the battery requires charging. This check can be disabled at compile time using the `NO_BATTERY_VOLTAGE_CHECK` flag for hardware configurations without battery monitoring circuitry.

### What is the difference between active and static battery checking?

**Static checking** uses the Raspberry Pi Pico's built-in voltage divider on **GPIO 29 (ADC3)** to measure battery voltage passively. **Active checking** utilizes an optional external circuit on **GPIO 28 (ADC2)** controlled by **GPIO 8**, which enables the voltage divider only during measurement to reduce quiescent current. During `pm_init()`, the firmware automatically probes both configurations to detect which hardware variant is present and sets the `_use_active_batt_check` flag accordingly.

### How does deep sleep (dormant mode) work in this firmware?

When the player remains idle on the charging screen for two seconds, `pm_enter_dormant_and_wake()` initiates a low-power sequence: it disables the backlight, sets the DCDC converter to PFM mode, de-initializes USB CDC, and configures the center button as a wake source. The RP2040 enters **dormant mode**, consuming minimal current until the button pulls the wake pin low. Upon wake, `pw_set_pll_usb_96MHz()` restores the 96 MHz PLL and peripheral clocks required for I²S audio playback.

### How does the firmware handle USB power detection?

The `pm_usb_power_detected()` function reads **GPIO 24** to determine if the board is connected to USB host or charger power. This status drives critical UI decisions in `UIInitialMode::update()`, determining whether the device enters charging mode or proceeds directly to playback mode. When USB power is detected, the firmware also engages the **power-keep circuit** (GPIO 19) to maintain board operation even if the physical power button is released, ensuring stable charging behavior.