zephyr settings part 1: persistence of single key-value (no handler)
July 26, 2022•581 words
(code taken from the settings sample https://github.com/nrfconnect/sdk-zephyr/tree/main/samples/subsys/settings )
At first I used settings (key-values) with littlefs but testing showed that files got corrupted when the board was suddenly powered off. That didn't happen with NVS so for me NVS is more failsafe then littlefs.
It's a lot of plumbery...
proj.conf:
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y
CONFIG_SETTINGS=y
CONFIG_SETTINGS_RUNTIME=y
CONFIG_SETTINGS_LOG_LEVEL_INF=y
CONFIG_NVS=y
CONFIG_NVS_LOG_LEVEL_INF=y
CONFIG_LOG=y
CONFIG_LOG_MODE_MINIMAL=y
defines, includes:
#include "settings/settings.h"
#define FAIL_MSG "fail (err %d)\n"
#define GAMMA_DEFAULT_VAl 0
LOAD, SAVE , DELETE KEY-VALUE WITHOUT HANDLER (kindoff)
struct direct_immediate_value
: an intermediate structure variable for loading/saving , the variable "dest" will point to your app setting (the persistent key-value you want to use in your app):
//necessary structure for the whole thing to work, you won't use it directly
struct direct_immediate_value {
size_t len;
void *dest;
uint8_t fetched;
};
//necessary function for the whole thing to work, you won't use it directly
static int direct_loader_immediate_value(const char *name, size_t len,
settings_read_cb read_cb, void *cb_arg,
void *param)
{
const char *next;
size_t name_len;
int rc;
struct direct_immediate_value *one_value =
(struct direct_immediate_value *)param;
name_len = settings_name_next(name, &next);
if (name_len == 0) {
if (len == one_value->len) {
rc = read_cb(cb_arg, one_value->dest, len);
if (rc >= 0) {
one_value->fetched = 1;
printk("immediate load: OK.\n");
return 0;
}
printk(FAIL_MSG, rc);
return rc;
}
return -EINVAL;
}
/* other keys aren't served by the calback
* Return success in order to skip them
* and keep storage processing.
*/
return 0;
}
//load_immediate_value is the function you will call in your code to load a key "name" with value "dest"
int load_immediate_value(const char *name, void *dest, size_t len)
{
int rc;
struct direct_immediate_value dov;
dov.fetched = 0;
dov.len = len;
dov.dest = dest;
rc = settings_load_subtree_direct(name, direct_loader_immediate_value,
(void *)&dov);
if (rc == 0) {
if (!dov.fetched) {
rc = -ENOENT;
}
}
return rc;
}
//example code that shows the use of load_immediate_value, settings_save_one (for saving)
//and settings_delete (for deleting)
static void example_without_handler(void)
{
uint8_t val_u8;
int rc;
printk("Service a key-value pair without dedicated handlers\n\n");
//try to load it
//not found in NVS so it get the default value GAMMA_DEFAULT_VAl
printk("load <gamma> key directly: ");
rc = load_immediate_value("gamma", &val_u8, sizeof(val_u8));
if (rc == -ENOENT) {
val_u8 = GAMMA_DEFAULT_VAl;
printk("<gamma> = %d (default)\n", val_u8);
} else if (rc == 0) {
printk("<gamma> = %d\n", val_u8);
} else {
printk("unexpected"FAIL_MSG, rc);
}
//we increase the initial value
val_u8++;
//we save it
printk("save <gamma> key directly: ");
rc = settings_save_one("gamma", (const void *)&val_u8,
sizeof(val_u8));
if (rc) {
printk(FAIL_MSG, rc);
} else {
printk("OK.\n");
}
uint8_t val_u8_2;
//we load it again in another variable val_u8_2
// this time it was found in NVS with value GAMMA_DEFAULT_VAl+1
printk("load <gamma> key directly again: ");
rc = load_immediate_value("gamma", &val_u8_2, sizeof(val_u8_2));
if (rc == -ENOENT) {
val_u8_2 = GAMMA_DEFAULT_VAl;
printk("<gamma> = %d (default)\n", val_u8_2);
} else if (rc == 0) {
printk("<gamma> = %d\n", val_u8_2);
} else {
printk("unexpected"FAIL_MSG, rc);
}
//we delete it
printk("delete <gamma>: ");
rc = settings_delete("gamma");
if (rc) {
printk(FAIL_MSG, rc);
} else {
printk("OK.\n");
}
uint8_t val_u8_3;
//try to load it again
//not found in NVS so it get the default value GAMMA_DEFAULT_VAl
rc = load_immediate_value("gamma", &val_u8_3, sizeof(val_u8_3));
if (rc == -ENOENT) {
val_u8_3 = GAMMA_DEFAULT_VAl;
printk("<gamma> = %d (default)\n", val_u8_3);
} else if (rc == 0) {
printk("<gamma> = %d\n", val_u8_3);
} else {
printk("unexpected"FAIL_MSG, rc);
}
}
void main(void)
{
int rc = settings_subsys_init();
if (rc) {
printk("settings subsys initialization: fail (err %d)\n", rc);
return;
}
example_without_handler();
}