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