zephyr settings part 1: persistence of single key-value (no handler)

(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();
}




You'll only receive email when they publish something new.

More from Loïc Devaux
All posts