# RPi Pico WAV Player UI Control Mechanisms: Push Buttons and Headphone Remote Implementation

> Explore RPi Pico WAV Player UI controls: implement 3 push buttons and headphone remote for seamless audio playback. Learn about ADC sampling and debouncing.

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

---

**The RPi Pico WAV Player uses three physical push buttons (Plus, Minus) and a headphone remote control (Center, Plus, Minus) sampled via ADC, processed through a 20 Hz debouncing timer, and dispatched as logical events through a single-element queue.**

The `elehobica/rpi_pico_wav_player` repository implements a dual-input user interface combining GPIO-sampled mechanical switches and an analog headphone remote. Understanding these **RPi Pico WAV Player UI control mechanisms** requires examining the hardware abstraction layer in [`src/ui_control.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/ui_control.cpp) and the event dispatch system that translates physical inputs into logical actions.

## Hardware Interface and Pin Mapping

### Push Button GPIO Configuration

The board provides two dedicated mechanical push buttons mapped to specific GPIO pins with internal pull-up resistors. In [`src/ui_control.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/ui_control.cpp), the **Plus** button connects to **GPIO 22** (`PIN_SW_PLUS`), while the **Minus** button connects to **GPIO 20** (`PIN_SW_MINUS`). Both pins configure as inputs with pull-ups, making them active-low when pressed【2†L22-L34】.

### Headphone Remote ADC Sampling

The headphone remote control utilizes a voltage-divider circuit connected to **GPIO 26** (`PIN_HP_BUTTON`), which maps to **ADC0** (`ADC_PIN_HP_BUTTON`). The remote's inline control buttons create distinct voltage levels across the microphone bias resistor. The firmware samples this analog input and converts raw ADC readings to voltage using calibration coefficients, then maps specific voltage windows to logical button states: Center, D, Plus, and Minus【2†L65-L86】.

## Software Architecture and Event Processing

### Button State Enumerations

The header file [`src/ui_control.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/ui_control.h) defines three critical enumerations that abstract the hardware. The `button_status_t` enum represents raw hardware states: `Open`, `Center`, `D`, `Plus`, and `Minus`. The `button_unit_t` enum distinguishes between `PushButtons` (physical GPIO switches) and `HpButtons` (headphone remote). Finally, `button_action_t` encodes logical user gestures including single clicks, double clicks, triple clicks, long presses (`CenterLong`), and extended long presses (`CenterLongLong`)【1†L11-L27】.

### Debouncing and Timing Mechanism

The UI layer executes a repeating hardware timer at **20 Hz** (`TIMER_UI_BUTTON_HZ`), triggering every 50 milliseconds via `update_button_action()`. This routine maintains a **30-sample history buffer** (`button_prv[NUM_BTN_HISTORY]`) to filter contact bounce. The system defines long-press thresholds at **10 counts (500 ms)** for `LONG_PUSH_COUNT` and **30 counts (1.5 seconds)** for `LONG_LONG_PUSH_COUNT`, comparing these against a running repeat counter (`button_repeat_count`) to distinguish short clicks from sustained presses【2†L27-L30】【2†L102-L104】.

### Event Queue Implementation

When `update_button_action()` recognizes a valid gesture, it invokes `trigger_event()` to package the `button_action_t` and `button_unit_t` into an `element_t` structure. This element pushes onto a **single-element queue** (`btn_evt_queue` with `QueueLength = 1`) implemented as a FreeRTOS queue. The queue intentionally holds only one event to prevent backlog; the UI mode logic processes events synchronously each tick and discards overflow. The public API `ui_get_btn_evt(button_action_t& btn_act, button_unit_t& btn_unit)` retrieves and clears this event, returning `true` when an action is available【2†L14-L23】【2†L36-L46】.

## Practical Implementation Examples

Detecting a single Center-press to toggle playback:

```cpp
// Inside a UIMode::update() implementation
button_action_t act;
button_unit_t  unit;
if (ui_get_btn_evt(act, unit)) {
    if (act == button_action_t::CenterSingle) {
        // Toggle play/pause
        player.toggle();
    }
}

```

Handling headphone remote volume control with source verification:

```cpp
button_action_t act;
button_unit_t  unit;
if (ui_get_btn_evt(act, unit) && unit == button_unit_t::HpButtons) {
    switch (act) {
        case button_action_t::PlusSingle:   audio.set_volume(+1); break;
        case button_action_t::MinusSingle:  audio.set_volume(-1); break;
        case button_action_t::PlusLong:     audio.set_volume(+5); break;
        case button_action_t::MinusLong:    audio.set_volume(-5); break;
        default: break;
    }
}

```

Configuring the Center button for sleep wake-up:

```cpp
// Before entering sleep mode
ui_set_center_switch_for_wakeup(true);
pm_enter_sleep();

```

This reconfigures the remote pin as a plain GPIO input to allow the Center button to trigger a wake interrupt.

## Summary

- The **RPi Pico WAV Player** employs a dual-input UI system combining GPIO push buttons and an ADC-sampled headphone remote, both managed in [`src/ui_control.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/ui_control.cpp).
- **Hardware mapping** assigns Plus/Minus push buttons to GPIO 22 and 20, while the headphone remote uses GPIO 26 (ADC0) with voltage-divider detection.
- **Event processing** runs at 20 Hz with 50 ms debouncing, utilizing a 30-sample history buffer and long-press thresholds (500 ms and 1.5 seconds).
- **Logical actions** abstract raw hardware into semantic gestures (single, double, triple clicks; long presses) sourced from either `PushButtons` or `HpButtons` units.
- **API consumption** occurs through `ui_get_btn_evt()`, retrieving events from a single-element queue populated by the timer-driven `update_button_action()` routine.

## Frequently Asked Questions

### What GPIO pins are used for the push buttons and headphone remote in the RPi Pico WAV Player?

The **Plus** push button connects to **GPIO 22** (`PIN_SW_PLUS`), and the **Minus** push button connects to **GPIO 20** (`PIN_SW_MINUS`). The headphone remote uses **GPIO 26** (`PIN_HP_BUTTON`), which maps to **ADC0** for analog voltage sampling. All push button pins configure as inputs with internal pull-up resistors, making them active-low when pressed.

### How does the headphone remote control detection work via ADC?

The headphone remote implements a voltage-divider circuit across the microphone bias resistor. When remote buttons are pressed, they create distinct voltage levels on **GPIO 26**. The firmware samples **ADC0**, converts the raw reading to a voltage using calibration coefficients, and maps specific voltage windows to logical states: **Center**, **D**, **Plus**, and **Minus**. This occurs in `adc0_get_hp_button()` within [`src/ui_control.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/ui_control.cpp).

### What is the polling rate and debouncing strategy for button inputs?

The UI layer executes a repeating hardware timer at **20 Hz** (`TIMER_UI_BUTTON_HZ`), polling inputs every **50 milliseconds** via `update_button_action()`. The system maintains a **30-sample history buffer** (`button_prv[NUM_BTN_HISTORY]`) to filter contact bounce. Long-press detection uses threshold counters: **10 counts (500 ms)** for standard long presses and **30 counts (1.5 seconds)** for extended long presses.

### How can the Center button wake the device from sleep mode?

Before entering low-power sleep, call `ui_set_center_switch_for_wakeup(true)` to reconfigure the headphone remote pin (**GPIO 26**) as a plain GPIO input rather than an ADC channel. This allows the Center button press to trigger a hardware interrupt and wake the RP2040 from sleep. After waking, the system restores normal ADC operation for remote button detection. This functionality is implemented in `ui_set_center_switch_for_wakeup()` within [`src/ui_control.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/ui_control.cpp).