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