飞书文档https://x509p6c8to.feishu.cn/wiki/SXf5w6seIijVVskvic5cNT2wng4
目前,ESP-IDF 框架支持三种文件系统。
SPIFFS(SPI Flash File System)
- 简介:SPIFFS 是专门为 SPI NOR Flash 设备设计的轻量级文件系统,适用于资源受限的嵌入式系统,在 ESP32 开发中较为常用。
- 特点
- 轻量级:占用资源少,对内存和处理能力的要求较低,适合在资源有限的 ESP32 设备上运行。
- 磨损均衡:支持基本的磨损均衡功能,能够均匀地将数据写入 Flash 芯片的各个块,延长 Flash 的使用寿命。
- 简单易用:API 相对简单,易于集成到项目中,开发人员可以方便地进行文件的读写操作。
- 不支持大文件:由于其设计初衷是用于存储小文件,对于大文件的处理能力有限,且文件系统的最大容量也受到一定限制。
- 适用场景:适合存储配置文件、小图片、脚本等小文件,例如存储设备的配置信息、简单的网页文件等。
FATFS
- 简介:FATFS 是一个通用的 FAT 文件系统模块,遵循 ANSI C 标准,与平台无关。ESP-IDF 可以通过移植 FATFS 来支持 FAT12、FAT16 和 FAT32 文件系统。
- 特点
- 兼容性好:FAT 文件系统是一种广泛使用的文件系统格式,在各种操作系统(如 Windows、Linux 等)中都能很好地支持,方便与其他设备进行数据交互。
- 支持大文件:能够处理较大的文件和文件夹,适用于需要存储大文件的场景。
- 功能丰富:支持文件的创建、删除、读写、重命名等常见操作,还支持文件权限和目录结构。
- 资源占用较多:相比于 SPIFFS,FATFS 需要更多的内存和处理能力来运行,对系统资源有一定的要求。
- 适用场景:适合需要与其他设备共享数据、存储大文件的场景,例如存储多媒体文件(如音频、视频)、大型配置文件等。
LittleFS
- 简介:LittleFS 是一种专为嵌入式系统设计的日志结构文件系统,旨在提供高性能、可靠性和低资源占用。
- 特点
- 高性能:采用日志结构设计,具有较高的写入性能和随机访问性能,能够快速地进行文件的读写操作。
- 可靠性强:支持数据的原子写入和崩溃恢复功能,在系统异常断电或崩溃的情况下,能够保证数据的完整性和一致性。
- 磨损均衡:提供了更高级的磨损均衡算法,能够更有效地延长 Flash 芯片的使用寿命。
- 资源占用适中:相比于 FATFS,LittleFS 的资源占用相对较少,但比 SPIFFS 略高。
- 适用场景:适合对文件系统性能和可靠性要求较高的场景,例如存储实时数据、重要的配置文件等。
课程主要讲解 SPIFFS ,其它文件系统的使用都是类似的,很容易扩展。
SPIFFS 是一个用于 SPI NOR flash 设备的嵌入式文件系统,支持磨损均衡、文件系统一致性检查等功能。
SPIFFS 与 NVS 的区别
SPIFFS(Serial Peripheral Interface Flash File System)和 NVS(Non-Volatile Storage)都是 ESP32-S3 的存储系统,但它们有一些关键的区别。
NVS 在 SPI NOR flash 上实现了一个有容错性,和磨损均衡功能的键值对存储。
NVS 可以存储一些 PHY 初始化数据,也可以存储其他数据,一些断电存储的数据建议放在这里。
总的来说,SPIFFS 更适合用于存储文件,而 NVS 更适合用于存储键值对数据。具体使用哪种存储系统,取决于应用需求。
1.1**、创建分区表**
同理,因为文件系统需要用到Flash存储,所以也得进行分区表的创建
# 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,
storage, data, spiffs, , 0xF0000,
然后通过idf.py menuconfig配置自定义分区表

2.1**、注册** & 格式化文件系统
有了分区后,就可以进行文件系统的注册函数esp_vfs_spiffs_register
esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t *conf);
参数
const esp_vfs_spiffs_conf_t *conf:
指向 esp_vfs_spiffs_conf_t 结构体的指针,包含 SPIFFS 文件系统的配置信息。
返回值
esp_err_t
表示函数执行的结果,通常为以下几种情况:
ESP_OK: 成功初始化并挂载 SPIFFS 文件系统。
ESP_FAIL: 挂载或格式化失败。
ESP_ERR_NOT_FOUND: 未找到 SPIFFS 分区。
ESP_ERR_NO_MEM: 内存不足,无法初始化文件系统。
ESP_ERR_INVALID_ARG: 输入参数无效。
其他可能的错误代码,具体取决于底层实现。
esp_vfs_spiffs_conf_t 结构体用于配置 SPIFFS(SPI Flash File System)文件系统的参数。通过设置这些参数,可以控制 SPIFFS 文件系统的挂载行为、分区选择、最大同时打开的文件数以及挂载失败时的处理方式。
字段说明
base_path: 文件系统的挂载路径前缀。所有通过该文件系统访问的文件路径都将基于此路径。。
例如,如果 base_path 设置为 "/spiffs",则文件 hello.txt 的完整路径将是 "/spiffs/hello.txt"。
partition_label: 可选参数,指定要使用的 SPIFFS 分区的标签,如果有多个 SPIFFS 分区,可以通过设置 partition_label 来指定具体的分区。
如果不设置,则默认使用第一个子类型为 spiffs 的分区。
max_files: 最大同时打开的文件数。
format_if_mount_failed: 如果为 true,则在挂载失败时自动格式化文件系统。
说明: 如果挂载 SPIFFS 分区失败(例如分区损坏或未初始化),
设置此标志为 true 可以自动格式化分区,使其重新可用。
使用参考:
// 配置 SPIFFS 文件系统
esp_vfs_spiffs_conf_t conf = {
.base_path = "/spiffs", // 文件系统的挂载路径
.partition_label = NULL, // 使用默认的 SPIFFS 分区标签
.max_files = 5, // 最大同时打开的文件数
.format_if_mount_failed = true // 如果挂载失败,则格式化文件系统
};
// 使用上述配置初始化并挂载 SPIFFS 文件系统
esp_err_t ret = esp_vfs_spiffs_register(&conf);
// 处理挂载结果
if (ret != ESP_OK) {
if (ret == ESP_FAIL) {
ESP_LOGE(TAG, "Failed to mount or format filesystem"); // 挂载或格式化失败
} else if (ret == ESP_ERR_NOT_FOUND) {
ESP_LOGE(TAG, "Failed to find SPIFFS partition"); // 未找到 SPIFFS 分区
} else {
ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret)); // 初始化 SPIFFS 失败
}
return;
}
3.1**、获取文件系统的信息**
注册完成后,就可以使用esp_spiffs_info 函数用于获取指定 SPIFFS 分区的总大小和已使用的空间大小。
esp_err_t esp_spiffs_info(const char* partition_label, size_t* total, size_t* used);
参数
const char* partition_label:
SPIFFS 分区的标签。如果设置为 NULL,则使用默认的 SPIFFS 分区。
size_t* total:
用于存储 SPIFFS 分区的总大小(以字节为单位)。
size_t* used:
用于存储 SPIFFS 分区已使用的空间大小(以字节为单位)。
使用参考:
// 获取 SPIFFS 分区的信息
size_t total = 0, used = 0;
ret = esp_spiffs_info(conf.partition_label, &total, &used);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s). Formatting...", esp_err_to_name(ret));
// 获取分区信息失败,格式化
esp_spiffs_format(conf.partition_label);
return;
} else {
ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used); // 分区大小信息
}
4.1**、检查文件系统的完整性**
最后,调用esp_spiffs_check 函数用于检查 SPIFFS 文件系统的完整性。具体来说:
-
它会扫描指定的 SPIFFS 分区,查找并修复文件系统中的潜在问题。
-
这个函数可以帮助检测和修复由于突然断电、程序异常终止等原因导致的文件系统损坏。
-
检查过程包括验证文件系统结构、索引节点、数据块等的正确性。
使用参考
// 检查报告的分区大小信息的一致性
if (used > total) {
ESP_LOGW(TAG, "Number of used bytes cannot be larger than total. Performing SPIFFS_check()."); // 已使用字节数不能大于总字节数,执行 SPIFFS_check
//如果 ESP32-S3 在文件系统操作期间断电,可能会导致 SPIFFS 损坏。但是仍可通过 esp_spiffs_check 函数恢复文件系统。
ret = esp_spiffs_check(conf.partition_label);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "SPIFFS_check() failed (%s)", esp_err_to_name(ret)); // SPIFFS_check 失败
return;
} else {
ESP_LOGI(TAG, "SPIFFS_check() successful"); // SPIFFS_check 成功
}
}
5.1**、创建文件、读写操作**
如果到这里都没问题,我们就可以用c的文件操作函数进行文件的创建、写入、读取等操作了,最终代码如下:
#include <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_err.h"
#include "esp_log.h"
#include "esp_spiffs.h"
static const char *TAG = "example";
void app_main(void)
{
// 初始化 SPIFFS
ESP_LOGI(TAG, "Initializing SPIFFS");
// 配置 SPIFFS 文件系统
esp_vfs_spiffs_conf_t conf = {
.base_path = "/spiffs", // 文件系统的挂载路径
.partition_label = NULL, // 使用默认的 SPIFFS 分区标签
.max_files = 5, // 最大同时打开的文件数
.format_if_mount_failed = true // 如果挂载失败,则格式化文件系统
};
// 使用上述配置初始化并挂载 SPIFFS 文件系统
esp_err_t ret = esp_vfs_spiffs_register(&conf);
// 处理挂载结果
if (ret != ESP_OK) {
if (ret == ESP_FAIL) {
ESP_LOGE(TAG, "Failed to mount or format filesystem"); // 挂载或格式化失败
} else if (ret == ESP_ERR_NOT_FOUND) {
ESP_LOGE(TAG, "Failed to find SPIFFS partition"); // 未找到 SPIFFS 分区
} else {
ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret)); // 初始化 SPIFFS 失败
}
return;
}
// 获取 SPIFFS 分区的信息
size_t total = 0, used = 0;
ret = esp_spiffs_info(conf.partition_label, &total, &used);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s). Formatting...", esp_err_to_name(ret));
// 获取分区信息失败,格式化
esp_spiffs_format(conf.partition_label);
return;
} else {
ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used); // 分区大小信息
}
// 检查报告的分区大小信息的一致性
if (used > total) {
ESP_LOGW(TAG, "Number of used bytes cannot be larger than total. Performing SPIFFS_check()."); // 已使用字节数不能大于总字节数,执行 SPIFFS_check
//如果 ESP32-S3 在文件系统操作期间断电,可能会导致 SPIFFS 损坏。但是仍可通过 esp_spiffs_check 函数恢复文件系统。
ret = esp_spiffs_check(conf.partition_label);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "SPIFFS_check() failed (%s)", esp_err_to_name(ret)); // SPIFFS_check 失败
return;
} else {
ESP_LOGI(TAG, "SPIFFS_check() successful"); // SPIFFS_check 成功
}
}
// 使用 POSIX 和 C 标准库函数来处理文件
// 首先创建一个文件
ESP_LOGI(TAG, "Opening file"); // 打开文件
FILE* f = fopen("/spiffs/hello.txt", "w");
if (f == NULL) {
ESP_LOGE(TAG, "Failed to open file for writing"); // 打开文件失败
return;
}
fprintf(f, "Hello World!\n"); // 写入文件
fclose(f);
ESP_LOGI(TAG, "File written"); // 文件写入成功
// 打开文件进行读取
ESP_LOGI(TAG, "Reading file"); // 读取文件
f = fopen("/spiffs/hello.txt", "r");
if (f == NULL) {
ESP_LOGE(TAG, "Failed to open file for reading"); // 打开文件失败
return;
}
char line[64];
fgets(line, sizeof(line), f); // 读取文件内容
fclose(f);
// 去掉换行符
char* pos = strchr(line, '\n');
if (pos) {
*pos = '\0';
}
ESP_LOGI(TAG, "Read from file: '%s'", line); // 读取到的内容
// 完成所有操作,卸载分区并禁用 SPIFFS
esp_vfs_spiffs_unregister(conf.partition_label);
ESP_LOGI(TAG, "SPIFFS unmounted"); // SPIFFS 卸载成功
}
首次启动时,因为需要初始化挂载格式化文件系统,所以会阻塞10s左右,这是正常的

扩展:如何把本地文件烧录到板卡文件系统中?
spiffs_create_partition_image 是一个用于将文件系统中的文件打包成 SPIFFS 镜像并烧录到 ESP32 Flash中的命令,可以在程序烧录阶段一同将自定义的SPIFFS 文件镜像烧录到开发板。
使用方法很简单,例如,我在电脑创建一个txt文件夹,文件夹内部创建一个文件hello1.txt
demo/txt/hello1.txt
this is hello1 txt
然后在demo/main/CMakeLists.txt中,就可以添加spiffs_create_partition_image指令
指令参数如下
spiffs_create_partition_image(分区名 "path/to/files" FLASH_IN_PROJECT)
所以内容如下:
spiffs_create_partition_image(storage "../txt" FLASH_IN_PROJECT)
这句指令的意思是将工程下../txt 目录中的文件打包成 SPIFFS 镜像,并且。
最终用代码打开
#include <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_err.h"
#include "esp_log.h"
#include "esp_spiffs.h"
static const char *TAG = "example";
void app_main(void)
{
// 初始化 SPIFFS
ESP_LOGI(TAG, "Initializing SPIFFS");
// 配置 SPIFFS 文件系统
esp_vfs_spiffs_conf_t conf = {
.base_path = "/spiffs", // 文件系统的挂载路径
.partition_label = NULL, // 使用默认的 SPIFFS 分区标签
.max_files = 5, // 最大同时打开的文件数
.format_if_mount_failed = true // 如果挂载失败,则格式化文件系统
};
// 使用上述配置初始化并挂载 SPIFFS 文件系统
esp_err_t ret = esp_vfs_spiffs_register(&conf);
// 处理挂载结果
if (ret != ESP_OK) {
if (ret == ESP_FAIL) {
ESP_LOGE(TAG, "Failed to mount or format filesystem"); // 挂载或格式化失败
} else if (ret == ESP_ERR_NOT_FOUND) {
ESP_LOGE(TAG, "Failed to find SPIFFS partition"); // 未找到 SPIFFS 分区
} else {
ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret)); // 初始化 SPIFFS 失败
}
return;
}
// 获取 SPIFFS 分区的信息
size_t total = 0, used = 0;
ret = esp_spiffs_info(conf.partition_label, &total, &used);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s). Formatting...", esp_err_to_name(ret));
// 获取分区信息失败,格式化
esp_spiffs_format(conf.partition_label);
return;
} else {
ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used); // 分区大小信息
}
// 检查报告的分区大小信息的一致性
if (used > total) {
ESP_LOGW(TAG, "Number of used bytes cannot be larger than total. Performing SPIFFS_check()."); // 已使用字节数不能大于总字节数,执行 SPIFFS_check
//如果 ESP32-S3 在文件系统操作期间断电,可能会导致 SPIFFS 损坏。但是仍可通过 esp_spiffs_check 函数恢复文件系统。
ret = esp_spiffs_check(conf.partition_label);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "SPIFFS_check() failed (%s)", esp_err_to_name(ret)); // SPIFFS_check 失败
return;
} else {
ESP_LOGI(TAG, "SPIFFS_check() successful"); // SPIFFS_check 成功
}
}
// 打开文件进行读取
ESP_LOGI(TAG, "Reading file"); // 读取文件
FILE* f = fopen("/spiffs/hello1.txt", "r");
if (f == NULL) {
ESP_LOGE(TAG, "Failed to open file for reading"); // 打开文件失败
return;
}
char line[64];
fgets(line, sizeof(line), f); // 读取文件内容
fclose(f);
// 去掉换行符
char* pos = strchr(line, '\n');
if (pos) {
*pos = '\0';
}
ESP_LOGI(TAG, "Read from file: '%s'", line); // 读取到的内容
// 完成所有操作,卸载分区并禁用 SPIFFS
esp_vfs_spiffs_unregister(conf.partition_label);
ESP_LOGI(TAG, "SPIFFS unmounted"); // SPIFFS 卸载成功
}