Files
ESP32-Guide/docs/04.外设学习/4.2-存储外设/4.2.4-SPIFFS文件系统/SPIFFS文件系统.md
2025-02-23 11:12:52 +08:00

249 lines
8.6 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ESP32存储-SPIFFS文件系统
> [!TIP] 🚀 ESP32 存储-SPIFFS文件系统 | 让存储更高效!
> - 💡 **碎碎念**😎:本节将介绍 ESP32 的 SPIFFS 文件系统,帮助你在闪存中管理文件,实现高效的文件存储与读取。
> - 📺 **视频教程**:🚧 *开发中*
> - 💾 **示例代码**[ESP32-Guide/code/04.peripheral/storage/flash_spiffs](https://github.com/DuRuofu/ESP32-Guide/tree/main/code/04.peripheral/storage/flash_spiffs)
## 一、介绍
关于[esp32](https://so.csdn.net/so/search?q=esp32&spm=1001.2101.3001.7020)的spiffs文件系统的详细内容请看esp32官网的介绍[espidf编程指南-spiffs](https://docs.espressif.com/projects/esp-idf/zh_CN/release-v4.1/api-reference/storage/spiffs.html)
- SPIFFS是一种专门为像ESP32/8266这种嵌入式设备而开发的微型文件系统SPIFFS是设置在Flash中的一块空间可以用来存放如网页、图片等体积小的静态资源而无需采用存储于外部SD卡的方式。
- SPIFFS中的数据不会因为断电而丢失
对于spiffs乐鑫提供了很好的支持专门提供了工具spiffsgen.pymkspiffs用于对实现spiffs 在esp32 上的创建、格式化等操作。
注意:
>1. 目前SPIFFS 尚不支持目录,但可以生成扁平结构。如果 SPIFFS 挂载在 `/spiffs` 下,在 `/spiffs/tmp/myfile.txt` 路径下创建一个文件则会在 SPIFFS 中生成一个名为 `/tmp/myfile.txt` 的文件,而不是在 `/spiffs/tmp` 下生成名为 `myfile.txt` 的文件;
>2. SPIFFS 并非实时栈,每次写操作耗时不等;
>3. 目前SPIFFS 尚不支持检测或处理已损坏的块。
>4. SPIFFS 只能稳定地使用约 75% 的指定分区容量。
>5. 当文件系统空间不足时,垃圾收集器会尝试多次扫描文件系统来寻找可用空间。根据所需空间的不同,写操作会被调用多次,每次函数调用将花费几秒
>6. 被删除文件通常不会被完全清除,会在文件系统中遗留下无法使用的部分
>7. 如果 ESP32 在文件系统操作期间断电,可能会导致 SPIFFS 损坏。但是仍可通过 `esp_spiffs_check` 函数恢复文件系统。
## 二、SPIFFS文件系统使用
##### 2.1 新建工程,准备自定义分区表
在分区表里添加 soiffs 文件系统的分区
```c
# ESP-IDF Partition Table
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x4000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
storage, data, spiffs, , 0xF0000,
```
选择自定义分区表:
![](attachments/1.png)
##### 2.2 建立soiffs镜像
在项目根目录建立soiffs镜像文件夹`spiffs_image`
在项目mian组件cmarklist文件添加镜像构建工具相关代码
`spiffs_create_partition_image(my_spiffs_partition my_folder FLASH_IN_PROJECT)`
``` c
idf_component_register(SRCS "flash_spiffs.c"
                    INCLUDE_DIRS ".")
# Create a SPIFFS image from the contents of the 'spiffs_image' directory
# that fits the partition named 'storage'. FLASH_IN_PROJECT indicates that
# the generated image should be flashed when the entire project is flashed to
# the target with 'idf.py -p PORT flash'.
spiffs_create_partition_image(storage ../spiffs_image FLASH_IN_PROJECT)
```
##### 2.3 码里挂载文件系统
然后在代码里挂载文件系统即可:
``` c
ESP_LOGI(TAG, "Initializing SPIFFS");
esp_vfs_spiffs_conf_t conf = {
.base_path = "/spiffs",
.partition_label = NULL,
.max_files = 5,
.format_if_mount_failed = false
};
// Use settings defined above to initialize and mount SPIFFS filesystem.
// Note: esp_vfs_spiffs_register is an all-in-one convenience function.
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");
} else {
ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
}
return;
}
```
`esp_vfs_spiffs_register`用于将SPIFFS注册并挂载到虚拟文件系统VFS并指定路径前缀。
函数参数中的conf是一个指向`esp_vfs_spiffs_conf_t`配置结构体的指针。
`esp_vfs_spiffs_conf_t`参数:
- **base_path:** 文件系统关联的文件路径前缀。
- **partition_label:** 可选项,要使用的 SPIFFS 分区的标签。如果设置为 NULL则将使用 subtype=spiffs 的第一个分区。
- **max_files:** 可同时打开的最大文件数。
- **format_if_mount_failed:** 如果为 true在挂载失败时将格式化文件系统。
## 三、案例
下面的示例程序来自https://github.com/espressif/esp-idf/tree/v5.2.1/examples/storage/spiffsgen
主要功能包括初始化 SPIFFS、读取文件内容以及计算文件的 MD5 哈希值。
在 `app_main` 函数中,首先初始化 SPIFFS 文件系统,然后获取文件系统的信息,接着依次调用 `read_hello_txt` 和 `compute_alice_txt_md5` 函数来读取 `hello.txt` 文件的内容和计算 `alice.txt` 文件的 MD5 哈希值。最后,取消挂载 SPIFFS 文件系统并结束程序。
``` 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"
#include "mbedtls/md5.h"
static const char *TAG = "example";
static void read_hello_txt(void)
{
ESP_LOGI(TAG, "Reading hello.txt");
// Open for reading hello.txt
FILE* f = fopen("/spiffs/hello.txt", "r");
if (f == NULL) {
ESP_LOGE(TAG, "Failed to open hello.txt");
return;
}
char buf[64];
memset(buf, 0, sizeof(buf));
fread(buf, 1, sizeof(buf), f);
fclose(f);
// Display the read contents from the file
ESP_LOGI(TAG, "Read from hello.txt: %s", buf);
}
static void compute_alice_txt_md5(void)
{
ESP_LOGI(TAG, "Computing alice.txt MD5 hash");
// The file alice.txt lives under a subdirectory, though SPIFFS itself is flat
FILE* f = fopen("/spiffs/sub/alice.txt", "r");
if (f == NULL) {
ESP_LOGE(TAG, "Failed to open alice.txt");
return;
}
// Read file and compute the digest chunk by chunk
#define MD5_MAX_LEN 16
char buf[64];
mbedtls_md5_context ctx;
unsigned char digest[MD5_MAX_LEN];
mbedtls_md5_init(&ctx);
mbedtls_md5_starts(&ctx);
size_t read;
do {
read = fread((void*) buf, 1, sizeof(buf), f);
mbedtls_md5_update(&ctx, (unsigned const char*) buf, read);
} while(read == sizeof(buf));
mbedtls_md5_finish(&ctx, digest);
// Create a string of the digest
char digest_str[MD5_MAX_LEN * 2];
for (int i = 0; i < MD5_MAX_LEN; i++) {
sprintf(&digest_str[i * 2], "%02x", (unsigned int)digest[i]);
}
// For reference, MD5 should be deeb71f585cbb3ae5f7976d5127faf2a
ESP_LOGI(TAG, "Computed MD5 hash of alice.txt: %s", digest_str);
fclose(f);
}
void app_main(void)
{
ESP_LOGI(TAG, "Initializing SPIFFS");
esp_vfs_spiffs_conf_t conf = {
.base_path = "/spiffs",
.partition_label = NULL,
.max_files = 5,
.format_if_mount_failed = false
};
// Use settings defined above to initialize and mount SPIFFS filesystem.
// Note: esp_vfs_spiffs_register is an all-in-one convenience function.
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");
} else {
ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
}
return;
}
size_t total = 0, used = 0;
ret = esp_spiffs_info(NULL, &total, &used);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret));
} else {
ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
}
/* The following calls demonstrate reading files from the generated SPIFFS
* image. The images should contain the same files and contents as the spiffs_image directory.
*/
// Read and display the contents of a small text file (hello.txt)
read_hello_txt();
// Compute and display the MD5 hash of a large text file (alice.txt)
compute_alice_txt_md5();
// All done, unmount partition and disable SPIFFS
esp_vfs_spiffs_unregister(NULL);
ESP_LOGI(TAG, "SPIFFS unmounted");
}
```
效果:
![](attachments/2.png)
# 参考链接
1. https://docs.espressif.com/projects/esp-idf/zh_CN/release-v5.2/esp32/api-reference/storage/spiffs.html