# ConfigParam.h in rpi_pico_wav_player: Managing Persistent Player Settings

> Learn how ConfigParam.h in the rpi_pico_wav_player manages persistent settings using a type-safe singleton, simplifying flash storage operations for user-adjustable parameters.

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

---

**[`ConfigParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/ConfigParam.h) serves as the central configuration manager for the Raspberry Pi Pico WAV player, providing a type-safe singleton interface that abstracts flash storage operations for all user-adjustable settings.**

In the `elehobica/rpi_pico_wav_player` firmware, [`src/ConfigParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/ConfigParam.h) defines the backbone of persistent storage. It transforms raw flash memory operations into intuitive C++ object interactions, allowing the player to remember volume levels, UI modes, and playback positions across power cycles without manual byte-level flash management.

## Core Architecture of ConfigParam.h

The header implements a **singleton pattern** that wraps the generic [`FlashParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/FlashParam.h) infrastructure. This design ensures that all firmware modules access a single, consistent configuration state while the underlying flash read/write logic remains completely hidden.

### Parameter Enumeration with ParamId_t

At the heart of the system lies the `ParamId_t` enum, which assigns every configurable setting a unique identifier. These IDs start from `FlashParamNs::CFG_ID_BASE` and cover all user-facing options:

```cpp
typedef enum {
    CFG_ID_VERSION = FlashParamNs::CFG_ID_BASE,
    CFG_ID_VOLUME,
    CFG_ID_UIMODE,
    CFG_ID_MENU_IDX_GENERAL_PLAY_POS,
    // ... additional parameters
    CFG_ID_TAIL
} ParamId_t;

```

This enumeration in [`src/ConfigParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/ConfigParam.h) (lines 13-46) ensures that each parameter has a stable address in the flash storage layout, preventing corruption when firmware updates add new settings.

### Typed Parameter Wrappers

Rather than exposing raw memory addresses, [`ConfigParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/ConfigParam.h) encapsulates each setting in a `FlashParamNs::Parameter<T>` template object. These objects store the parameter ID, a human-readable name, and a default value:

```cpp
FlashParamNs::Parameter<std::string> P_CFG_VERSION {CFG_ID_VERSION, "CFG_VERSION", "0.0.0"};
FlashParamNs::Parameter<uint8_t>     P_CFG_VOLUME  {CFG_ID_VOLUME, "CFG_VOLUME", 65};
FlashParamNs::Parameter<uint32_t>    P_CFG_UIMODE   {CFG_ID_UIMODE, "CFG_UIMODE", 0};

```

This pattern, defined in lines 57-90 of [`src/ConfigParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/ConfigParam.h), provides compile-time type safety. Attempting to assign a string to a numeric parameter triggers a compiler error rather than a runtime flash corruption.

### Flash Persistence Management

The class inherits from `FlashParamNs::FlashParam`, which implements the actual flash storage algorithms. [`ConfigParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/ConfigParam.h) exposes two critical lifecycle methods that the firmware calls during state transitions:

- **`initialize()`**: Loads all persisted values from RP2040 flash memory into the parameter objects
- **`finalize()`**: Writes modified values back to flash before power-down

These methods (lines 91-95) ensure atomic updates—either all parameters persist successfully, or none do, preventing partial configuration corruption.

### Singleton Global Access

To prevent multiple instances from desynchronizing the flash state, [`ConfigParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/ConfigParam.h) implements a singleton pattern:

```cpp
static ConfigParam& instance() {
    static ConfigParam instance;
    return instance;
}

```

Accessed via `ConfigParam::instance()`, this global entry point (lines 51-55) allows any UI mode or audio handler to read or modify settings without passing configuration pointers through the entire call stack.

## Practical Usage Examples

The firmware demonstrates three primary interaction patterns with [`ConfigParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/ConfigParam.h): initialization, modification, and runtime querying.

### Loading Configuration at Startup

When the player boots, `UIInitialMode::loadFromFlash()` retrieves the singleton instance and initializes the flash system:

```cpp
ConfigParam& cfg = ConfigParam::instance();
cfg.initialize();  // Reads all persisted values from flash

printf("Raspberry Pi Pico Player: %s\r\n", 
       cfg.P_CFG_VERSION.get().c_str());

```

This pattern, found in [`src/UIMode.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/UIMode.cpp) (lines 33-38), ensures that volume, UI mode, and playback position reflect the user's last session rather than factory defaults.

### Saving Settings Before Power Off

When the user initiates shutdown, `UIPowerOffMode::storeToFlash()` persists the current state:

```cpp
ConfigParam& cfg = ConfigParam::instance();
cfg.P_CFG_VOLUME.set(75);  // Update volume to current level
cfg.P_CFG_UIMODE.set(static_cast<uint32_t>(uiMode));  // Remember UI state
cfg.finalize();  // Atomic write to flash

```

This implementation in [`src/UIPowerOffMode.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/UIPowerOffMode.cpp) (lines 71-99) guarantees that user adjustments survive power cycles without requiring real-time flash writes during normal operation.

### Reading Menu Preferences at Runtime

During button handling, `UIFileViewMode::update()` queries configuration to determine navigation behavior:

```cpp
auto& layout = (btn_unit == button_unit_t::PushButtons)
    ? cfgParam.P_CFG_MENU_IDX_GENERAL_PUSH_BUTTON_LAYOUT
    : cfgParam.P_CFG_MENU_IDX_GENERAL_HP_BUTTON_LAYOUT;

if (layout.get() == static_cast<uint32_t>(button_layout_t::Horizontal)) {
    idxInc();  // Horizontal navigation
} else {
    idxDec();  // Vertical navigation
}

```

This logic in [`src/UIFileViewMode.cpp`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/UIFileViewMode.cpp) (lines 99-104) demonstrates how [`ConfigParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/ConfigParam.h) enables dynamic UI adaptation based on user-configured hardware layouts.

## Integration with the Firmware Ecosystem

[`ConfigParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/ConfigParam.h) operates as the central hub in a three-layer configuration system:

- **Storage Layer**: [`FlashParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/FlashParam.h) handles low-level flash sector management and wear leveling
- **Model Layer**: [`ConfigParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/ConfigParam.h) defines the specific parameters and their defaults
- **Interface Layer**: [`ConfigMenu.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/ConfigMenu.h) maps these parameters to the on-screen menu system that users interact with

This architecture ensures that adding a new setting requires only three steps: adding an entry to the `ParamId_t` enum in [`ConfigParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/ConfigParam.h), declaring the `Parameter<T>` object with default value, and registering it in [`ConfigMenu.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/ConfigMenu.h) for UI exposure.

## Summary

- **[`ConfigParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/ConfigParam.h) defines the singleton `ConfigParam` class**, the authoritative source for all user-adjustable settings in the RP2040 WAV player.
- **The `ParamId_t` enum** assigns stable flash storage IDs to each parameter, preventing data corruption during firmware updates.
- **Typed `Parameter<T>` wrappers** provide compile-time safety and encapsulate default values, eliminating raw memory manipulation.
- **`initialize()` and `finalize()` methods** abstract the flash persistence logic, ensuring atomic load/save operations across power cycles.
- **Global singleton access** via `ConfigParam::instance()` allows any firmware module to read or modify settings without complex dependency injection.

## Frequently Asked Questions

### How does ConfigParam.h differ from FlashParam.h?

[`FlashParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/FlashParam.h) provides the generic infrastructure for reading and writing raw bytes to the RP2040's flash memory, including sector management and wear-leveling algorithms. [`ConfigParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/ConfigParam.h) builds upon this foundation by defining the specific application-level parameters (volume, UI mode, etc.) and wrapping them in type-safe C++ objects. While [`FlashParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/FlashParam.h) knows about memory addresses, [`ConfigParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/ConfigParam.h) knows about user settings.

### What happens if flash storage becomes corrupted?

During `initialize()`, the `FlashParam` base class validates the flash sector checksums. If corruption is detected, the system automatically falls back to the default values defined in the `Parameter<T>` declarations within [`ConfigParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/ConfigParam.h). This ensures the player remains functional even after flash wear or unexpected power loss during a write operation, though user settings revert to factory defaults.

### Can I add new configuration parameters without modifying existing flash addresses?

Yes. The `ParamId_t` enum in [`ConfigParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/ConfigParam.h) uses `CFG_ID_TAIL` as a sentinel value, and new parameters should be inserted before this marker. Because each parameter stores its ID within its own flash slot and the `FlashParam` system scans by ID rather than fixed offset, adding new entries does not shift the storage location of existing parameters. This prevents corruption when upgrading firmware while preserving user settings.

### Where are the default values for settings defined?

Default values are hardcoded in the `Parameter<T>` object declarations within [`src/ConfigParam.h`](https://github.com/elehobica/rpi_pico_wav_player/blob/main/src/ConfigParam.h). For example, `P_CFG_VOLUME` initializes with a default of `65`, and `P_CFG_VERSION` defaults to `"0.0.0"`. These values are applied when the flash sector is empty or corrupted, and they serve as the baseline until the user modifies the setting through the menu system.