|  | /* | 
|  | * NVS Sample for Zephyr using high level API, the sample illustrates the usage | 
|  | * of NVS for storing data of different kind (strings, binary blobs, unsigned | 
|  | * 32 bit integer) and also how to read them back from flash. The reading of | 
|  | * data is illustrated for both a basic read (latest added value) as well as | 
|  | * reading back the history of data (previously added values). Next to reading | 
|  | * and writing data it also shows how data can be deleted from flash. | 
|  | * | 
|  | * The sample stores the following items: | 
|  | * 1. A string representing an IP-address: stored at id=1, data="192.168.1.1" | 
|  | * 2. A binary blob representing a key: stored at id=2, data=FF FE FD FC FB FA | 
|  | *    F9 F8 | 
|  | * 3. A reboot counter (32bit): stored at id=3, data=reboot_counter | 
|  | * 4. A string: stored at id=4, data="DATA" (used to illustrate deletion of | 
|  | * items) | 
|  | * | 
|  | * At first boot the sample checks if the data is available in flash and adds | 
|  | * the items if they are not in flash. | 
|  | * | 
|  | * Every reboot increases the values of the reboot_counter and updates it in | 
|  | * flash. | 
|  | * | 
|  | * At the 10th reboot the string item with id=4 is deleted (or marked for | 
|  | * deletion). | 
|  | * | 
|  | * At the 11th reboot the string item with id=4 can no longer be read with the | 
|  | * basic nvs_read() function as it has been deleted. It is possible to read the | 
|  | * value with nvs_read_hist() | 
|  | * | 
|  | * At the 78th reboot the first sector is full and a new sector is taken into | 
|  | * use. The data with id=1, id=2 and id=3 is copied to the new sector. As a | 
|  | * result of this the history of the reboot_counter will be removed but the | 
|  | * latest values of address, key and reboot_counter is kept. | 
|  | * | 
|  | * Copyright (c) 2018 Laczen | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  |  | 
|  | #include <zephyr/kernel.h> | 
|  | #include <zephyr/sys/reboot.h> | 
|  | #include <zephyr/device.h> | 
|  | #include <string.h> | 
|  | #include <zephyr/drivers/flash.h> | 
|  | #include <zephyr/storage/flash_map.h> | 
|  | #include <zephyr/fs/nvs.h> | 
|  |  | 
|  | static struct nvs_fs fs; | 
|  |  | 
|  | #define NVS_PARTITION		storage_partition | 
|  | #define NVS_PARTITION_DEVICE	FIXED_PARTITION_DEVICE(NVS_PARTITION) | 
|  | #define NVS_PARTITION_OFFSET	FIXED_PARTITION_OFFSET(NVS_PARTITION) | 
|  |  | 
|  | /* 1000 msec = 1 sec */ | 
|  | #define SLEEP_TIME      100 | 
|  | /* maximum reboot counts, make high enough to trigger sector change (buffer */ | 
|  | /* rotation). */ | 
|  | #define MAX_REBOOT 400 | 
|  |  | 
|  | #define ADDRESS_ID 1 | 
|  | #define KEY_ID 2 | 
|  | #define RBT_CNT_ID 3 | 
|  | #define STRING_ID 4 | 
|  | #define LONG_ID 5 | 
|  |  | 
|  |  | 
|  | void main(void) | 
|  | { | 
|  | int rc = 0, cnt = 0, cnt_his = 0; | 
|  | char buf[16]; | 
|  | uint8_t key[8], longarray[128]; | 
|  | uint32_t reboot_counter = 0U, reboot_counter_his; | 
|  | struct flash_pages_info info; | 
|  |  | 
|  | /* define the nvs file system by settings with: | 
|  | *	sector_size equal to the pagesize, | 
|  | *	3 sectors | 
|  | *	starting at NVS_PARTITION_OFFSET | 
|  | */ | 
|  | fs.flash_device = NVS_PARTITION_DEVICE; | 
|  | if (!device_is_ready(fs.flash_device)) { | 
|  | printk("Flash device %s is not ready\n", fs.flash_device->name); | 
|  | return; | 
|  | } | 
|  | fs.offset = NVS_PARTITION_OFFSET; | 
|  | rc = flash_get_page_info_by_offs(fs.flash_device, fs.offset, &info); | 
|  | if (rc) { | 
|  | printk("Unable to get page info\n"); | 
|  | return; | 
|  | } | 
|  | fs.sector_size = info.size; | 
|  | fs.sector_count = 3U; | 
|  |  | 
|  | rc = nvs_mount(&fs); | 
|  | if (rc) { | 
|  | printk("Flash Init failed\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* ADDRESS_ID is used to store an address, lets see if we can | 
|  | * read it from flash, since we don't know the size read the | 
|  | * maximum possible | 
|  | */ | 
|  | rc = nvs_read(&fs, ADDRESS_ID, &buf, sizeof(buf)); | 
|  | if (rc > 0) { /* item was found, show it */ | 
|  | printk("Id: %d, Address: %s\n", ADDRESS_ID, buf); | 
|  | } else   {/* item was not found, add it */ | 
|  | strcpy(buf, "192.168.1.1"); | 
|  | printk("No address found, adding %s at id %d\n", buf, | 
|  | ADDRESS_ID); | 
|  | (void)nvs_write(&fs, ADDRESS_ID, &buf, strlen(buf)+1); | 
|  | } | 
|  | /* KEY_ID is used to store a key, lets see if we can read it from flash | 
|  | */ | 
|  | rc = nvs_read(&fs, KEY_ID, &key, sizeof(key)); | 
|  | if (rc > 0) { /* item was found, show it */ | 
|  | printk("Id: %d, Key: ", KEY_ID); | 
|  | for (int n = 0; n < 8; n++) { | 
|  | printk("%x ", key[n]); | 
|  | } | 
|  | printk("\n"); | 
|  | } else   {/* item was not found, add it */ | 
|  | printk("No key found, adding it at id %d\n", KEY_ID); | 
|  | key[0] = 0xFF; | 
|  | key[1] = 0xFE; | 
|  | key[2] = 0xFD; | 
|  | key[3] = 0xFC; | 
|  | key[4] = 0xFB; | 
|  | key[5] = 0xFA; | 
|  | key[6] = 0xF9; | 
|  | key[7] = 0xF8; | 
|  | (void)nvs_write(&fs, KEY_ID, &key, sizeof(key)); | 
|  | } | 
|  | /* RBT_CNT_ID is used to store the reboot counter, lets see | 
|  | * if we can read it from flash | 
|  | */ | 
|  | rc = nvs_read(&fs, RBT_CNT_ID, &reboot_counter, sizeof(reboot_counter)); | 
|  | if (rc > 0) { /* item was found, show it */ | 
|  | printk("Id: %d, Reboot_counter: %d\n", | 
|  | RBT_CNT_ID, reboot_counter); | 
|  | } else   {/* item was not found, add it */ | 
|  | printk("No Reboot counter found, adding it at id %d\n", | 
|  | RBT_CNT_ID); | 
|  | (void)nvs_write(&fs, RBT_CNT_ID, &reboot_counter, | 
|  | sizeof(reboot_counter)); | 
|  | } | 
|  | /* STRING_ID is used to store data that will be deleted,lets see | 
|  | * if we can read it from flash, since we don't know the size read the | 
|  | * maximum possible | 
|  | */ | 
|  | rc = nvs_read(&fs, STRING_ID, &buf, sizeof(buf)); | 
|  | if (rc > 0) { | 
|  | /* item was found, show it */ | 
|  | printk("Id: %d, Data: %s\n", | 
|  | STRING_ID, buf); | 
|  | /* remove the item if reboot_counter = 10 */ | 
|  | if (reboot_counter == 10U) { | 
|  | (void)nvs_delete(&fs, STRING_ID); | 
|  | } | 
|  | } else   { | 
|  | /* entry was not found, add it if reboot_counter = 0*/ | 
|  | if (reboot_counter == 0U) { | 
|  | printk("Id: %d not found, adding it\n", | 
|  | STRING_ID); | 
|  | strcpy(buf, "DATA"); | 
|  | (void)nvs_write(&fs, STRING_ID, &buf, strlen(buf) + 1); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* LONG_ID is used to store a larger dataset ,lets see if we can read | 
|  | * it from flash | 
|  | */ | 
|  | rc = nvs_read(&fs, LONG_ID, &longarray, sizeof(longarray)); | 
|  | if (rc > 0) { | 
|  | /* item was found, show it */ | 
|  | printk("Id: %d, Longarray: ", LONG_ID); | 
|  | for (int n = 0; n < sizeof(longarray); n++) { | 
|  | printk("%x ", longarray[n]); | 
|  | } | 
|  | printk("\n"); | 
|  | } else   { | 
|  | /* entry was not found, add it if reboot_counter = 0*/ | 
|  | if (reboot_counter == 0U) { | 
|  | printk("Longarray not found, adding it as id %d\n", | 
|  | LONG_ID); | 
|  | for (int n = 0; n < sizeof(longarray); n++) { | 
|  | longarray[n] = n; | 
|  | } | 
|  | (void)nvs_write( | 
|  | &fs, LONG_ID, &longarray, sizeof(longarray)); | 
|  | } | 
|  | } | 
|  |  | 
|  | cnt = 5; | 
|  | while (1) { | 
|  | k_msleep(SLEEP_TIME); | 
|  | if (reboot_counter < MAX_REBOOT) { | 
|  | if (cnt == 5) { | 
|  | /* print some history information about | 
|  | * the reboot counter | 
|  | * Check the counter history in flash | 
|  | */ | 
|  | printk("Reboot counter history: "); | 
|  | while (1) { | 
|  | rc = nvs_read_hist( | 
|  | &fs, RBT_CNT_ID, | 
|  | &reboot_counter_his, | 
|  | sizeof(reboot_counter_his), | 
|  | cnt_his); | 
|  | if (rc < 0) { | 
|  | break; | 
|  | } | 
|  | printk("...%d", reboot_counter_his); | 
|  | cnt_his++; | 
|  | } | 
|  | if (cnt_his == 0) { | 
|  | printk("\n Error, no Reboot counter"); | 
|  | } else { | 
|  | printk("\nOldest reboot counter: %d", | 
|  | reboot_counter_his); | 
|  | } | 
|  | printk("\nRebooting in "); | 
|  | } | 
|  | printk("...%d", cnt); | 
|  | cnt--; | 
|  | if (cnt == 0) { | 
|  | printk("\n"); | 
|  | reboot_counter++; | 
|  | (void)nvs_write( | 
|  | &fs, RBT_CNT_ID, &reboot_counter, | 
|  | sizeof(reboot_counter)); | 
|  | if (reboot_counter == MAX_REBOOT) { | 
|  | printk("Doing last reboot...\n"); | 
|  | } | 
|  | sys_reboot(0); | 
|  | } | 
|  | } else { | 
|  | printk("Reboot counter reached max value.\n"); | 
|  | printk("Reset to 0 and exit test.\n"); | 
|  | reboot_counter = 0U; | 
|  | (void)nvs_write(&fs, RBT_CNT_ID, &reboot_counter, | 
|  | sizeof(reboot_counter)); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } |