ESP32学习与快速总结——5.系统存储

1.ESP32分区表

  • 为什么ESP32要分区    00:34--
    • 简述:其他单片机生成文件少,功能少,而ESP32功能多,文件多       
  • 分区表各个文件简介   --7:31
  • vscode查看分区表  --9:33
  • ota通过idf.py menuconfig配置命令调出配置菜单  --12.35
  • 创建自己的分区  --17.41
  • 写.c文件
    • #include <stdio.h>
      #include "esp_partition.h"
      #include "esp_log.h"
      #include <string.h>static const char* TAG = "partition";   //TAG 区分日志#define USER_PARTITION_TYPE 0x40
      #define USER_PARTITION_SUBTYPE 0x00     //定义分区类型static const esp_partition_t* partition_user = NULL;    //定义分区指针操作分区的void app_main(void)
      {//找到自定义的分区,找得到之后会返回分区指针/*知识点1find与find_first的区别 --21.10find_first: 找到第一个符合条件的分区find: 找到符合条件的所有分区*/partition_user = esp_partition_find_first(USER_PARTITION_TYPE, USER_PARTITION_SUBTYPE, NULL); if (partition_user == NULL) {ESP_LOGE(TAG, "Failed to find user partition");return;}esp_partition_erase_range(partition_user, 0, 0x1000);    //擦除分区const char *data = "Hello, ESP32!";esp_partition_write(partition_user, 0, data, strlen(data));    //写入分区 char buffer[100];memset(buffer, 0, sizeof(buffer));esp_partition_read(partition_user, 0, buffer,strlen(data));     //读取分区ESP_LOGI(TAG, "Read from user partition: %s", buffer);return;}
      

补充1:分区表

# Name,   Type, SubType, Offset,  Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs,      data, nvs,     ,        0x6000,
phy_init, data, phy,     ,        0x1000,
factory,  app,  factory, ,        1M,
user,     0x40, 0x01,    ,        0x1000,  

列含义
  • Name:分区的名称,方便识别和引用。
  • Type:分区的类型,常见有 app(应用程序)、data(数据),也可以用十六进制表示自定义类型。
  • SubType:分区类型的子类型,进一步细分分区用途。
  • Offset:分区在存储设备上的起始偏移地址。
  • Size:分区的大小,可以用十六进制(如 0x1000)或单位(如 1M)表示。
  • Flags:分区的额外标志,可选字段。

每行解释

  • nvs:非易失性存储分区,用于存储系统和应用的配置信息。Type 为 dataSubType 为 nvs,大小是 0x6000
  • phy_init:物理层初始化数据分区,存储与硬件物理层初始化相关的数据。Type 为 dataSubType 为 phy,大小是 0x1000
  • factory:工厂应用分区,用于存储出厂默认的应用程序。Type 为 appSubType 为 factory,大小是 1M
  • user:自定义分区,Type 是十六进制 0x40SubType 是 0x01,大小是 0x1000,可用于用户自定义的数据或应用。

补充2 OTA

OTA 分区作用

设备软件更新时,新固件先下载到 OTA 分区,验证通过后,系统从该分区加载新固件,避免直接覆盖运行中的程序,提升更新安全性与可靠性。

OTA 分区常见配置示例及解释
# Name,   Type, SubType, Offset,  Size, Flags
ota_0,    app,  ota_0,   ,        1M,
ota_1,    app,  ota_1,   ,        1M,
  • Name:常见的 OTA 分区名称有 ota_0 和 ota_1,系统会在这两个分区间切换来实现固件更新。
  • Type:分区类型为 app,用于存储应用程序固件。
  • SubType:ota_0 和 ota_1 是 OTA 分区的子类型,可区分不同 OTA 槽位,让系统确定当前使用的分区。
  • Offset:即分区在存储设备上的起始偏移地址,留空由系统自动分配。
  • Size:每个 OTA 分区大小设为 1M,最多能存储 1MB 的应用程序固件。

设备实际使用时,通过 ota_0 和 ota_1 分区交替实现更新。若当前运行 ota_0 固件,新固件下载至 ota_1,验证通过后,下次启动便从 ota_1 加载新固件 。

本章脉络 

2.ESP中的NVS 

如何更方便的管理NVS存储操作

  • 背景  ---3.20
  • 操作  
    • 新建nvs测试程序
      • 创建项目文件夹
        • esp32下idf.py create-project nvs_test
        • nvs_test下idf.py build
        • VScode下打开文件夹
      • 代码
        • 定义两个命名空间,两个空间中有相同的键,向这两个键写入不同的值,观察他们会不会受到影响
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_err.h"#define NAME_SPACE_WIFI1 "wifi1"
#define NAME_SPACE_WIFI2 "wifi2"#define KEY_SSID_KEY "ssid"
#define KEY_PASSWORE_KEY "password"void nvs_blob_read(const char *name_space, const char *key, void *buffer, int maxlen)
{nvs_handle_t nvs_handle; //用于存储NVS分区的句柄size_t length = 0;nvs_open(name_space, NVS_READONLY, &nvs_handle);nvs_get_blob(nvs_handle, key, (void*)&length, &length); //读取键数据,并获取键数据的长度(主要是这个)if (length && length <= maxlen) //如果键数据存在且长度不超过maxlen{nvs_get_blob(nvs_handle, key, buffer, &length); //读取键数据}else{printf("nvs_blob_read: %s is not exist or too long\n", key); //打印错误信息}nvs_close(nvs_handle); //关闭NVS分区
}void app_main(void)
{nvs_handle_t nvs_handle1;esp_err_t ret = nvs_flash_init();   //初始化NVS分区if (ret != ESP_OK) {                 //如果初始化失败,擦除NVS分区,再初始化NVS分区nvs_flash_erase();               //擦除NVS分区ESP_ERROR_CHECK(nvs_flash_init()); //ESP_ERROR_CHECK()是一个宏定义,用于检查函数的返回值,如果返回值不为ESP_OK,则打印错误信息并终止程序}/*命名空间1 NVS写入*/nvs_open(NAME_SPACE_WIFI1, NVS_READWRITE, &nvs_handle1); //打开NVS分区,读写模式nvs_set_blob(nvs_handle1, KEY_SSID_KEY, "wifi_esp32", strlen("wifi_esp32")); //写入键数据nvs_set_blob(nvs_handle1, KEY_PASSWORE_KEY, "12345678", strlen("12345678")); //写入密码数据// strlen()函数是一个C语言库函数,用于计算字符串的长度。nvs_commit(nvs_handle1); //提交数据/*如果不调用nvs_commit()函数,那么数据将不会被写入到NVS分区中(flash)。写入的时机要跟随系统走如果调用nvs_commit()函数,那么数据将立即被写入到NVS分区中(flash)。不许推迟写入*/nvs_close(nvs_handle1); //关闭NVS分区/*命名空间2 NVS写入*/ nvs_open(NAME_SPACE_WIFI2, NVS_READWRITE, &nvs_handle1); //打开NVS分区,读写模式nvs_set_blob(nvs_handle1, KEY_SSID_KEY, "hello,world", strlen("hello,world")); //写入键数据nvs_set_blob(nvs_handle1, KEY_PASSWORE_KEY, "654321", strlen("654321")); //写入密码数据nvs_commit(nvs_handle1); //提交数据nvs_close(nvs_handle1); //关闭NVS分区vTaskDelay(1000 / portTICK_PERIOD_MS); //延时1秒char read_buffer[64];//用于存储键数据//命名空间WIFI1 SSID的值memset(read_buffer, 0, sizeof(read_buffer)); //清空read_buffer数组nvs_blob_read(NAME_SPACE_WIFI1, KEY_SSID_KEY, read_buffer, sizeof(read_buffer)); //读取键数据ESP_LOGI("NVS", "namespace: %s , key:%s -> value:%s", NAME_SPACE_WIFI1,KEY_SSID_KEY,read_buffer); //打印键数据
//命名空间WIFI1 PASSWORD的值memset(read_buffer, 0, sizeof(read_buffer)); nvs_blob_read(NAME_SPACE_WIFI1, KEY_SSID_KEY, read_buffer, sizeof(read_buffer)); ESP_LOGI("NVS", "namespace: %s , key:%s -> value:%s", NAME_SPACE_WIFI1,KEY_PASSWORE_KEY,read_buffer); 
//命名空间WIFI2 SSID的值memset(read_buffer, 0, sizeof(read_buffer)); nvs_blob_read(NAME_SPACE_WIFI2, KEY_SSID_KEY, read_buffer, sizeof(read_buffer)); ESP_LOGI("NVS", "namespace: %s , key:%s -> value:%s", NAME_SPACE_WIFI2,KEY_SSID_KEY,read_buffer); 
//命名空间WIFI2 PASSWORD的值memset(read_buffer, 0, sizeof(read_buffer)); nvs_blob_read(NAME_SPACE_WIFI2, KEY_SSID_KEY, read_buffer, sizeof(read_buffer)); ESP_LOGI("NVS", "namespace: %s , key:%s -> value:%s", NAME_SPACE_WIFI2,KEY_PASSWORE_KEY,read_buffer); 
}

3.ESP32中的SPIFFS文件系统

  • 新建SPIFFS测试程序
    • 创建项目文件夹----4.00
      • esp32下idf.py create-project spiffs_test
      • spiffs_test下 cp ../esp-idf/components/partition_table/partitions_singleapp.csv .
        • 注意这个partitions,这里后面的s不要拉下
        • 不要忘了后面有个点
      • 通过idf.py menuconfig指定分区表
      • spiffs_test下idf.py build
      • VScode下打开文件夹
      • 改分区表  5.48--6.30
        • 记得改成512k而不是1M,后面会改
    • 代码
      • 配置spiffs文件系统
        • esp_vfs_spiffs_register具体实现逻辑  9.10

      • 文件操作

        • 文件打开
          • f = fopen("/spiffs/hello.txt","r"); 尝试以只读模式打开位于 SPIFFS 文件系统下的 hello.txt 文件。如果打开失败(f 为 NULL),则通过 ESP_LOGE 输出错误日志并返回,结束当前函数执行。
        • 文件读取
          • fgets(line,sizeof(line),f); 使用 fgets 函数从打开的文件 f 中读取一行内容,存储到字符数组 line 中,最多读取 sizeof(line) - 1 个字符。
          • fclose(f); 读取完成后关闭文件。
        • 处理换行符
          • char *pos = strchr(line,'\n'); 查找字符数组 line 中第一次出现换行符 \n 的位置。
          • 如果找到换行符(pos 不为 NULL),则将该位置的字符设置为 '\0',即把换行符替换为字符串结束符,从而去掉换行符。
        • 日志输出
          • ESP_LOGI("spiffs","read str:%s",line); 使用 ESP_LOGI 输出读取到的字符串内容。
        • 文件系统注销
          • esp_vfs_spiffs_unregister(conf.partition_label); 注销之前注册的 SPIFFS 文件系统,这里的 conf 应该是之前定义好的配置结构体,partition_label 是文件系统分区标签。

        VFS(虚拟文件系统)是一个抽象层,它让操作系统能以统一的方式管理不同类型的文件系统,就像一个万能的 “翻译官”,让你不用管具体的文件系统差异,就能方便地操作文件。

        抽象层是一种编程和系统设计里常用的概念。

        现实中存在很多不同类型的文件系统,比如 FAT、NTFS、ext4 等,它们存储和管理文件的方式各不相同。

        抽象层就像是一个 “翻译官” 和 “协调者”。VFS 作为抽象层,把不同文件系统的具体操作细节都隐藏起来,给操作系统和用户程序提供一套统一的接口。无论你用的是哪种底层文件系统,操作系统和程序都可以通过这套统一接口来进行文件的创建、读取、修改、删除等操作。

        这样一来,操作系统和程序开发者就不用关心具体是哪种文件系统在工作,降低了开发和使用的复杂度。

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_spiffs.h"
#include "esp_err.h"
#include "esp_log.h"
#include <string.h>void app_main(void)
{// 定义 SPIFFS 配置结构体esp_vfs_spiffs_conf_t conf = {.base_path = "/spiffs",         // 挂载基础路径.format_if_mount_failed = true, // 挂载失败时格式化.max_files = 5,                 // 最大可打开文件数.partition_label = NULL         // 分区标签为 NULL};// 注册 SPIFFS 文件系统esp_err_t ret = esp_vfs_spiffs_register(&conf);if (ret != ESP_OK) {ESP_LOGE("spiffs", "spiffs mount fail!");return;}// 检查 SPIFFS 分区完整性ret = esp_spiffs_check(conf.partition_label);if (ret != ESP_OK) {ESP_LOGE("spiffs", "spiffs check fail!");return;}// 获取 SPIFFS 分区信息size_t total = 0, used = 0;ret = esp_spiffs_info(conf.partition_label, &total, &used);if (ret != ESP_OK) {ESP_LOGE("spiffs", "spiffs info fail!");return;}ESP_LOGI("spiffs", "spiffs total size:%d,used:%d", total, used);// 若使用空间大于总空间,再次检查分区完整性if (used > total) {ret = esp_spiffs_check(conf.partition_label);if (ret != ESP_OK) {ESP_LOGE("spiffs", "used > total & spiffs check fail!");return;}}// 以写入模式打开文件FILE *f = fopen("/spiffs/helloworld.txt", "w");if (f == NULL) {ESP_LOGE("spiffs", "Failed to open file");return;}// 写入内容fprintf(f, "Hello, World!\n");// 关闭写入的文件fclose(f);// 延时 1 秒vTaskDelay(pdMS_TO_TICKS(1000));// 以读取模式打开文件f = fopen("/spiffs/helloworld.txt", "r");if (f == NULL) {ESP_LOGE("spiffs", "Failed to open file for read");return;}// 读取文件一行内容char line[64];fgets(line, sizeof(line), f);// 关闭读取的文件fclose(f);// 去除换行符char *pos = strchr(line, '\n');if (pos) {*pos = 0;}// 打印读取的内容ESP_LOGI("spiffs", "read str:%s", line);// 注销 SPIFFS 文件系统esp_vfs_spiffs_unregister(conf.partition_label);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/79850.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux 进程控制(自用)

非阻塞调用waitpid 这样父进程就不会阻塞&#xff0c;此时循环使用我们可以让父进程执行其他任务而不是阻塞等待 进程程序替换 进程PCB加载到内存中的代码和数据 替换就是完全替换当前进程的代码段、数据段、堆和栈&#xff0c;保存当前的PCB 代码指的是二进制代码不是源码&a…

Spring 微服务解决了单体架构的哪些痛点?

1. 部署困难 (Deployment Difficulty & Risk) 单体痛点: 整体部署: 对单体应用的任何微小修改&#xff08;哪怕只是一行代码&#xff09;&#xff0c;都需要重新构建、测试和部署整个庞大的应用程序。部署频率低: 由于部署过程复杂且风险高&#xff0c;发布周期通常很长&a…

面试题之高频面试题

最近开始面试了&#xff0c;410面试了一家公司 针对自己薄弱的面试题库&#xff0c;深入了解下&#xff0c;也应付下面试。在这里先祝愿大家在现有公司好好沉淀&#xff0c;定位好自己的目标&#xff0c;在自己的领域上发光发热&#xff0c;在自己想要的领域上&#xff08;技术…

【MySQL】Read view存储的机制,记录可见分析

read view核心组成 1.1 事务id相关 creator_trx_id: 创建该read view的事务id 每开启一个事务都会生成一个 ReadView&#xff0c;而 creator_trx_id 就是这个开启的事务的 id。 m_ids: 创建read view时系统的活跃事务&#xff08;未提交的事务&#xff09;id集合 当前有哪些事…

【刷题Day20】TCP和UDP(浅)

TCP 和 UDP 有什么区别&#xff1f; TCP提供了可靠、面向连接的传输&#xff0c;适用于需要数据完整性和顺序的场景。 UDP提供了更轻量、面向报文的传输&#xff0c;适用于实时性要求高的场景。 特性TCPUDP连接方式面向连接无连接可靠性提供可靠性&#xff0c;保证数据按顺序…

Flink 内部通信底层原理

Flink 集群内部节点之间的通信是用 Akka 实现,比如 JobManager 和 TaskManager 之间的通信。而 operator 之间的数据传输是用 Netty 实现。 RPC 框架是 Flink 任务运行的基础,Flink 整个 RPC 框架基于 Akka 实现。 一、相关概念 RPC(Remote Procedure Call) 概念 定义:…

企业级Kubernetes 1.28高可用集群离线部署全指南(含全组件配置)

企业级Kubernetes 1.28高可用集群离线部署全指南(含全组件配置) 摘要:本文手把手教学在无外网环境下部署生产级Kubernetes 1.28高可用集群,涵盖ETCD集群、HAProxy+Keepalived负载均衡、Containerd运行时、Calico网络插件及Kuboard可视化管理全流程。提供100年有效证书配置…

【中间件】redis使用

一、redis介绍 redis是一种NoSQL类型的数据库&#xff0c;其数据存储在内存中&#xff0c;因此其数据查询效率很高&#xff0c;很快。常被用作数据缓存&#xff0c;分布式锁 等。SpringBoot集成了Redis&#xff0c;可查看开发文档Redis开发文档。Redis有自己的可视化工具Redis …

C语言——函数递归与迭代

各位CSDN的uu们大家好呀&#xff0c;今天将会给大家带来关于C语言的函数递归的知识&#xff0c;这一块知识理解起来稍微会比较难&#xff0c;需要多花点时间。 话不多说&#xff0c;让我们开始今天的内容吧&#xff01; 目录 1.函数递归 1.1 什么是递归&#xff1f; 1.2 递归…

藏品馆管理系统

藏品馆管理系统 项目简介 这是一个基于 PHP 开发的藏品馆管理系统&#xff0c;实现了藏品管理、用户管理等功能。 藏品馆管理系统 系统架构 开发语言&#xff1a;PHP数据库&#xff1a;MySQL前端框架&#xff1a;BootstrapJavaScript 库&#xff1a;jQuery 目录结构 book/…

centos停服 迁移centos7.3系统到新搭建的openEuler

背景 最近在做的事&#xff0c;简单来讲&#xff0c;就是一套系统差不多有10多台虚拟机&#xff0c;都是centos系统&#xff0c;版本主要是7.3、7.6、7.9&#xff0c;现在centos停止维护了&#xff0c;转为了centos stream&#xff0c;而centos stream的定位是&#xff1a;Red …

什么是 IDE?集成开发环境的功能与优势

原文&#xff1a;什么是 IDE&#xff1f;集成开发环境的功能与优势 | w3cschool笔记 &#xff08;注意&#xff1a;此为科普文章&#xff0c;请勿标记为付费文章&#xff01;且此文章并非我原创&#xff0c;不要标记为付费&#xff01;&#xff09; IDE 是什么&#xff1f; …

jenkins批量复制Job项目的shell脚本实现

背景 现在需要将“测试” 目录中的所有job全部复制到 一个新目录中 test2。可以结合jenkins提供的apilinux shell 进行实现。 测试目录的实际文件夹名称是 test。 脚本运行效果如下&#xff1a; [qdevsom5f-dev-hhyl shekk]$ ./copy_jenkins_job.sh 创建文件夹 test2 获取源…

VisualSVN过期后的解决方法

作为一款不错的源代码管理软件&#xff0c;svn还是有很多公司使用的。在vs中使用svn&#xff0c;大家一般用的都是VisualSVN插件。在30天试用期过后&#xff0c;它就不能被免费使用了。下面给大家讲如何免费延长过期时间&#xff08;自定义天数&#xff0c;可以设定一个很大的值…

硬件工程师笔记——电子器件汇总大全

目录 1、电阻 工作原理 欧姆定律 电阻的物理本质 一、限制电流 二、分压作用 三、消耗电能&#xff08;将电能转化为热能&#xff09; 2、压敏电阻 伏安特性 1. 过压保护 2. 电压调节 3. 浪涌吸收 4. 消噪与消火花 5. 高频应用 3、电容 工作原理 &#xff08;…

[图论]Kruskal

Kruskal 本质&#xff1a;贪心&#xff0c;对边进行操作。存储结构&#xff1a;边集数组。适用对象&#xff1a;可为负权图&#xff0c;可求最大生成树。核心思想&#xff1a;最短的边一定在最小生成树(MST)上&#xff0c;对最短的边进行贪心。算法流程&#xff1a;对全体边集…

vulnhub five86系列靶机合集

five86 ~ VulnHubhttps://www.vulnhub.com/series/five86,272/ five86-1渗透过程 信息收集 # 主机发现 nmap 192.168.56.0/24 -Pn ​ # 靶机全面扫描 nmap 192.168.56.131 -A -T4 目录扫描 dirsearch -u http://192.168.56.131/ /robots.txt提示/ona。 /ona二层目录扫描。 …

如何高效利用呼叫中心系统和AI语音机器人

要更好地使用呼叫中心系统和语音机器人&#xff0c;需要结合两者的优势&#xff0c;实现自动化、智能化、高效率的客户服务与业务运营。以下是优化策略和具体实践方法&#xff1a; 一、呼叫中心系统优化 1. 智能路由与IVR优化 智能ACD&#xff08;自动呼叫分配&#xff09; …

Nacos安装及数据持久化

1.Nacos安装及数据持久化 1.1下载nacos 下载地址&#xff1a;https://nacos.io/download/nacos-server/ 不用安装&#xff0c;直接解压缩即可。 1.2配置文件增加jdk环境和修改单机启动standalone 找到bin目录下的startup.cmd文件&#xff0c;添加以下语句(jdk路径根据自己…

【牛客练习赛137 C】题解

比赛链接 C. 变化的数组(Easy Version) 题目大意 一个长度为 n n n 的非负数组 a a a&#xff0c;要求执行 k k k 次操作&#xff0c;每次操作如下&#xff1a; 有 1 2 \frac{1}{2} 21​ 的概率令 a i ← a i ( a i ⊗ m ) x , ∀ i ∈ [ 1 , n ] a_i \leftarrow a_…