【ESP32】打造全网最强esp-idf基础教程——13.ESP32中的NVS

ESP32中的NVS

       这几天的天气只有钱包的余额能让我冷静,好好活着,每天都有新的打击,写写博客压压惊。

一、什么是NVS?
       NVS即Non-volatile storage,意思是非易失存储,也就是掉电后能依然能持久化保存数据。在我们应用NVS时,一般用于存储一些配置数据、状态数据等,一般不会用来存储存放大量的数据量。

       在嵌入式系统中,NVS主要是在flash进行键值对的存储。那么什么是键值对?这边举一个例子方便大家理解,假设我们要把东西存到flash中,按照底层的操作习惯,我们要先指定一个地址,然后对这个地址执行擦除操作,然后才能写入;读取的时候也需要根据这个地址,然后指定读取长度。如果我们要存的项比较多,又在代码中比较分散,我们对flash的地址就很难管理。因为我们很难知道要存的内容与其他地址有没冲突,会不会误擦除。存在诸多问题。那如果有一个机制,方便帮我们把这些检测判断活都干了,不需要我们指定地址操作,那岂不是很好。有的人又说了,文件系统不就是这样吗?是的,文件系统是这样,但NVS操作更加轻量级。

       在NVS中,我们要存一个值,我们不需要指定地址,但需要指定一个“键”,我们称为key,然后我们在这个“键”索引下存我们的值,也就是value。假设我们要存wifi的ssid和密码,我们可以在nvs中这样定义。

       key = ssid,value = testwifi

       key = password,value = 12345678
在键名ssid下,我们存的值是testwifi,在键名password下,我们存的值是12345678。因此键名key一般不会修改,经常修改的是value。

       在ESP32中对于NVS的操作,还需要指定一个命名空间,是因为还考虑了一种情况,在各个不同的功能模块中,键名是有可能取到一样的,比如对于wifi模块,存在一个password键名,对于管理员模块,可能也存在一个password键名,这样有可能就造成了重复,程序无法按我们的意思进行。如果我们增加了一个命名空间进行隔离,那么键名有重复也不怕,比如说在wifi模块中,我们指定一个命名空间”wifi”,在此命名空间下有ssid和password键名,在管理员模块,我们指定一个命名空间”manager”,在此命名空间下有password键名,这两组命名空间互不干扰。

二、ESP32中NVS的使用
       上一节介绍了NVS的基本概念,这一节我们看下esp-idf中给我们提供了什么接口。
//打开一个NVS存储操作

       esp_err_t nvs_open(const char* namespace_name, /*命名空间名称*/
       nvs_open_mode_t open_mode,  /* 打开的模式*/

                                                           NVS_READONLY,只读
                                                           NVS_READWRITE 读写

       nvs_handle_t *out_handle);        /* 返回的nvs操作句柄

       //向NVS中读取字符串数据

      esp_err_t nvs_get_str (nvs_handle_t handle,     /*nvs操作句柄*/
                                          const char* key,        /*键名*/
                                          char* out_value,         /*读取到的值*/
                                          size_t* length)            /*读取到的字符串长度*/

       //向NVS中读取二进制数据

       esp_err_t nvs_get_blob(nvs_handle_t handle,  /*nvs操作句柄*/
                                             const char* key,       /*键名*/
                                             void* out_value,     /*返回的值
                                             size_t* length);      /*返回值的长度*/

       //向NVS中写入字符串数据(最大4000字节)
       esp_err_t
nvs_set_str (nvs_handle_t handle,     /*nvs操作句柄*/
                                           const char* key,         /*键名*/
                                           const char* value);        /*值*/

       //向NVS中写入二进制数据(最大值:NVS分区大小*97.6%-4000)
       esp_err_t
nvs_set_blob(nvs_handle_t handle,     /*nvs操作句柄
                                             const char* key,         /*键名*/
                                             const void* value,         /*值*/
                                             size_t length);            /*大小*/

       //擦除对应命名空间下所有的nvs值
       esp_err_t
nvs_erase_all(
                                              nvs_handle_t handle); /*nvs操作句柄*/

       //在执行写操作后,为了确保写入到NVS区域中,需要调用此函数
       esp_err_t
nvs_commit(
                                            nvs_handle_t handle);        /*nvs操作句柄*/

       //关闭NVS操作句柄,释放资源
       void
nvs_close(
                                nvs_handle_t handle);        /*nvs操作句柄*/

       以上操作有几点注意:
       1)操作NVS的第一步就是调用nvs_open,获取到nvs句柄后才能执行其他操作
       2)命名空间长度、key长度,默认是15字节
       3)在执行写操作后,需要nvs_commit,才能确保写入到了nvs区域

       在上一课程对分区表进行解析后,我们知道nvs默认的分区地址是0x9000开始,默认大小是0x6000,flash中这一段的区域是专门留给我们做nvs存储的。我们来看下具体例程部分源码,源码位于esp32-board/nvs中。

#define NVS_BOB_NAMESPACE  "Bob"        //namespace最长15字节
#define NVS_JOHN_NAMESPACE  "John"      //namespace最长15字节

#define NVS_AGE_KEY    "age"        //年龄键名
#define NVS_SEX_KEY    "sex"        //性别键名

void app_main(void)
{
    //初始化NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        //NVS出现错误,执行擦除
        ESP_ERROR_CHECK(nvs_flash_erase());
        //重新尝试初始化
        ESP_ERROR_CHECK(nvs_flash_init());
    }

    char read_buf[64];
    size_t len = 0;
    //以字节方式写入到NVS中
    write_nvs_str(NVS_BOB_NAMESPACE,NVS_SEX_KEY,"female"); 
    write_nvs_str(NVS_JOHN_NAMESPACE,NVS_SEX_KEY,"male");
    //读取NVS_BOB_NAMESPACE命名空间中的SEX键值
    len =read_nvs_str(NVS_BOB_NAMESPACE,NVS_SEX_KEY,read_buf,64);
    if(len)
        ESP_LOGI(TAG,"Read BOB SEX:%s",read_buf);
    else
        ESP_LOGI(TAG,"Read BOB SEX fail,please perform nvs_erase_key and try again");
     //读取NVS_JOHN_NAMESPACE命名空间中的SEX键值
    len =read_nvs_str(NVS_JOHN_NAMESPACE,NVS_SEX_KEY,read_buf,64);
    if(len)
        ESP_LOGI(TAG,"Read JOHN SEX:%s",read_buf);
    else
        ESP_LOGI(TAG,"Read JOHN SEX fail,please perform nvs_erase_key and try again");
    uint8_t blob_buf[32];
    blob_buf[0] = 19;
    //以字节方式写入
    write_nvs_blob(NVS_BOB_NAMESPACE,NVS_AGE_KEY,blob_buf,1); 
    blob_buf[0] = 23;
    write_nvs_blob(NVS_JOHN_NAMESPACE,NVS_AGE_KEY,blob_buf,1); 
    
    //以字节方式读取
    len = read_nvs_blob(NVS_BOB_NAMESPACE,NVS_AGE_KEY,blob_buf,32);
    if(len)
        ESP_LOGI(TAG,"Read BOB age:%d",blob_buf[0]);
    else
        ESP_LOGI(TAG,"Read BOB age fail,please perform nvs_erase_key and try again");
    //以字节方式读取
    len = read_nvs_blob(NVS_JOHN_NAMESPACE,NVS_AGE_KEY,blob_buf,32);
    if(len)
        ESP_LOGI(TAG,"Read JOHN age:%d",blob_buf[0]);
    else
        ESP_LOGI(TAG,"Read JOHN age fail,please perform nvs_erase_key and try again");
    
    while(1)
    {
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

       这个例程中,有两个命名空间”Bob”和”John”,这两个命名空间分别都有”age”和”sex”这两个键,这个例程可以看到,两组命名空间之间互不干扰,其他具体代码请看例程源码。 

最后附上相关资料:

ESP32教程资料链接:
https://pan.baidu.com/s/1kCjD8yktZECSGmHomx_veg?pwd=q8er 
提取码:q8er 

配套源码下载地址:
esp32-board: esp32开发板配套的经典例程

鉴于实验需要开发板的支持,我也设计了一款ESP32开发板,包含部分传感器模块,1.69寸LCD高亮屏,Type-C一键下载,方便大家学习和做各种实验。开发板链接如下:

https://item.taobao.com/item.htm?ft=t&id=802401650392&spm=a21dvs.23580594.0.0.4fee645eXpkfcp&skuId=5635015963649
 

请大家多多支持。

 

       

  • 31
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
nvs_flash_init函数是ESP-IDFESP32 IoT Development Framework)的一个重要函数,用于初始化Flash驱动器的非易失性存储(NVS)分区。NVS分区是ESP32用于存储应用程序参数和配置的重要分区之一。该函数的目的是在ESP32启动时初始化NVS分区,以便应用程序可以访问和写入NVS存储器。以下是nvs_flash_init()函数的详细讲解: 函数原型:esp_err_t nvs_flash_init(void) 函数返回值:esp_err_t类型,如果函数执行成功,则返回ESP_OK,否则返回错误代码 函数功能:该函数用于初始化Flash驱动器的NVS分区。在ESP32启动时,该函数应该被调用一次,以便应用程序可以访问和写入NVS存储器。 函数说明:该函数是ESP-IDF的一个重要函数,需要在应用程序主函数之前调用。如果未初始化NVS分区,则应用程序将无法访问和写入NVS存储器。在调用该函数之前,需要确保已经初始化了ESP32的Flash驱动器。 函数示例: ``` #include <nvs_flash.h> void app_main(void) { // 初始化Flash驱动器的NVS分区 esp_err_t ret = nvs_flash_init(); if (ret == ESP_OK) { printf("NVS分区初始化成功"); } else { printf("NVS分区初始化失败"); } } ``` 需要注意的是,nvs_flash_init()函数仅需要在ESP32首次启动时调用一次,并且不应该在应用程序的主循环重复调用该函数。如果应用程序需要重新初始化NVS分区,则可以使用nvs_flash_erase()函数清除NVS分区的数据,并再次调用nvs_flash_init()函数以重新初始化NVS分区。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值