ESP32的系统存储

1. ESP32 中的分区表(Partition Table)

作用:
将 Flash 划分为多个区域,便于管理程序和数据。
支持多种类型的分区:app(程序)、data(数据)、自定义类型等。

分区名类型  子类型大小    说明
nvs        datanvs0x6000非易失存储
phy_initdataphy0x1000PHY 初始化数据(通常不用)
factoryappfactory1M默认启动程序

 OTA 分区表(partitions_two_ota.csv):
 新增 ota data:记录当前 OTA 程序信息
 多个 app 分区:ota_0、ota_1 等,支持固件升级

 自定义分区:
 类型可自定义(0x40–0xFE)
 使用 esp_partition_find_first()获取分区指针
 使用 esp_partition_write/read() 进行读写

操作自定义分区的 API 示例

#include "esp_partition.h"

#define USER_PARTITION_TYPE    0x40
#define USER_PARTITION_SUBTYPE 0x01

const esp_partition_t *partition = esp_partition_find_first(USER_PARTITION_TYPE, USER_PARTITION_SUBTYPE, NULL);

// 擦除
esp_partition_erase_range(partition, 0, 4096);

// 写入
esp_partition_write(partition, 0, "hello", 5);

// 读取
char buf[1024];
esp_partition_read(partition, 0, buf, 5);

2. ESP32 中的 NVS(Non-Volatile Storage)

概念:
 键值对存储,掉电不丢失
 支持命名空间(namespace)隔离不同模块的键名

 常用 API:

nvs_open()        // 打开命名空间
nvs_set_str()     // 写入字符串
nvs_get_str()     // 读取字符串
nvs_set_blob()    // 写入二进制数据
nvs_get_blob()    // 读取二进制数据
nvs_commit()      // 提交写入
nvs_close()       // 关闭句柄

使用流程:
1. 初始化 nvs_flash_init()
2. 打开命名空间 nvs_open()
3. 读写数据
4. 提交并关闭

完整使用流程示例

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_err.h"
#include "nvs_flash.h"
#include <string.h>
#include "esp_log.h"

#define NAME_SPACE_WIFI1 "wifi1"
#define NAME_SPACE_WIFI2 "wifi2"

#define NVS_SSID_KEY     "ssid"
#define NVS_PASSWORD_KEY "password"

// 修正后的读取函数
esp_err_t nvs_blob_read(const char* namespace_name, const char* key, void* buffer, size_t maxlen)
{
    nvs_handle_t nvs_handle;
    esp_err_t err;
    
    // 打开命名空间
    err = nvs_open(namespace_name, NVS_READONLY, &nvs_handle);
    if (err != ESP_OK) {
        ESP_LOGE("NVS", "打开命名空间失败: %s", esp_err_to_name(err));
        return err;
    }
    
    // 第一步:获取数据长度
    size_t length = 0;
    err = nvs_get_blob(nvs_handle, key, NULL, &length);
    if (err != ESP_OK) {
        ESP_LOGE("NVS", "获取数据长度失败: %s", esp_err_to_name(err));
        nvs_close(nvs_handle);
        return err;
    }
    
    // 检查缓冲区是否足够
    if (length > 0 && length <= maxlen) {
        // 第二步:实际读取数据
        err = nvs_get_blob(nvs_handle, key, buffer, &length);
        if (err != ESP_OK) {
            ESP_LOGE("NVS", "读取数据失败: %s", esp_err_to_name(err));
        }
    } else {
        ESP_LOGE("NVS", "缓冲区太小或数据长度为0: 需要%d字节,但只有%d字节", length, maxlen);
        err = ESP_FAIL;
    }
    
    nvs_close(nvs_handle);
    return err;
}

void app_main(void)
{
    nvs_handle_t nvs_handle1;
    esp_err_t ret;

    // 初始化 NVS
    ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_LOGI("NVS", "NVS 需要擦除重建");
        nvs_flash_erase();
        ESP_ERROR_CHECK(nvs_flash_init());
    } else if (ret != ESP_OK) {
        ESP_LOGE("NVS", "NVS 初始化失败: %s", esp_err_to_name(ret));
        return;
    }

    ESP_LOGI("NVS", "=== 开始写入 NVS 数据 ===");

    // 命名空间1 - 存储 WiFi 配置
    ret = nvs_open(NAME_SPACE_WIFI1, NVS_READWRITE, &nvs_handle1);
    if (ret == ESP_OK) {
        // 注意:使用 nvs_set_blob 时,长度应该包含字符串终止符
        nvs_set_blob(nvs_handle1, NVS_SSID_KEY, "wifi_esp32", strlen("wifi_esp32") + 1);
        nvs_set_blob(nvs_handle1, NVS_PASSWORD_KEY, "12345678", strlen("12345678") + 1);
        nvs_commit(nvs_handle1);
        nvs_close(nvs_handle1);
        ESP_LOGI("NVS", "命名空间 %s 写入完成", NAME_SPACE_WIFI1);
    }

    // 命名空间2 - 存储另一个 WiFi 配置
    ret = nvs_open(NAME_SPACE_WIFI2, NVS_READWRITE, &nvs_handle1);
    if (ret == ESP_OK) {
        // 修正:使用正确的字符串长度
        nvs_set_blob(nvs_handle1, NVS_SSID_KEY, "helloworld", strlen("helloworld") + 1);
        nvs_set_blob(nvs_handle1, NVS_PASSWORD_KEY, "87654321", strlen("87654321") + 1);
        nvs_commit(nvs_handle1);
        nvs_close(nvs_handle1);
        ESP_LOGI("NVS", "命名空间 %s 写入完成", NAME_SPACE_WIFI2);
    }

    vTaskDelay(pdMS_TO_TICKS(1000));

    ESP_LOGI("NVS", "=== 开始读取 NVS 数据 ===");

    // 读取并打印所有配置
    char read_buffer[64];
    
    // 读取命名空间1的SSID
    memset(read_buffer, 0, sizeof(read_buffer));
    if (nvs_blob_read(NAME_SPACE_WIFI1, NVS_SSID_KEY, read_buffer, sizeof(read_buffer)) == ESP_OK) {
        ESP_LOGI("NVS", "namespace:%s, key:%s -> value:%s", NAME_SPACE_WIFI1, NVS_SSID_KEY, read_buffer);
    }

    // 读取命名空间1的密码
    memset(read_buffer, 0, sizeof(read_buffer));
    if (nvs_blob_read(NAME_SPACE_WIFI1, NVS_PASSWORD_KEY, read_buffer, sizeof(read_buffer)) == ESP_OK) {
        ESP_LOGI("NVS", "namespace:%s, key:%s -> value:%s", NAME_SPACE_WIFI1, NVS_PASSWORD_KEY, read_buffer);
    }

    // 读取命名空间2的SSID
    memset(read_buffer, 0, sizeof(read_buffer));
    if (nvs_blob_read(NAME_SPACE_WIFI2, NVS_SSID_KEY, read_buffer, sizeof(read_buffer)) == ESP_OK) {
        ESP_LOGI("NVS", "namespace:%s, key:%s -> value:%s", NAME_SPACE_WIFI2, NVS_SSID_KEY, read_buffer);
    }

    // 读取命名空间2的密码
    memset(read_buffer, 0, sizeof(read_buffer));
    if (nvs_blob_read(NAME_SPACE_WIFI2, NVS_PASSWORD_KEY, read_buffer, sizeof(read_buffer)) == ESP_OK) {
        ESP_LOGI("NVS", "namespace:%s, key:%s -> value:%s", NAME_SPACE_WIFI2, NVS_PASSWORD_KEY, read_buffer);
    }

    ESP_LOGI("NVS", "=== 演示完成 ===");
}

3. VFS 与 SPIFFS 文件系统

 VFS(虚拟文件系统):
 提供统一接口,屏蔽底层文件系统差异
 通过 esp_vfs_register() 注册驱动
 支持多个挂载点,路径最长匹配

SPIFFS(SPI Flash File系统):
 轻量级文件系统,适用于 SPI NOR Flash
 不支持目录,路径为扁平结构
 使用前需在分区表中定义data, spiffs类型分区使用流程:

// 配置并挂载
esp_vfs_spiffs_conf_t conf = {
    .base_path = "/spiffs",
    .partition_label = NULL,
    .max_files = 5,
    .format_if_mount_failed = true
};
esp_vfs_spiffs_register(&conf);

// 使用标准 C 文件操作
FILE* f = fopen("/spiffs/hello.txt", "w");
fprintf(f, "Hello World!");
fclose(f);

// 卸载
esp_vfs_spiffs_unregister(conf.partition_label);

🧩 总结对比:

存储方式适用场景特点
分区表程序分区、OTA、自定义数据硬件划分,直接读写
NVS配置信息、键值对数据轻量、命名空间隔离
SPIFFS文件存储(如网页、日志)支持文件操作,挂载到 VFS

思维导图如下,仅供参考

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值