ESP32项目实战:在PlatformIO中自定义分区表以优化Flash空间
1. 为什么需要自定义分区表?
第一次用ESP32开发项目时,我也遇到过编译后程序空间不足的问题。明明代码量不大,却总是提示"No space left on device"。后来发现,这其实是ESP32默认分区表分配的空间太小导致的。ESP32的Flash存储空间就像一块大蛋糕,分区表就是切蛋糕的刀,决定了每块蛋糕的大小和用途。
官方默认的分区表为了兼容各种使用场景,给应用程序(APP)分配的空间往往比较保守。比如4MB Flash的ESP32-WROOM-32模块,默认只给APP分区分配1.25MB空间。如果你的项目用到了大量库文件或者需要存储资源,这点空间根本不够用。这时候就需要自定义分区表,重新分配Flash空间。
2. 理解ESP32分区表结构
2.1 分区表的基本组成
ESP32的分区表是一个CSV格式的文件,定义了Flash中各个区域的用途。每个分区包含6个关键信息:
- Name:分区名称,如nvs、otadata等
- Type:分区类型,主要有app(应用程序)、data(数据)两种
- SubType:子类型,进一步说明分区用途
- Offset:分区起始地址(16进制)
- Size:分区大小(16进制)
- Flags:特殊标志,如加密等
举个例子,这是8MB Flash的默认分区表片段:
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x330000,
2.2 常见分区的作用
- nvs:存储设备配置参数,如Wi-Fi密码
- otadata:OTA升级时记录当前使用的固件分区
- app0/app1:两个OTA固件分区,可以交替升级
- spiffs:文件系统分区,存放网页、配置文件等
- coredump:崩溃时保存错误信息
3. 在PlatformIO中创建自定义分区表
3.1 新建分区表文件
首先在项目根目录下创建一个CSV文件,比如custom_partitions.csv。可以直接复制默认分区表内容作为起点。PlatformIO默认的分区表文件位于:
~/.platformio/packages/framework-arduinoespressif32/tools/partitions
建议先备份原始分区表,再开始修改。我习惯把分区表文件放在项目的/data目录下,方便管理。
3.2 调整分区大小
重点来了!我们需要重新分配APP分区的大小。假设我们用的是8MB Flash的ESP32:
原始配置:
app0, app, ota_0, 0x10000, 0x330000, # 3.1875MB
修改后:
app0, app, ota_0, 0x10000, 0x500000, # 5MB
这里把APP分区从3.1875MB扩大到了5MB。注意要确保:
- 各分区的Offset不能重叠
- 总大小不超过Flash容量
- 保留必要的系统分区(nvs、otadata)
3.3 计算分区地址的技巧
计算分区地址时,我推荐使用这个公式:
下一个分区的Offset = 当前分区的Offset + 当前分区的Size
比如:
app0, app, ota_0, 0x10000, 0x500000,
spiffs, data, spiffs, 0x510000, 0x2E0000,
这里spiffs的Offset就是0x10000 + 0x500000 = 0x510000。
4. 在PlatformIO中配置自定义分区表
4.1 修改platformio.ini
在项目的platformio.ini文件中添加:
[env:esp32dev]
platform = espressif32
board = esp32dev
board_build.partitions = data/custom_partitions.csv
4.2 验证分区表
编译前,建议先用这个命令检查分区表是否有效:
pio run -t partition-table
如果看到类似这样的输出,说明分区表配置正确:
Checking size partitions.csv
Partition table verified.
4.3 常见错误排查
- 分区重叠:检查各分区的Offset和Size是否冲突
- 超出Flash容量:确保所有分区Size总和不超过芯片规格
- 缺少必要分区:nvs和otadata分区必须保留
- 格式错误:CSV文件不能有多余的空格或符号
5. 实战案例:优化OTA项目分区
最近做一个智能家居项目时,我需要同时支持OTA升级和文件系统。硬件是16MB Flash的ESP32-S3,这是最终的分区表配置:
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
otadata, data, ota, 0xf000, 0x2000,
app0, app, ota_0, 0x10000, 0x300000,
app1, app, ota_1, 0x310000, 0x300000,
spiffs, data, spiffs, 0x610000, 0x9E0000,
这个配置:
- 给每个OTA分区分配了3MB空间
- 文件系统分区高达9.875MB
- 保留了必要的系统分区
编译后,可用程序空间从默认的1.2MB提升到了3MB,完美解决了之前频繁出现的空间不足问题。
6. 高级技巧与注意事项
6.1 优化nvs分区大小
nvs分区默认是20KB(0x5000),但实际项目中:
- 简单设备:16KB足够
- 复杂配置:可能需要32KB
建议根据实际存储的参数数量调整。
6.2 处理特殊需求
-
不需要OTA:可以只保留一个app分区,节省空间
factory, app, factory, 0x10000, 0x300000, -
需要更多文件空间:可以适当减小app分区,增大spiffs
-
安全需求:可以添加加密标志
storage, data, nvs, 0x9000, 0x6000, encrypted
6.3 性能优化建议
- 将频繁访问的数据放在Flash前部(地址小的区域)
- 对齐分区Offset到4KB边界(0x1000的倍数)
- 大文件尽量放在连续的空间
7. 调试与验证
修改分区表后,可以通过以下方式验证:
-
查看编译日志中的分区信息
-
在代码中打印分区信息:
#include "esp_partition.h" void print_partitions() { esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL); while (it != NULL) { const esp_partition_t *p = esp_partition_get(it); printf("Type=%02x, SubType=%02x, Address=0x%06x, Size=0x%06x, Label=%s\n", p->type, p->subtype, p->address, p->size, p->label); it = esp_partition_next(it); } } -
使用esptool查看实际烧录情况:
esptool.py read_flash 0x8000 0xc00 partition_table.bin
记得在实际硬件上测试所有功能,特别是OTA升级和文件系统操作。我在一个项目中就遇到过spiffs分区设置过大导致写入速度变慢的问题,后来调整为4MB后性能明显改善。
更多推荐



所有评论(0)