1 /* Console example — NVS commands
2 
3    This example code is in the Public Domain (or CC0 licensed, at your option.)
4 
5    Unless required by applicable law or agreed to in writing, this
6    software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
7    CONDITIONS OF ANY KIND, either express or implied.
8 */
9 
10 #include <stdio.h>
11 #include <errno.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <inttypes.h>
15 #include "esp_log.h"
16 #include "esp_console.h"
17 #include "argtable3/argtable3.h"
18 #include "freertos/FreeRTOS.h"
19 #include "freertos/event_groups.h"
20 #include "esp_err.h"
21 #include "cmd_nvs.h"
22 #include "nvs.h"
23 
24 typedef struct {
25     nvs_type_t type;
26     const char *str;
27 } type_str_pair_t;
28 
29 static const type_str_pair_t type_str_pair[] = {
30     { NVS_TYPE_I8, "i8" },
31     { NVS_TYPE_U8, "u8" },
32     { NVS_TYPE_U16, "u16" },
33     { NVS_TYPE_I16, "i16" },
34     { NVS_TYPE_U32, "u32" },
35     { NVS_TYPE_I32, "i32" },
36     { NVS_TYPE_U64, "u64" },
37     { NVS_TYPE_I64, "i64" },
38     { NVS_TYPE_STR, "str" },
39     { NVS_TYPE_BLOB, "blob" },
40     { NVS_TYPE_ANY, "any" },
41 };
42 
43 static const size_t TYPE_STR_PAIR_SIZE = sizeof(type_str_pair) / sizeof(type_str_pair[0]);
44 static const char *ARG_TYPE_STR = "type can be: i8, u8, i16, u16 i32, u32 i64, u64, str, blob";
45 static char current_namespace[16] = "storage";
46 static const char *TAG = "cmd_nvs";
47 
48 static struct {
49     struct arg_str *key;
50     struct arg_str *type;
51     struct arg_str *value;
52     struct arg_end *end;
53 } set_args;
54 
55 static struct {
56     struct arg_str *key;
57     struct arg_str *type;
58     struct arg_end *end;
59 } get_args;
60 
61 static struct {
62     struct arg_str *key;
63     struct arg_end *end;
64 } erase_args;
65 
66 static struct {
67     struct arg_str *namespace;
68     struct arg_end *end;
69 } erase_all_args;
70 
71 static struct {
72     struct arg_str *namespace;
73     struct arg_end *end;
74 } namespace_args;
75 
76 static struct {
77     struct arg_str *partition;
78     struct arg_str *namespace;
79     struct arg_str *type;
80     struct arg_end *end;
81 } list_args;
82 
83 
str_to_type(const char * type)84 static nvs_type_t str_to_type(const char *type)
85 {
86     for (int i = 0; i < TYPE_STR_PAIR_SIZE; i++) {
87         const type_str_pair_t *p = &type_str_pair[i];
88         if (strcmp(type, p->str) == 0) {
89             return  p->type;
90         }
91     }
92 
93     return NVS_TYPE_ANY;
94 }
95 
type_to_str(nvs_type_t type)96 static const char *type_to_str(nvs_type_t type)
97 {
98     for (int i = 0; i < TYPE_STR_PAIR_SIZE; i++) {
99         const type_str_pair_t *p = &type_str_pair[i];
100         if (p->type == type) {
101             return  p->str;
102         }
103     }
104 
105     return "Unknown";
106 }
107 
store_blob(nvs_handle_t nvs,const char * key,const char * str_values)108 static esp_err_t store_blob(nvs_handle_t nvs, const char *key, const char *str_values)
109 {
110     uint8_t value;
111     size_t str_len = strlen(str_values);
112     size_t blob_len = str_len / 2;
113 
114     if (str_len % 2) {
115         ESP_LOGE(TAG, "Blob data must contain even number of characters");
116         return ESP_ERR_NVS_TYPE_MISMATCH;
117     }
118 
119     char *blob = (char *)malloc(blob_len);
120     if (blob == NULL) {
121         return ESP_ERR_NO_MEM;
122     }
123 
124     for (int i = 0, j = 0; i < str_len; i++) {
125         char ch = str_values[i];
126         if (ch >= '0' && ch <= '9') {
127             value = ch - '0';
128         } else if (ch >= 'A' && ch <= 'F') {
129             value = ch - 'A' + 10;
130         } else if (ch >= 'a' && ch <= 'f') {
131             value = ch - 'a' + 10;
132         } else {
133             ESP_LOGE(TAG, "Blob data contain invalid character");
134             free(blob);
135             return ESP_ERR_NVS_TYPE_MISMATCH;
136         }
137 
138         if (i & 1) {
139             blob[j++] += value;
140         } else {
141             blob[j] = value << 4;
142         }
143     }
144 
145     esp_err_t err = nvs_set_blob(nvs, key, blob, blob_len);
146     free(blob);
147 
148     if (err == ESP_OK) {
149         err = nvs_commit(nvs);
150     }
151 
152     return err;
153 }
154 
print_blob(const char * blob,size_t len)155 static void print_blob(const char *blob, size_t len)
156 {
157     for (int i = 0; i < len; i++) {
158         printf("%02x", blob[i]);
159     }
160     printf("\n");
161 }
162 
163 
set_value_in_nvs(const char * key,const char * str_type,const char * str_value)164 static esp_err_t set_value_in_nvs(const char *key, const char *str_type, const char *str_value)
165 {
166     esp_err_t err;
167     nvs_handle_t nvs;
168     bool range_error = false;
169 
170     nvs_type_t type = str_to_type(str_type);
171 
172     if (type == NVS_TYPE_ANY) {
173         ESP_LOGE(TAG, "Type '%s' is undefined", str_type);
174         return ESP_ERR_NVS_TYPE_MISMATCH;
175     }
176 
177     err = nvs_open(current_namespace, NVS_READWRITE, &nvs);
178     if (err != ESP_OK) {
179         return err;
180     }
181 
182     if (type == NVS_TYPE_I8) {
183         int32_t value = strtol(str_value, NULL, 0);
184         if (value < INT8_MIN || value > INT8_MAX || errno == ERANGE) {
185             range_error = true;
186         } else {
187             err = nvs_set_i8(nvs, key, (int8_t)value);
188         }
189     } else if (type == NVS_TYPE_U8) {
190         uint32_t value = strtoul(str_value, NULL, 0);
191         if (value > UINT8_MAX || errno == ERANGE) {
192             range_error = true;
193         } else {
194             err = nvs_set_u8(nvs, key, (uint8_t)value);
195         }
196     } else if (type == NVS_TYPE_I16) {
197         int32_t value = strtol(str_value, NULL, 0);
198         if (value < INT16_MIN || value > INT16_MAX || errno == ERANGE) {
199             range_error = true;
200         } else {
201             err = nvs_set_i16(nvs, key, (int16_t)value);
202         }
203     } else if (type == NVS_TYPE_U16) {
204         uint32_t value = strtoul(str_value, NULL, 0);
205         if (value > UINT16_MAX || errno == ERANGE) {
206             range_error = true;
207         } else {
208             err = nvs_set_u16(nvs, key, (uint16_t)value);
209         }
210     } else if (type == NVS_TYPE_I32) {
211         int32_t value = strtol(str_value, NULL, 0);
212         if (errno != ERANGE) {
213             err = nvs_set_i32(nvs, key, value);
214         }
215     } else if (type == NVS_TYPE_U32) {
216         uint32_t value = strtoul(str_value, NULL, 0);
217         if (errno != ERANGE) {
218             err = nvs_set_u32(nvs, key, value);
219         }
220     } else if (type == NVS_TYPE_I64) {
221         int64_t value = strtoll(str_value, NULL, 0);
222         if (errno != ERANGE) {
223             err = nvs_set_i64(nvs, key, value);
224         }
225     } else if (type == NVS_TYPE_U64) {
226         uint64_t value = strtoull(str_value, NULL, 0);
227         if (errno != ERANGE) {
228             err = nvs_set_u64(nvs, key, value);
229         }
230     } else if (type == NVS_TYPE_STR) {
231         err = nvs_set_str(nvs, key, str_value);
232     } else if (type == NVS_TYPE_BLOB) {
233         err = store_blob(nvs, key, str_value);
234     }
235 
236     if (range_error || errno == ERANGE) {
237         nvs_close(nvs);
238         return ESP_ERR_NVS_VALUE_TOO_LONG;
239     }
240 
241     if (err == ESP_OK) {
242         err = nvs_commit(nvs);
243         if (err == ESP_OK) {
244             ESP_LOGI(TAG, "Value stored under key '%s'", key);
245         }
246     }
247 
248     nvs_close(nvs);
249     return err;
250 }
251 
get_value_from_nvs(const char * key,const char * str_type)252 static esp_err_t get_value_from_nvs(const char *key, const char *str_type)
253 {
254     nvs_handle_t nvs;
255     esp_err_t err;
256 
257     nvs_type_t type = str_to_type(str_type);
258 
259     if (type == NVS_TYPE_ANY) {
260         ESP_LOGE(TAG, "Type '%s' is undefined", str_type);
261         return ESP_ERR_NVS_TYPE_MISMATCH;
262     }
263 
264     err = nvs_open(current_namespace, NVS_READONLY, &nvs);
265     if (err != ESP_OK) {
266         return err;
267     }
268 
269     if (type == NVS_TYPE_I8) {
270         int8_t value;
271         err = nvs_get_i8(nvs, key, &value);
272         if (err == ESP_OK) {
273             printf("%d\n", value);
274         }
275     } else if (type == NVS_TYPE_U8) {
276         uint8_t value;
277         err = nvs_get_u8(nvs, key, &value);
278         if (err == ESP_OK) {
279             printf("%u\n", value);
280         }
281     } else if (type == NVS_TYPE_I16) {
282         int16_t value;
283         err = nvs_get_i16(nvs, key, &value);
284         if (err == ESP_OK) {
285             printf("%u\n", value);
286         }
287     } else if (type == NVS_TYPE_U16) {
288         uint16_t value;
289         if ((err = nvs_get_u16(nvs, key, &value)) == ESP_OK) {
290             printf("%u\n", value);
291         }
292     } else if (type == NVS_TYPE_I32) {
293         int32_t value;
294         if ((err = nvs_get_i32(nvs, key, &value)) == ESP_OK) {
295             printf("%d\n", value);
296         }
297     } else if (type == NVS_TYPE_U32) {
298         uint32_t value;
299         if ((err = nvs_get_u32(nvs, key, &value)) == ESP_OK) {
300             printf("%u\n", value);
301         }
302     } else if (type == NVS_TYPE_I64) {
303         int64_t value;
304         if ((err = nvs_get_i64(nvs, key, &value)) == ESP_OK) {
305             printf("%lld\n", value);
306         }
307     } else if (type == NVS_TYPE_U64) {
308         uint64_t value;
309         if ( (err = nvs_get_u64(nvs, key, &value)) == ESP_OK) {
310             printf("%llu\n", value);
311         }
312     } else if (type == NVS_TYPE_STR) {
313         size_t len;
314         if ( (err = nvs_get_str(nvs, key, NULL, &len)) == ESP_OK) {
315             char *str = (char *)malloc(len);
316             if ( (err = nvs_get_str(nvs, key, str, &len)) == ESP_OK) {
317                 printf("%s\n", str);
318             }
319             free(str);
320         }
321     } else if (type == NVS_TYPE_BLOB) {
322         size_t len;
323         if ( (err = nvs_get_blob(nvs, key, NULL, &len)) == ESP_OK) {
324             char *blob = (char *)malloc(len);
325             if ( (err = nvs_get_blob(nvs, key, blob, &len)) == ESP_OK) {
326                 print_blob(blob, len);
327             }
328             free(blob);
329         }
330     }
331 
332     nvs_close(nvs);
333     return err;
334 }
335 
erase(const char * key)336 static esp_err_t erase(const char *key)
337 {
338     nvs_handle_t nvs;
339 
340     esp_err_t err = nvs_open(current_namespace, NVS_READWRITE, &nvs);
341     if (err == ESP_OK) {
342         err = nvs_erase_key(nvs, key);
343         if (err == ESP_OK) {
344             err = nvs_commit(nvs);
345             if (err == ESP_OK) {
346                 ESP_LOGI(TAG, "Value with key '%s' erased", key);
347             }
348         }
349         nvs_close(nvs);
350     }
351 
352     return err;
353 }
354 
erase_all(const char * name)355 static esp_err_t erase_all(const char *name)
356 {
357     nvs_handle_t nvs;
358 
359     esp_err_t err = nvs_open(name, NVS_READWRITE, &nvs);
360     if (err == ESP_OK) {
361         err = nvs_erase_all(nvs);
362         if (err == ESP_OK) {
363             err = nvs_commit(nvs);
364         }
365     }
366 
367     ESP_LOGI(TAG, "Namespace '%s' was %s erased", name, (err == ESP_OK) ? "" : "not");
368 
369     nvs_close(nvs);
370     return ESP_OK;
371 }
372 
list(const char * part,const char * name,const char * str_type)373 static int list(const char *part, const char *name, const char *str_type)
374 {
375     nvs_type_t type = str_to_type(str_type);
376 
377     nvs_iterator_t it = nvs_entry_find(part, NULL, type);
378     if (it == NULL) {
379         ESP_LOGE(TAG, "No such enty was found");
380         return 1;
381     }
382 
383     do {
384         nvs_entry_info_t info;
385         nvs_entry_info(it, &info);
386         it = nvs_entry_next(it);
387 
388         printf("namespace '%s', key '%s', type '%s' \n",
389                info.namespace_name, info.key, type_to_str(info.type));
390     } while (it != NULL);
391 
392     return 0;
393 }
394 
set_value(int argc,char ** argv)395 static int set_value(int argc, char **argv)
396 {
397     int nerrors = arg_parse(argc, argv, (void **) &set_args);
398     if (nerrors != 0) {
399         arg_print_errors(stderr, set_args.end, argv[0]);
400         return 1;
401     }
402 
403     const char *key = set_args.key->sval[0];
404     const char *type = set_args.type->sval[0];
405     const char *values = set_args.value->sval[0];
406 
407     esp_err_t err = set_value_in_nvs(key, type, values);
408 
409     if (err != ESP_OK) {
410         ESP_LOGE(TAG, "%s", esp_err_to_name(err));
411         return 1;
412     }
413 
414     return 0;
415 }
416 
get_value(int argc,char ** argv)417 static int get_value(int argc, char **argv)
418 {
419     int nerrors = arg_parse(argc, argv, (void **) &get_args);
420     if (nerrors != 0) {
421         arg_print_errors(stderr, get_args.end, argv[0]);
422         return 1;
423     }
424 
425     const char *key = get_args.key->sval[0];
426     const char *type = get_args.type->sval[0];
427 
428     esp_err_t err = get_value_from_nvs(key, type);
429 
430     if (err != ESP_OK) {
431         ESP_LOGE(TAG, "%s", esp_err_to_name(err));
432         return 1;
433     }
434 
435     return 0;
436 }
437 
erase_value(int argc,char ** argv)438 static int erase_value(int argc, char **argv)
439 {
440     int nerrors = arg_parse(argc, argv, (void **) &erase_args);
441     if (nerrors != 0) {
442         arg_print_errors(stderr, erase_args.end, argv[0]);
443         return 1;
444     }
445 
446     const char *key = erase_args.key->sval[0];
447 
448     esp_err_t err = erase(key);
449 
450     if (err != ESP_OK) {
451         ESP_LOGE(TAG, "%s", esp_err_to_name(err));
452         return 1;
453     }
454 
455     return 0;
456 }
457 
erase_namespace(int argc,char ** argv)458 static int erase_namespace(int argc, char **argv)
459 {
460     int nerrors = arg_parse(argc, argv, (void **) &erase_all_args);
461     if (nerrors != 0) {
462         arg_print_errors(stderr, erase_all_args.end, argv[0]);
463         return 1;
464     }
465 
466     const char *name = erase_all_args.namespace->sval[0];
467 
468     esp_err_t err = erase_all(name);
469     if (err != ESP_OK) {
470         ESP_LOGE(TAG, "%s", esp_err_to_name(err));
471         return 1;
472     }
473 
474     return 0;
475 }
476 
set_namespace(int argc,char ** argv)477 static int set_namespace(int argc, char **argv)
478 {
479     int nerrors = arg_parse(argc, argv, (void **) &namespace_args);
480     if (nerrors != 0) {
481         arg_print_errors(stderr, namespace_args.end, argv[0]);
482         return 1;
483     }
484 
485     const char *namespace = namespace_args.namespace->sval[0];
486     strlcpy(current_namespace, namespace, sizeof(current_namespace));
487     ESP_LOGI(TAG, "Namespace set to '%s'", current_namespace);
488     return 0;
489 }
490 
list_entries(int argc,char ** argv)491 static int list_entries(int argc, char **argv)
492 {
493     list_args.partition->sval[0] = "";
494     list_args.namespace->sval[0] = "";
495     list_args.type->sval[0] = "";
496 
497     int nerrors = arg_parse(argc, argv, (void **) &list_args);
498     if (nerrors != 0) {
499         arg_print_errors(stderr, list_args.end, argv[0]);
500         return 1;
501     }
502 
503     const char *part = list_args.partition->sval[0];
504     const char *name = list_args.namespace->sval[0];
505     const char *type = list_args.type->sval[0];
506 
507     return list(part, name, type);
508 }
509 
register_nvs(void)510 void register_nvs(void)
511 {
512     set_args.key = arg_str1(NULL, NULL, "<key>", "key of the value to be set");
513     set_args.type = arg_str1(NULL, NULL, "<type>", ARG_TYPE_STR);
514 
515     set_args.value = arg_str1("v", "value", "<value>", "value to be stored");
516     set_args.end = arg_end(2);
517 
518     get_args.key = arg_str1(NULL, NULL, "<key>", "key of the value to be read");
519     get_args.type = arg_str1(NULL, NULL, "<type>", ARG_TYPE_STR);
520     get_args.end = arg_end(2);
521 
522     erase_args.key = arg_str1(NULL, NULL, "<key>", "key of the value to be erased");
523     erase_args.end = arg_end(2);
524 
525     erase_all_args.namespace = arg_str1(NULL, NULL, "<namespace>", "namespace to be erased");
526     erase_all_args.end = arg_end(2);
527 
528     namespace_args.namespace = arg_str1(NULL, NULL, "<namespace>", "namespace of the partition to be selected");
529     namespace_args.end = arg_end(2);
530 
531     list_args.partition = arg_str1(NULL, NULL, "<partition>", "partition name");
532     list_args.namespace = arg_str0("n", "namespace", "<namespace>", "namespace name");
533     list_args.type = arg_str0("t", "type", "<type>", ARG_TYPE_STR);
534     list_args.end = arg_end(2);
535 
536     const esp_console_cmd_t set_cmd = {
537         .command = "nvs_set",
538         .help = "Set key-value pair in selected namespace.\n"
539         "Examples:\n"
540         " nvs_set VarName i32 -v 123 \n"
541         " nvs_set VarName str -v YourString \n"
542         " nvs_set VarName blob -v 0123456789abcdef \n",
543         .hint = NULL,
544         .func = &set_value,
545         .argtable = &set_args
546     };
547 
548     const esp_console_cmd_t get_cmd = {
549         .command = "nvs_get",
550         .help = "Get key-value pair from selected namespace. \n"
551         "Example: nvs_get VarName i32",
552         .hint = NULL,
553         .func = &get_value,
554         .argtable = &get_args
555     };
556 
557     const esp_console_cmd_t erase_cmd = {
558         .command = "nvs_erase",
559         .help = "Erase key-value pair from current namespace",
560         .hint = NULL,
561         .func = &erase_value,
562         .argtable = &erase_args
563     };
564 
565     const esp_console_cmd_t erase_namespace_cmd = {
566         .command = "nvs_erase_namespace",
567         .help = "Erases specified namespace",
568         .hint = NULL,
569         .func = &erase_namespace,
570         .argtable = &erase_all_args
571     };
572 
573     const esp_console_cmd_t namespace_cmd = {
574         .command = "nvs_namespace",
575         .help = "Set current namespace",
576         .hint = NULL,
577         .func = &set_namespace,
578         .argtable = &namespace_args
579     };
580 
581     const esp_console_cmd_t list_entries_cmd = {
582         .command = "nvs_list",
583         .help = "List stored key-value pairs stored in NVS."
584         "Namespace and type can be specified to print only those key-value pairs.\n"
585         "Following command list variables stored inside 'nvs' partition, under namespace 'storage' with type uint32_t"
586         "Example: nvs_list nvs -n storage -t u32 \n",
587         .hint = NULL,
588         .func = &list_entries,
589         .argtable = &list_args
590     };
591 
592     ESP_ERROR_CHECK(esp_console_cmd_register(&set_cmd));
593     ESP_ERROR_CHECK(esp_console_cmd_register(&get_cmd));
594     ESP_ERROR_CHECK(esp_console_cmd_register(&erase_cmd));
595     ESP_ERROR_CHECK(esp_console_cmd_register(&namespace_cmd));
596     ESP_ERROR_CHECK(esp_console_cmd_register(&list_entries_cmd));
597     ESP_ERROR_CHECK(esp_console_cmd_register(&erase_namespace_cmd));
598 }
599