当前位置: 首页 > backend >正文

为 ESP32 解锁跨平台存储识别能力:支持 FAT、NTFS、EXT4、APFS 的轻量级 BlockFS 组件

  • 📦 blockfs
  • ✅ 支持功能
  • 📐 接口定义
    • blockfs_reader_t
    • blockfs_detect()
      • 参数说明:
  • 📌 示例用法
    • ✅ 使用 SDMMC 卡(C 示例)
    • ✅ 使用 USB SCSI MSC 设备(C 示例)
  • 📁 文件结构
  • 🚀 完整代码
    • 📁 blockfs.h
    • 📁 blockfs.c
    • 📁 CMakeLists.txt
  • 🧩 集成方式(CMake)
  • 📝 文件系统类型定义
  • 📣 注意事项
  • 🧪 测试平台
  • 📜 License

APFS

📦 blockfs

blockfs 是一个轻量级文件系统识别组件,适用于 ESP32 平台,用于探测连接的块设备(如 SD 卡 或 USB 存储设备)上的文件系统类型(如 FAT32、NTFS、EXT4、APFS 等)。

组件采用 纯 C 接口,可直接集成到基于 main.c 的项目中,不依赖 C++ 调用。


✅ 支持功能

  • 自动识别以下文件系统类型:

    • FAT16 / FAT32
    • NTFS
    • EXT4
    • APFS
  • 支持以下块设备来源:

    • 📀 SDMMC(原生 SD 卡接口)
    • 💽 USB SCSI(通过 TinyUSB MSC 接口)

📐 接口定义

blockfs_reader_t

统一的块设备读取抽象:

typedef struct {void* context;esp_err_t (*read)(void* context, void* buffer, uint32_t sector, uint32_t count);uint32_t sector_size;
} blockfs_reader_t;

blockfs_detect()

核心函数,用于识别设备上的文件系统类型:

esp_err_t blockfs_detect(const blockfs_reader_t* reader, blockfs_type_t* out_type);
参数说明:
  • reader:你构造的读取器对象,封装底层块设备
  • out_type:输出文件系统类型(BLOCKFS_FAT / BLOCKFS_EXT4 等)

📌 示例用法

✅ 使用 SDMMC 卡(C 示例)

#include "blockfs.h"
#include "sdmmc_cmd.h"
#include "driver/sdmmc_host.h"static esp_err_t sdmmc_read_cb(void* ctx, void* buffer, uint32_t sector, uint32_t count) {return sdmmc_read_sectors((sdmmc_card_t*)ctx, buffer, sector, count);
}static const char* fs_type_str(blockfs_type_t type) {switch (type) {case BLOCKFS_FAT: return "FAT";case BLOCKFS_NTFS: return "NTFS";case BLOCKFS_EXT4: return "EXT4";case BLOCKFS_APFS: return "APFS";default: return "Unknown";}
}void detect_sdcard(sdmmc_card_t* card) {blockfs_reader_t reader = {.context = card,.read = sdmmc_read_cb,.sector_size = 512};blockfs_type_t type;if (blockfs_detect(&reader, &type) == ESP_OK) {printf("Filesystem: %s\n", fs_type_str(type));}
}

✅ 使用 USB SCSI MSC 设备(C 示例)

#include "blockfs.h"
#include "esp_private/msc_scsi_bot.h"static esp_err_t scsi_read_cb(void* ctx, void* buffer, uint32_t sector, uint32_t count) {msc_host_device_handle_t dev = (msc_host_device_handle_t)ctx;return scsi_cmd_read10(dev, buffer, sector, count, 512); // sector_size 传入
}static const char* fs_type_str(blockfs_type_t type) {switch (type) {case BLOCKFS_FAT: return "FAT";case BLOCKFS_NTFS: return "NTFS";case BLOCKFS_EXT4: return "EXT4";case BLOCKFS_APFS: return "APFS";default: return "Unknown";}
}void detect_usb_device(msc_host_device_handle_t dev) {blockfs_reader_t reader = {.context = dev,.read = scsi_read_cb,.sector_size = 512};blockfs_type_t type;if (blockfs_detect(&reader, &type) == ESP_OK) {printf("Filesystem: %s\n", fs_type_str(type));}
}

📁 文件结构

blockfs/
├── include/
│   └── blockfs.h              // C 接口头文件
├── src/
│   └── blockfs.c              // 文件系统检测逻辑(C)
├── CMakeLists.txt

🚀 完整代码

📁 blockfs.h

#pragma once#include "esp_err.h"
#include <stdint.h>
#include <stddef.h>#ifdef __cplusplus
extern "C" {
#endiftypedef enum {BLOCKFS_UNKNOWN = 0,BLOCKFS_FAT,BLOCKFS_NTFS,BLOCKFS_EXT4,BLOCKFS_APFS
} blockfs_type_t;// C 风格块设备读取接口
typedef struct {void* context;esp_err_t (*read)(void* context, void* buffer, uint32_t sector, uint32_t count);uint32_t sector_size;
} blockfs_reader_t;// 文件系统识别函数
esp_err_t blockfs_detect(const blockfs_reader_t* reader, blockfs_type_t* out_type);#ifdef __cplusplus
}
#endif

📁 blockfs.c

#include "blockfs.h"
#include <string.h>
#include <stdbool.h>esp_err_t blockfs_detect(const blockfs_reader_t *reader, blockfs_type_t *out_type)
{if (!reader || !reader->read || !out_type){return ESP_ERR_INVALID_ARG;}uint8_t buffer[4096];const uint32_t sec_size = reader->sector_size;// 读取 LBA 0if (reader->read(reader->context, buffer, 0, 1) != ESP_OK){return ESP_FAIL;}bool is_mbr = (buffer[510] == 0x55 && buffer[511] == 0xAA);uint32_t fs_start = 0;if (is_mbr){uint8_t *entry = buffer + 446;if (entry[4] == 0xEE){// GPT: 检查 LBA 1 是否是 "EFI PART"if (reader->read(reader->context, buffer, 1, 1) != ESP_OK)return ESP_FAIL;if (memcmp(buffer, "EFI PART", 8) != 0)return ESP_FAIL;// 读取 GPT 第一个分区项(LBA 2)if (reader->read(reader->context, buffer, 2, 1) != ESP_OK)return ESP_FAIL;const uint8_t apfs_guid[16] = {0xEF, 0x57, 0x34, 0x7C,0x00, 0x00,0xAA, 0x11,0xAA, 0x11,0x00, 0x30, 0x65, 0x43, 0xEC, 0xAC};// 比较 GPT 分区类型 GUID(前16字节)if (memcmp(buffer, apfs_guid, 16) == 0){*out_type = BLOCKFS_APFS;return ESP_OK;}// 如果不是 APFS,继续取分区起始位置(GPT 分区项中的 First LBA)fs_start = *(uint64_t *)(buffer + 32);}else{fs_start = *(uint32_t *)(entry + 8); // MBR 第一个分区的起始 LBA}}else{fs_start = 0; // 无分区,直接是文件系统}// 读取文件系统头if (reader->read(reader->context, buffer, fs_start, 8) != ESP_OK){return ESP_FAIL;}// 检测 FATif (memcmp(buffer + 0x52, "FAT32", 5) == 0 || memcmp(buffer + 0x36, "FAT16", 5) == 0){*out_type = BLOCKFS_FAT;return ESP_OK;}// 检测 NTFSif (memcmp(buffer + 3, "NTFS", 4) == 0){*out_type = BLOCKFS_NTFS;return ESP_OK;}// 检测 EXT4(超级块在 1080 字节处)uint32_t ext4_offset = 1080 % sec_size;uint32_t ext4_sector = fs_start + (1080 / sec_size);if (reader->read(reader->context, buffer, ext4_sector, 1) == ESP_OK){if (buffer[ext4_offset] == 0x53 && buffer[ext4_offset + 1] == 0xEF){*out_type = BLOCKFS_EXT4;return ESP_OK;}}*out_type = BLOCKFS_UNKNOWN;return ESP_OK;
}

📁 CMakeLists.txt

idf_component_register(SRCS "src/blockfs.c"INCLUDE_DIRS "include"REQUIRES sdmmc usb_host_msc # 可选
)

🧩 集成方式(CMake)

在你的主工程 CMakeLists.txt 添加:

set(EXTRA_COMPONENT_DIRS path/to/blockfs)

或将 blockfs 放入 components/ 目录,自动扫描。


📝 文件系统类型定义

typedef enum {BLOCKFS_UNKNOWN = 0,BLOCKFS_FAT,BLOCKFS_NTFS,BLOCKFS_EXT4,BLOCKFS_APFS
} blockfs_type_t;

📣 注意事项

  • 不挂载文件系统blockfs 仅识别类型,不挂载。挂载请使用 ESP-IDF 的 esp_vfs_fat 或 ext4/littlefs 等组件。
  • 分区支持:自动识别 MBR / GPT 分区,提取第一个有效分区进行分析。
  • 需 512 字节扇区设备:部分设备可能使用其他扇区大小(需确认传入正确 sector_size

🧪 测试平台

  • ESP32 / ESP32-S3 / ESP32-P4
  • SDMMC 卡、USB MSC 存储
  • FAT32/EXT4/NTFS/APFS U 盘和 TF 卡

📜 License

MIT License

http://www.xdnf.cn/news/12355.html

相关文章:

  • Java中的阻塞队列
  • [arthas]arthas安装使用
  • NVM!(可以快速替换你的node版本)
  • Mysql主从复制原理分析
  • 高性能分布式消息队列系统(三)
  • CVE-2020-17518源码分析与漏洞复现(Flink 路径遍历)
  • AtCoder 第408​场初级竞赛 A~E题解
  • 强化学习入门:Gym实现CartPole随机智能体
  • VBA信息获取与处理专题五第一节:利用CDO发送简单邮件
  • AirSim/Cosys-AirSim 游戏开发(二)使用自定义场景
  • Python训练营---Day45
  • DeepSeek 农业大模型:应用潜力与数据隐私保护的双重考量
  • Python训练营---Day44
  • MySQL常用知识总结
  • 三分算法与DeepSeek辅助证明是单峰函数
  • 学习路之PHP--webman安装及使用、webman/admin安装
  • OpenLayers 地图投影转换
  • 视频监控EasyCVR3.7.2版本支持更改播放器默认解码方式,该如何进行配置?
  • 组合与排列
  • 湖北理元理律所债务优化实践:法律技术与人文关怀的双轨服务
  • 【LC#39270】判断子序列爬楼梯(dp算法 第一期)
  • 面向开发者的提示词工程③——文本总结(Summarizing)
  • [蓝桥杯]序列计数
  • 26考研|数学分析:多元函数极限与连续
  • 面试总结。
  • 数据迁移是什么?数据迁移过程中
  • 细说STM32单片机FreeRTOS空闲任务及低功耗的设计方法及应用
  • Java(io)字节流
  • Java应用10(客户端与服务器通信)
  • App使用webview套壳引入h5(一)—— webview和打开的h5进行通信传参