MycilaConfig
A simple, efficient configuration library for ESP32 (Arduino framework) built on top of Preferences (NVS). Provides defaults, caching, typed getters, validators, change/restore callbacks, backup/restore, and optional JSON export with password masking.
Features
- πΎ Persistent storage using ESP32 Preferences (NVS)
- π― Default values with transparent caching for fast access
- π’ Typed getters:
getString(),getBool(),getInt(),getLong(),getFloat() - π Change listener callback fired when values change
- π Restore callback fired after bulk configuration restore
- β Global and per-key validators to enforce value constraints before persisting
- πΏ Backup and restore from key=value text format or
std::map - π Optional JSON export with ArduinoJson integration (
toJson()) - π Password masking for keys ending with
_pwdin JSON output - ποΈ Smart restore order: non-
_enablekeys applied first, then_enablekeys last (useful for feature toggles) - π Key helpers:
isPasswordKey(),isEnableKey()detect special suffixes - π NVS constraint enforcement: asserts key names β€ 15 characters
Installation
PlatformIO
Add to your platformio.ini:
lib_deps =
mathieucarbou/MycilaConfig
Optional: JSON Support
To enable toJson() method with ArduinoJson:
build_flags =
-D MYCILA_JSON_SUPPORT
lib_deps =
mathieucarbou/MycilaConfig
bblanchon/ArduinoJson
Quick Start
#include <MycilaConfig.h>
Mycila::Config config;
void setup() {
Serial.begin(115200);
// Initialize the config system with NVS namespace
config.begin("CONFIG");
// Declare configuration keys with optional default values
// Key names must be β€ 15 characters
config.configure("debug_enable", MYCILA_CONFIG_VALUE_FALSE);
config.configure("wifi_ssid");
config.configure("wifi_pwd");
config.configure("port", "80");
// Listen to configuration changes
config.listen([](const char* key, const std::string& newValue) {
Serial.printf("Config changed: %s = %s\n", key, newValue.c_str());
});
// Listen to configuration restore events
config.listen([]() {
Serial.println("Configuration restored!");
});
// Set a global validator (optional)
config.setValidator([](const char* key, const std::string& value) {
// Example: limit all values to 64 characters
return value.size() <= 64;
});
// Set a per-key validator (optional)
config.setValidator("port", [](const char* key, const std::string& value) {
int port = std::stoi(value);
return port > 0 && port < 65536;
});
// Set configuration values
auto result = config.set("wifi_ssid", "MyNetwork");
if (result) {
Serial.println("WiFi SSID saved successfully");
} else {
// Check detailed result
switch ((Mycila::Config::Result)result) {
case Mycila::Config::Result::INVALID_VALUE:
Serial.println("Invalid value rejected by validator");
break;
case Mycila::Config::Result::UNKNOWN_KEY:
Serial.println("Key not configured");
break;
default:
break;
}
}
// Get configuration values
Serial.printf("SSID: %s\n", config.get("wifi_ssid"));
bool debug = config.getBool("debug_enable");
int port = config.getInt("port");
// Check if value is empty or equals something
if (config.isEmpty("wifi_pwd")) {
Serial.println("Password not set");
}
if (config.isEqual("debug_enable", MYCILA_CONFIG_VALUE_TRUE)) {
Serial.println("Debug mode enabled");
}
}
void loop() {}
API Reference
Class: Mycila::Config
Setup and Storage
-
void begin(const char* name = "CONFIG")
Initialize the configuration system with the specified NVS namespace. -
void configure(const char* key, std::string defaultValue = "")
Register a configuration key with an optional default value. Key must be β€ 15 characters. -
bool exists(const char* key) const
Check if a configuration key has been registered. -
void clear()
Clear all persisted settings and cache.
Reading Values
-
const char* get(const char* key) const
Get the value as a C-string (never returnsnullptr, returns""for unknown keys). -
const std::string& getString(const char* key) const
Get the value as astd::stringreference. bool getBool(const char* key) const
Parse value as boolean:- If
-D MYCILA_CONFIG_EXTENDED_BOOL_VALUE_PARSING=1(or not defined): (MYCILA_CONFIG_VALUE_TRUE,"true","1","on","yes") βtrue. This is the default behavior. - If
-D MYCILA_CONFIG_EXTENDED_BOOL_VALUE_PARSING=0: onlyMYCILA_CONFIG_VALUE_TRUEβtrue
- If
-
int getInt(const char* key) const
Parse value as integer usingstd::stoi(). -
long getLong(const char* key) const
Parse value as long usingstd::stol(). -
float getFloat(const char* key) const
Parse value as float usingstd::stof(). -
bool isEmpty(const char* key) const
Check if the value is an empty string. bool isEqual(const char* key, const std::string& value) constbool isEqual(const char* key, const char* value) const
Compare the stored value with the provided value.
Writing Values
-
SetResult set(const char* key, const std::string& value, bool fireChangeCallback = true)
Set a configuration value. Returns aSetResultthat converts tobool(true = persisted successfully). -
bool set(const std::map<const char*, std::string>& settings, bool fireChangeCallback = true)
Set multiple values at once. Returns true if any value was changed. -
bool setBool(const char* key, bool value)
Set a boolean value (stored asMYCILA_CONFIG_VALUE_TRUEorMYCILA_CONFIG_VALUE_FALSE). Eg.,MYCILA_CONFIG_VALUE_TRUE= βtrueβ,MYCILA_CONFIG_VALUE_FALSE= βfalseβ (by default). -
bool unset(const char* key, bool fireChangeCallback = true)
Remove the persisted value (revert to default).
SetResult and Result Enum
set() returns a SetResult object that:
- Converts to
bool(true only if value was persisted) - Can be cast to
Mycila::Config::Resultenum for detailed status
Result codes:
PERSISTEDβ Value written to NVS successfullyALREADY_PERSISTEDβ Value already matches whatβs stored (no-op)SAME_AS_DEFAULTβ Value matches default and key not persisted (no-op)INVALID_VALUEβ Rejected by validatorUNKNOWN_KEYβ Key not registered viaconfigure()FAIL_ON_WRITEβ NVS write failed
Example:
auto res = config.set("key", "value");
if (!res) {
switch ((Mycila::Config::Result)res) {
case Mycila::Config::Result::INVALID_VALUE:
Serial.println("Value rejected by validator");
break;
case Mycila::Config::Result::UNKNOWN_KEY:
Serial.println("Key not configured");
break;
default:
break;
}
}
// You can also return Result directly from functions returning SetResult:
// return Mycila::Config::Result::UNKNOWN_KEY;
Callbacks and Validators
-
void listen(ConfigChangeCallback callback)
Register a callback invoked when any value changes:void callback(const char* key, const std::string& newValue) -
void listen(ConfigRestoredCallback callback)
Register a callback invoked after a bulk restore:void callback() -
bool setValidator(ConfigValidatorCallback callback)
Set a global validator called for all keys. Passnullptrto remove.
Signature:bool validator(const char* key, const std::string& newValue) -
bool setValidator(const char* key, ConfigValidatorCallback callback)
Set a per-key validator. Passnullptrto remove.
Backup and Restore
-
void backup(Print& out)
Write all configuration askey=value\nlines to anyPrintdestination (Serial, StreamString, etc.). -
bool restore(const char* data)
Parse and restore configuration fromkey=value\nformatted text. -
bool restore(const std::map<const char*, std::string>& settings)
Restore configuration from a map. Returns true if any value changed.
Restore order: Non-_enable keys are applied first, then _enable keys, ensuring feature toggles are activated after their dependencies.
Utilities
-
const std::vector<const char*>& keys() const
Get a sorted list of all registered configuration keys. -
const char* keyRef(const char* buffer) const
Given a string buffer, return the canonical key pointer if it matches a registered key (useful for pointer comparisons). -
bool isPasswordKey(const char* key) const
Returns true if key ends with_pwd. -
bool isEnableKey(const char* key) const
Returns true if key ends with_enable. -
void toJson(const JsonObject& root)(requiresMYCILA_JSON_SUPPORT)
Export all configuration to an ArduinoJson object. Password keys are masked.
JSON Export and Password Masking
When MYCILA_JSON_SUPPORT is defined, you can export configuration to JSON:
#include <ArduinoJson.h>
JsonDocument doc;
config.toJson(doc.to<JsonObject>());
serializeJson(doc, Serial);
Keys ending with _pwd are automatically masked in the JSON output (unless you define MYCILA_CONFIG_SHOW_PASSWORD).
Customize the mask:
#define MYCILA_CONFIG_PASSWORD_MASK "****" // default is "********"
Backup and Restore Example
// Backup to StreamString
StreamString backup;
backup.reserve(1024);
config.backup(backup);
Serial.println(backup);
// Restore from text
const char* savedConfig =
"wifi_ssid=MyNetwork\n"
"wifi_pwd=secret123\n"
"debug_enable=true\n";
config.restore(savedConfig);
// Restore from map
std::map<const char*, std::string> settings = {
{"wifi_ssid", "NewNetwork"},
{"port", "8080"}
};
config.restore(settings);
Key Naming Conventions
- Maximum length: 15 characters (NVS limitation enforced by assert)
- Password keys: End with
_pwdβ masked in JSON export - Enable keys: End with
_enableβ applied last during bulk restore
Examples
Basic Configuration
See examples/Config/Config.ino for a complete example demonstrating:
- Setting up keys and defaults
- Using validators
- Setting and getting values
- Checking SetResult codes
- Backup and restore
JSON Export
See examples/ConfigJson/ConfigJson.ino for:
- JSON export with
toJson() - Password masking
- Integration with ArduinoJson
License
MIT License - see LICENSE file for details.