This guide covers dropping config_lib into an existing Raspberry Pi Pico
(C/C++ SDK) project so it can be configured over USB serial without reflashing.
Copy the config_lib/ folder into your project root:
your_project/
config_lib/
line_reader.h / .cpp
dispatcher.h / .cpp
registry.h / .cpp
flash_store.h / .cpp
config_service.h / .cpp
your_existing_files...
CMakeLists.txt
Add a library target for config_lib and link it into your executable.
add_library(config_lib
config_lib/line_reader.cpp
config_lib/dispatcher.cpp
config_lib/registry.cpp
config_lib/flash_store.cpp
config_lib/config_service.cpp
)
target_include_directories(config_lib PUBLIC
${CMAKE_CURRENT_LIST_DIR}/config_lib
)
target_link_libraries(config_lib PUBLIC
pico_stdlib
pico_stdio_usb
hardware_flash
hardware_sync
)
# ... your existing add_executable(...) block ...
target_link_libraries(your_target
pico_stdlib
config_lib
# ...your other libraries...
)Also make sure USB stdio is enabled and UART stdio is disabled for your target, since the protocol communicates over USB CDC serial:
pico_enable_stdio_uart(your_target 0)
pico_enable_stdio_usb(your_target 1)#include "config_service.h"
ConfigService config;
int main() {
// Register settings BEFORE calling begin() - this order matters,
// since begin() loads saved values from flash and needs the
// settings already registered (same order they were saved) to
// match them up correctly.
config.registerInt("retry_count", 3);
config.registerString("wifi_ssid", "MyNetwork");
config.begin(); // sets up USB serial, loads saved settings from flash
while (true) {
config.poll(); // non-blocking - processes one waiting command if present
// ...your existing application loop code...
if (config.getInt("retry_count") > 5) {
// react to current setting value
}
}
}| Method | Description |
|---|---|
registerInt(name, default) |
Register an integer setting. Call before begin(). |
registerString(name, default) |
Register a string setting. Call before begin(). |
begin() |
One-time setup: initializes USB serial (if not already up), loads saved settings from flash if present, otherwise uses registered defaults. |
poll() |
Call every loop iteration. Non-blocking — checks for one waiting byte, processes a command if a full line has arrived, returns immediately either way. |
getInt(name) |
Read current value of an int setting. Returns 0 if name not found. |
getString(name) |
Read current value of a string setting. Returns "" if name not found. |
Settings are changed by the user at runtime via the serial protocol
(SET, then SAVE to persist) — your application code only ever reads
current values with getInt/getString; it doesn't need to handle the
serial protocol directly.
- Only
intandstringsetting types are supported. - Settings persist to the last flash sector (4KB) — don't use that region for anything else in your own flash layout.
- Saved data is loaded by registration order, not by name — if you
change the order or types of
registerInt/registerStringcalls between firmware versions, previously saved settings will fail to load and defaults will be used instead (safe fallback, but data is lost). flash_range_programcurrently writes a single 256-byte page; total serialized settings data must stay under that until multi-page writes are added.- One
ConfigServiceinstance is assumed per device (uses USB serial as a de facto singleton resource).
See protocol/README.txt for the full serial command/response format.