1 /*
2 * NVS Sample for Zephyr using high level API, the sample illustrates the usage
3 * of NVS for storing data of different kind (strings, binary blobs, unsigned
4 * 32 bit integer) and also how to read them back from flash. The reading of
5 * data is illustrated for both a basic read (latest added value) as well as
6 * reading back the history of data (previously added values). Next to reading
7 * and writing data it also shows how data can be deleted from flash.
8 *
9 * The sample stores the following items:
10 * 1. A string representing an IP-address: stored at id=1, data="192.168.1.1"
11 * 2. A binary blob representing a key: stored at id=2, data=FF FE FD FC FB FA
12 * F9 F8
13 * 3. A reboot counter (32bit): stored at id=3, data=reboot_counter
14 * 4. A string: stored at id=4, data="DATA" (used to illustrate deletion of
15 * items)
16 *
17 * At first boot the sample checks if the data is available in flash and adds
18 * the items if they are not in flash.
19 *
20 * Every reboot increases the values of the reboot_counter and updates it in
21 * flash.
22 *
23 * At the 10th reboot the string item with id=4 is deleted (or marked for
24 * deletion).
25 *
26 * At the 11th reboot the string item with id=4 can no longer be read with the
27 * basic nvs_read() function as it has been deleted. It is possible to read the
28 * value with nvs_read_hist()
29 *
30 * At the 78th reboot the first sector is full and a new sector is taken into
31 * use. The data with id=1, id=2 and id=3 is copied to the new sector. As a
32 * result of this the history of the reboot_counter will be removed but the
33 * latest values of address, key and reboot_counter is kept.
34 *
35 * Copyright (c) 2018 Laczen
36 *
37 * SPDX-License-Identifier: Apache-2.0
38 */
39
40
41 #include <zephyr/kernel.h>
42 #include <zephyr/sys/reboot.h>
43 #include <zephyr/device.h>
44 #include <string.h>
45 #include <zephyr/drivers/flash.h>
46 #include <zephyr/storage/flash_map.h>
47 #include <zephyr/fs/nvs.h>
48
49 static struct nvs_fs fs;
50
51 #define NVS_PARTITION storage_partition
52 #define NVS_PARTITION_DEVICE FIXED_PARTITION_DEVICE(NVS_PARTITION)
53 #define NVS_PARTITION_OFFSET FIXED_PARTITION_OFFSET(NVS_PARTITION)
54
55 /* 1000 msec = 1 sec */
56 #define SLEEP_TIME 100
57 /* maximum reboot counts, make high enough to trigger sector change (buffer */
58 /* rotation). */
59 #define MAX_REBOOT 400
60
61 #define ADDRESS_ID 1
62 #define KEY_ID 2
63 #define RBT_CNT_ID 3
64 #define STRING_ID 4
65 #define LONG_ID 5
66
67
main(void)68 int main(void)
69 {
70 int rc = 0, cnt = 0, cnt_his = 0;
71 char buf[16];
72 uint8_t key[8], longarray[128];
73 uint32_t reboot_counter = 0U, reboot_counter_his;
74 struct flash_pages_info info;
75
76 /* define the nvs file system by settings with:
77 * sector_size equal to the pagesize,
78 * 3 sectors
79 * starting at NVS_PARTITION_OFFSET
80 */
81 fs.flash_device = NVS_PARTITION_DEVICE;
82 if (!device_is_ready(fs.flash_device)) {
83 printk("Flash device %s is not ready\n", fs.flash_device->name);
84 return 0;
85 }
86 fs.offset = NVS_PARTITION_OFFSET;
87 rc = flash_get_page_info_by_offs(fs.flash_device, fs.offset, &info);
88 if (rc) {
89 printk("Unable to get page info\n");
90 return 0;
91 }
92 fs.sector_size = info.size;
93 fs.sector_count = 3U;
94
95 rc = nvs_mount(&fs);
96 if (rc) {
97 printk("Flash Init failed\n");
98 return 0;
99 }
100
101 /* ADDRESS_ID is used to store an address, lets see if we can
102 * read it from flash, since we don't know the size read the
103 * maximum possible
104 */
105 rc = nvs_read(&fs, ADDRESS_ID, &buf, sizeof(buf));
106 if (rc > 0) { /* item was found, show it */
107 printk("Id: %d, Address: %s\n", ADDRESS_ID, buf);
108 } else {/* item was not found, add it */
109 strcpy(buf, "192.168.1.1");
110 printk("No address found, adding %s at id %d\n", buf,
111 ADDRESS_ID);
112 (void)nvs_write(&fs, ADDRESS_ID, &buf, strlen(buf)+1);
113 }
114 /* KEY_ID is used to store a key, lets see if we can read it from flash
115 */
116 rc = nvs_read(&fs, KEY_ID, &key, sizeof(key));
117 if (rc > 0) { /* item was found, show it */
118 printk("Id: %d, Key: ", KEY_ID);
119 for (int n = 0; n < 8; n++) {
120 printk("%x ", key[n]);
121 }
122 printk("\n");
123 } else {/* item was not found, add it */
124 printk("No key found, adding it at id %d\n", KEY_ID);
125 key[0] = 0xFF;
126 key[1] = 0xFE;
127 key[2] = 0xFD;
128 key[3] = 0xFC;
129 key[4] = 0xFB;
130 key[5] = 0xFA;
131 key[6] = 0xF9;
132 key[7] = 0xF8;
133 (void)nvs_write(&fs, KEY_ID, &key, sizeof(key));
134 }
135 /* RBT_CNT_ID is used to store the reboot counter, lets see
136 * if we can read it from flash
137 */
138 rc = nvs_read(&fs, RBT_CNT_ID, &reboot_counter, sizeof(reboot_counter));
139 if (rc > 0) { /* item was found, show it */
140 printk("Id: %d, Reboot_counter: %d\n",
141 RBT_CNT_ID, reboot_counter);
142 } else {/* item was not found, add it */
143 printk("No Reboot counter found, adding it at id %d\n",
144 RBT_CNT_ID);
145 (void)nvs_write(&fs, RBT_CNT_ID, &reboot_counter,
146 sizeof(reboot_counter));
147 }
148 /* STRING_ID is used to store data that will be deleted,lets see
149 * if we can read it from flash, since we don't know the size read the
150 * maximum possible
151 */
152 rc = nvs_read(&fs, STRING_ID, &buf, sizeof(buf));
153 if (rc > 0) {
154 /* item was found, show it */
155 printk("Id: %d, Data: %s\n",
156 STRING_ID, buf);
157 /* remove the item if reboot_counter = 10 */
158 if (reboot_counter == 10U) {
159 (void)nvs_delete(&fs, STRING_ID);
160 }
161 } else {
162 /* entry was not found, add it if reboot_counter = 0*/
163 if (reboot_counter == 0U) {
164 printk("Id: %d not found, adding it\n",
165 STRING_ID);
166 strcpy(buf, "DATA");
167 (void)nvs_write(&fs, STRING_ID, &buf, strlen(buf) + 1);
168 }
169 }
170
171 /* LONG_ID is used to store a larger dataset ,lets see if we can read
172 * it from flash
173 */
174 rc = nvs_read(&fs, LONG_ID, &longarray, sizeof(longarray));
175 if (rc > 0) {
176 /* item was found, show it */
177 printk("Id: %d, Longarray: ", LONG_ID);
178 for (int n = 0; n < sizeof(longarray); n++) {
179 printk("%x ", longarray[n]);
180 }
181 printk("\n");
182 } else {
183 /* entry was not found, add it if reboot_counter = 0*/
184 if (reboot_counter == 0U) {
185 printk("Longarray not found, adding it as id %d\n",
186 LONG_ID);
187 for (int n = 0; n < sizeof(longarray); n++) {
188 longarray[n] = n;
189 }
190 (void)nvs_write(
191 &fs, LONG_ID, &longarray, sizeof(longarray));
192 }
193 }
194
195 cnt = 5;
196 while (1) {
197 k_msleep(SLEEP_TIME);
198 if (reboot_counter < MAX_REBOOT) {
199 if (cnt == 5) {
200 /* print some history information about
201 * the reboot counter
202 * Check the counter history in flash
203 */
204 printk("Reboot counter history: ");
205 while (1) {
206 rc = nvs_read_hist(
207 &fs, RBT_CNT_ID,
208 &reboot_counter_his,
209 sizeof(reboot_counter_his),
210 cnt_his);
211 if (rc < 0) {
212 break;
213 }
214 printk("...%d", reboot_counter_his);
215 cnt_his++;
216 }
217 if (cnt_his == 0) {
218 printk("\n Error, no Reboot counter");
219 } else {
220 printk("\nOldest reboot counter: %d",
221 reboot_counter_his);
222 }
223 printk("\nRebooting in ");
224 }
225 printk("...%d", cnt);
226 cnt--;
227 if (cnt == 0) {
228 printk("\n");
229 reboot_counter++;
230 (void)nvs_write(
231 &fs, RBT_CNT_ID, &reboot_counter,
232 sizeof(reboot_counter));
233 if (reboot_counter == MAX_REBOOT) {
234 printk("Doing last reboot...\n");
235 }
236 sys_reboot(0);
237 }
238 } else {
239 printk("Reboot counter reached max value.\n");
240 printk("Reset to 0 and exit test.\n");
241 reboot_counter = 0U;
242 (void)nvs_write(&fs, RBT_CNT_ID, &reboot_counter,
243 sizeof(reboot_counter));
244 break;
245 }
246 }
247 return 0;
248 }
249