首页 > 其他分享 >【ESP32】打造全网最强esp-idf基础教程——13.ESP32中的NVS

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

时间:2024-06-30 21:59:14浏览次数:27  
标签:13 handle NVS ESP32 blob nvs buf 键名

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
 

请大家多多支持。

 

       

标签:13,handle,NVS,ESP32,blob,nvs,buf,键名
From: https://blog.csdn.net/Hello_kitty_ling/article/details/139997557

相关文章

  • 数据分析必备:一步步教你如何用matplotlib做数据可视化(13)
    1、Matplotlib文本Matplotlib具有广泛的文本支持,包括对数学表达式的支持,对光栅和矢量输出的TrueType支持,具有任意旋转的换行符分隔文本以及unicode支持。Matplotlib包含自己的matplotlib.font_manager,它实现了一个跨平台,符合W3C标准的字体查找算法。用户可以对文本属性(......
  • 2713. 矩阵中严格递增的单元格数 Hard
    给你一个下标从 1 开始、大小为 mxn 的整数矩阵 mat,你可以选择任一单元格作为 起始单元格 。从起始单元格出发,你可以移动到 同一行或同一列 中的任何其他单元格,但前提是目标单元格的值 严格大于 当前单元格的值。你可以多次重复这一过程,从一个单元格移动到另一......
  • JAVA高级进阶13单元测试、反射、注解
    第十三天、单元测试、反射、注解单元测试介绍单元测试就是针对最小的功能单元(方法),编写测试代码对其进行正确性测试咱们之前是如何进行单元测试的?有啥问题?只能在main方法编写测试代码,去调用其他方法进行测试。无法实现自动化测试,一个方法测试失败,可能影响其他方......
  • 【重写SpringFramework】第一章beans模块:本章小结(chapter 1-13)
    1.前言在Spring框架中,beans模块是仅次于core模块的基础模块。我们知道,IOC机制是Spring框架的两大基石之一,beans模块的主要任务就是实现控制反转和依赖注入的功能。从具体实现来说,BeanFactory接口是整个模块的核心接口,几乎所有功能都是围绕对象展开的。BeanFacto......
  • AI数据分析013:根据时间序列数据生成动态条形图
    文章目录一、介绍二、输入内容三、输出内容一、介绍动态条形竞赛图(BarChartRace)是一种通过动画展示分类数据随时间变化的可视化工具。它通过动态条形图的形式,展示不同类别在不同时间点的数据排名和变化情况。这种图表非常适合用来展示时间序列数据的变化,能够直......
  • MQTT + 微信小程序:打造你的掌上ESP32智能鱼缸,远程监控,实时掌控
    一、项目概述厌倦了传统鱼缸的繁琐管理?想要随时随地了解鱼儿的生活状态?本项目将带你使用ESP32开发板、MQTT通信协议和微信小程序,打造一个功能完善的智能鱼缸系统。实时监测水温、远程控制灯光、定时定量喂食……一切尽在掌握!二、系统架构本系统采用典型的物联网架构,......
  • Arturia - FX Collection 5 v5.0.0 VST, VST3, AAX x64 {R2R} [13.06.2024]
    Arturia-FXCollection5v5.0.0forWindowsmac【【新品发布+小广告】ArturiaFXCollection5超强音乐制作插件套装34款产品逐一点评】https://www.bilibili.com/video/B...4d4e7f5c56f93e901cd    包括BusEXCITER-104BusFORCEBusPEAKChorusDIMENSION-DCh......
  • [题解]CF1312E Array Shrinking
    思路本题为P3146变式,也算是一道很经典的区间DP题了。因为\(n\leq500\),考虑区间DP。定义\(dp_{i,j}\)表示操作\([i,j]\)区间剩余长度的最小值。那么,我们可以枚举一个中间值\(k\),可以显然地得到一个状态转移方程(即不能合二为一的情况):\[dp_{i,j}=\min(dp_{i,......
  • 基于SpringBoot的高校大学生学科竞赛管理系统+53135(免费领源码)可做计算机毕业设计JAVA
    springboot高校大学生学科竞赛管理系统的设计与实现摘 要随着互联网趋势的到来,各行各业都在考虑利用互联网将自己推广出去,最好方式就是建立自己的互联网系统,并对其进行维护和管理。在现实运用中,应用软件的工作规则和开发步骤,采用Java技术建设高校大学生学科竞赛管理系统。......
  • Klipper RP2040 display ssd1306 0.96 屏幕配置
    接线屏幕接线parampinGNDGNDVCCVCCSCLSDA编码器接线parampinGNDGNDEN1VCCEN2CLklipper配置#显示屏及旋钮[display]lcd_type:ssd1306#i2c_bus:i2c0dencoder_pins:^gpio24,^gpio23encoder_steps_per_detent:2c......