Skip to content

Instantly share code, notes, and snippets.

@themadsens
Last active July 20, 2020 11:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save themadsens/026d38f432727567c3c456fb4396621b to your computer and use it in GitHub Desktop.
Save themadsens/026d38f432727567c3c456fb4396621b to your computer and use it in GitHub Desktop.
NVS Iterator
// Example use of the iterator. Intended for the ESP32 console
// Caveat emptor: No express or implied warranty, functionality
// or fitness for any particular purpose
....
static int nvsdump_cmd(int argc, char** argv)
{
bool all = argc == 2 && *argv[1] == '*';
const char *key;
nvs_iter_typ_t typ;
nvs_iter_t it = nvs_iter_new(all ? NULL : "nvs-data", !all && argc > 1 ? argv[1] : NULL);
if (!it) {
printf("Can not iterate nvs\n");
return -1;
}
char buf[10];
while (nvs_iter_next(it, &key, &typ)) {
char val[20];
uint64_t data = nvs_iter_get_data(key);
if (typ.nvs_type == NVST_SIGNED || typ.nvs_type == NVST_UNSIGNED) {
uint64_t ival = 0;
memcpy(&ival, &data, typ.bytewid);
sprintf(val, " = %llu", ival);
}
else
val[0] = 0;
if (all)
printf("%-6s %15s:%-15s%s\n", nvs_iter_type_to_string(&typ, buf), nvs_iter_get_ns(it, key), key, val);
else
printf("%-6s %-15s%s\n", nvs_iter_type_to_string(&typ, buf), key, val);
}
nvs_iter_done(it);
return 0;
}
// From https://github.com/Edzelf/ESP32-Show_nvs_keys/
// Caveat emptor: No express or implied warranty, functionality
// or fitness for any particular purpose
// Show_nvs_keys.ino
// Read all the keys from nvs partition and dump this information.
//
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <esp_log.h>
#include <esp_partition.h>
#include "nvs_iter.h"
static const char *TAG = "NVSITER";
struct nvs_entry
{
uint8_t Ns ; // Namespace ID
uint8_t Type ; // Type of value
uint8_t Span ; // Number of entries used for this item
uint8_t Rvs ; // Reserved, should be 0xFF
uint32_t CRC ; // CRC
char Key[16] ; // Key in Ascii
uint64_t Data ; // Data in entry
} ;
struct nvs_page // For nvs entries
{ // 1 page is 4096 bytes
uint32_t State ;
uint32_t Seqnr ;
uint32_t Unused[5] ;
uint32_t CRC ;
uint8_t Bitmap[32] ;
struct nvs_entry Entry[126] ;
} ;
struct ns_index
{
uint16_t names[256];
uint16_t next;
char buf[2048];
};
struct nvs_iter
{
struct nvs_page page ; // Holds current page in partition
const esp_partition_t* nvs ; // Pointer to partition struct
char prefix[16];
struct ns_index *nsindex; // Holds namespace names
uint8_t namespace_ID;
uint32_t offset; // Offset in nvs partition
int32_t ix; // Index in Entry 0..125
};
char *nvs_iter_type_to_string(struct nvs_iter_typ *nityp, char *buf) {
strcpy(buf, nityp->nvs_type == NVST_SIGNED ? "int" :
nityp->nvs_type == NVST_UNSIGNED ? "uint" :
nityp->nvs_type == NVST_STRING ? "string" :
nityp->nvs_type == NVST_BLOB ? "blob" : "<unknown>");
if (nityp->nvs_type == NVST_SIGNED || nityp->nvs_type == NVST_UNSIGNED) {
sprintf(buf + strlen(buf), "%d", 8 * nityp->bytewid);
}
return buf;
}
uint64_t nvs_iter_get_data(const char *key) {
return *((uint64_t *) (key + 16));
}
char *nvs_iter_get_ns(struct nvs_iter *it, const char *key) {
size_t off = offsetof(struct nvs_entry, Key);
struct nvs_entry *ent = (struct nvs_entry *) (key - off);
return NULL == it->nsindex ? "<NONE>" : (it->nsindex->buf + it->nsindex->names[ent->Ns]);
}
//**************************************************************************************************
// F I N D N S I D *
//**************************************************************************************************
// Find the namespace ID for the namespace passed as parameter. *
//**************************************************************************************************
static uint8_t FindNsID(const esp_partition_t* nvs, const char* ns, struct ns_index **indexP)
{
esp_err_t result = ESP_OK ; // Result of reading partition
uint32_t offset = 0 ; // Offset in nvs partition
uint8_t bm ; // Bitmap for an entry
uint8_t res = 0xFF ; // Function result
struct nvs_page *page = malloc(sizeof(struct nvs_page));
struct ns_index *index = NULL;
if ( NULL == ns ) {
index = (struct ns_index *) malloc(sizeof(struct ns_index));
strcpy(index->buf, "<OVF>");
index->next = 6;
*indexP = index;
}
while ( offset < nvs->size )
{
result = esp_partition_read ( nvs, offset, // Read 1 page in nvs partition
page,
sizeof(struct nvs_page) ) ;
ESP_LOGV(TAG, "NSREAD: %d/%d -> %d", offset, nvs->size, result);
if ( result != ESP_OK )
{
ESP_LOGD ( TAG, "Error reading NVS!" ) ;
break ;
}
int i = 0 ;
while ( i < 126 )
{
bm = ( page->Bitmap[i/4] >> ( ( i % 4 ) * 2 ) ) & 0x03 ;// Get bitmap for this entry
if ( ( bm == 2 ) &&
( page->Entry[i].Ns == 0 ) &&
( NULL == ns || strcmp ( ns, page->Entry[i].Key ) == 0 ) )
{
res = page->Entry[i].Data & 0xFF ; // Return the ID
if ( NULL == ns ) {
int len = strlen(page->Entry[i].Key) + 1;
ESP_LOGV(TAG, "NS(%d): %s", res, page->Entry[i].Key);
if (index->next + len > 2048) {
index->names[res] = 0;
}
else {
index->names[res] = index->next;
strcpy(index->buf + index->next, page->Entry[i].Key);
index->next += len;
}
i += page->Entry[i].Span ; // Next entry
}
else {
ESP_LOGV(TAG, "Found NS(%d): %s", res, page->Entry[i].Key);
offset = nvs->size ; // Stop outer loop as well
break ;
}
}
else
{
if ( bm == 2 )
{
i += page->Entry[i].Span ; // Next entry
ESP_LOGV(TAG, "KEY(%d): %s", page->Entry[i].Ns, page->Entry[i].Ns == 0xFF ? "<ANY>" : page->Entry[i].Key);
}
else
{
i++ ;
}
}
}
offset += sizeof(struct nvs_page) ; // Prepare to read next page in nvs
}
free(page);
return res ;
}
nvs_iter_t nvs_iter_new(const char *namespace, const char *prefix)
{
esp_partition_iterator_t pi ; // Iterator for find
const esp_partition_t* nvs ; // Pointer to partition struct
struct nvs_iter *it;
struct ns_index *index;
uint8_t namespace_ID;
pi = esp_partition_find ( ESP_PARTITION_TYPE_DATA, // Get partition iterator for
ESP_PARTITION_SUBTYPE_ANY, // this partition
"nvs" ) ;
if ( pi )
{
nvs = esp_partition_get ( pi ) ; // Get partition struct
esp_partition_iterator_release ( pi ) ; // Release the iterator
}
else
{
ESP_LOGD ( TAG, "NVS Partition not found!" ) ;
return NULL;
}
if ( namespace && *namespace ) {
namespace_ID = FindNsID ( nvs, namespace, NULL );
index = NULL;
if ( namespace_ID == 0xFF ) {
ESP_LOGD ( TAG, "NVS namespace not found!" ) ;
return NULL;
}
}
else {
namespace_ID = 0xFF;
FindNsID ( nvs, NULL, &index );
}
it = (struct nvs_iter *) malloc(sizeof(struct nvs_iter));
assert(it);
it->namespace_ID = namespace_ID;
strcpy(it->prefix, prefix ? prefix : "");
it->nvs = nvs;
it->ix = -1;
it->offset = 0;
it->nsindex = index;
ESP_LOGV(TAG, "OFF:%d/%d IX:%d NS:%d", it->offset, it->nvs->size, it->ix, it->namespace_ID);
return it;
}
void nvs_iter_done(struct nvs_iter *it)
{
if ( NULL != it ) {
if ( it->nsindex ) free(it->nsindex);
free(it);
}
}
bool nvs_iter_next(struct nvs_iter *it, const char **key, nvs_iter_typ_t *typ)
{
esp_err_t result = ESP_OK ;
ESP_LOGV(TAG, "OFF:%d/%d IX:%d", it->offset, it->nvs->size, it->ix);
while ( it->offset < it->nvs->size )
{
if (it->ix < 0 || it->ix >= 126) {
result = esp_partition_read ( it->nvs, it->offset, // Read 1 page in nvs partition
&it->page,
sizeof(struct nvs_page) ) ;
ESP_LOGV(TAG, "NSREAD: %d/%d -> %d", it->offset, it->nvs->size, result);
if ( result != ESP_OK ) {
ESP_LOGD ( TAG, "Error reading NVS!" ) ;
return false;
}
it->ix = 0;
it->offset += sizeof(struct nvs_page) ; // Prepare to read next page in nvs
}
while ( it->ix < 126 )
{
uint8_t bm = ( it->page.Bitmap[it->ix/4] >> ( ( it->ix % 4 ) * 2 ) ) & 0x03 ; // Get bitmap for this entry
if ( bm == 2 )
{
const char *ky = it->page.Entry[it->ix].Key;
uint8_t tp = it->page.Entry[it->ix].Type;
int ns = it->page.Entry[it->ix].Ns;
it->ix += it->page.Entry[it->ix].Span ; // Next entry
if ( ( ( it->namespace_ID == 0xFF && ns > 0 && ns < 0xFF ) || // Show all if ID = 0xFF
ns == it->namespace_ID ) && // otherwise just my namespace
( ! it->prefix[0] ||
0 == strncmp(it->prefix, ky, strlen(it->prefix)) ) )
{
typ->nvs_type = (tp >> 4) & 0xf;
typ->bytewid = tp & 0xf;
*key = ky;
return true;
}
}
else {
it->ix++ ;
}
}
}
return false;
}
// vim: set sw=2 sts=2 et:
// From https://github.com/Edzelf/ESP32-Show_nvs_keys/
// Caveat emptor: No express or implied warranty, functionality
// or fitness for any particular purpose
// Show_nvs_keys.ino
// Read all the keys from nvs partition and dump this information.
//
typedef struct nvs_iter_typ {
// From ItemType in nvs_types.hpp
enum { NVST_SIGNED=0, NVST_UNSIGNED=1, NVST_STRING=2, NVST_BLOB=4 } nvs_type;
uint8_t bytewid;
} nvs_iter_typ_t;
typedef struct nvs_iter* nvs_iter_t;
nvs_iter_t nvs_iter_new(const char *nmspace, const char *prefix);
void nvs_iter_done(struct nvs_iter *it);
bool nvs_iter_next(struct nvs_iter *it, const char **key, nvs_iter_typ_t *typ);
char *nvs_iter_type_to_string(nvs_iter_typ_t *nityp, char *buf);
uint64_t nvs_iter_get_data(const char *key);
char *nvs_iter_get_ns(struct nvs_iter *it, const char *key);
// vim: set sw=2 sts=2 et:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment