-
Notifications
You must be signed in to change notification settings - Fork 3
PartitionsAndFlashLayout
The flash partition table determines where firmware, configuration, OTA images and the filesystem live on the device. Raft projects ship a
partitions.csvper SysType so each board variant can size partitions to fit its flash chip and feature set.
ESP-IDF reads the partition table from a CSV file at build time, lays each partition out at fixed offsets, and embeds the table in flash so the bootloader and OTA system can find them at runtime. Raft makes two contributions to this:
-
RaftProject.cmakecopies the SysType'spartitions.csvinto the build directory, so each variant can have its own table without changes elsewhere. - The build sets
CONFIG_PARTITION_TABLE_CUSTOM=yandCONFIG_PARTITION_TABLE_CUSTOM_FILENAMEinsdkconfig.defaultsto point at the copy.
This page covers the partition layouts Raft uses and the trade-offs between them.
The default Raft project ships a layout designed for in-the-field OTA updates and a filesystem for Web UI assets and runtime data:
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x009000, 0x015000, # 84 KB Non-volatile storage
otadata, data, ota, 0x01e000, 0x002000, # 8 KB OTA boot selector
app0, app, ota_0, 0x020000, 0x1b0000, # 1.6875 MB App slot 0
app1, app, ota_1, 0x1d0000, 0x1b0000, # 1.6875 MB App slot 1
fs, data, 0x83, 0x380000, 0x080000, # 512 KB LittleFS / Web UI
Total: 4 MB flash. Two equal-sized app partitions plus a small otadata are required by esp_ota — see OTA Update Flow. nvs is used by RaftJson for runtime-mutable configuration. fs (subtype 0x83 = LittleFS) holds the bundled Web UI and any data files placed under FS_IMAGE_PATH.
This layout is the right starting point for any device that:
- Has 4 MB or more of flash.
- Will receive OTA firmware updates after manufacture.
- Serves a Web UI or bundles data files.
For 8 MB or 16 MB flash chips, simply enlarge the app partitions and/or fs. Keep app0 and app1 the same size; the layout is otherwise insensitive to flash size.
Some examples (e.g. RaftWebServer/examples/basic/) use a non-OTA layout:
nvs, data, nvs, 0x9000, 0x6000, # 24 KB
phy_init, data, phy, 0xf000, 0x1000, # 4 KB
fs, data, spiffs, 0x10000, 0xF0000, # 960 KB
factory, app, factory, 0x100000, 2M, # 2 MB
This is acceptable for tethered or factory-flashed-only devices. It frees up the second app partition (saving ~1.7 MB) at the cost of being unable to OTA-update — ESPOTAUpdate will refuse the update because there is no ota_0/ota_1 to swap into. Pick this only if you are sure OTA will never be needed.
A few hard rules apply to every Raft partition table:
-
Offsets must be 4 KB-aligned (0x1000) for
dataand 64 KB-aligned (0x10000) forapppartitions. - The first partition must start at or after 0x9000 — the bootloader and partition table itself live below that. ESP-IDF will warn if you start
nvsearlier. -
app0andapp1(when present) must be the same size; OTA writes whichever is currently inactive and will refuse if the new image does not fit. - The
otadatapartition is mandatory if anyota_xpartition exists. It is exactly 0x2000 (8 KB) — it stores two redundant copies of the boot selector. - The
fspartition's subtype tells the firmware which FS to mount:spiffsfor SPIFFS,0x83for LittleFS. Match the value ofFS_TYPEin your SysType'sfeatures.cmake(see WebUI Build Pipeline).
Rules of thumb:
| Partition | Typical size | When to grow |
|---|---|---|
nvs |
24–84 KB | Increase when storing many runtime-mutable config keys, paired BLE bond data, or large key/value blobs. 84 KB suits most apps; 24 KB is the bootloader minimum. |
otadata |
8 KB | Fixed by ESP-IDF. |
app0 / app1
|
1.5 MB–3 MB each | Increase when adding many SysMods, BLE + WiFi simultaneously, large ML/DSP libraries, or -Og debug builds. 1.6875 MB is enough for typical Raft apps with WebUI handling code. Halve flash usage with -Os and CONFIG_COMPILER_OPTIMIZATION_SIZE=y. |
fs |
256 KB–4 MB | Drives Web UI bundle size + any data files in FS_IMAGE_PATH. 512 KB fits a parcel-built React + MUI starter. Audio / image assets push this up quickly. |
When a build runs out of room you will see one of:
-
Error: app partition is too small for binary— the linked firmware exceeds theota_0size. -
LittleFS image creation failed: too large— the FS image bigger than thefspartition. -
Partition table … unaligned— the offsets and sizes do not line up.
For each, edit partitions.csv to grow the affected partition (and shrink another, since total ≤ flash size).
Each SysType has its own partitions.csv next to its sdkconfig.defaults:
systypes/RaftI2CBox/
├── features.cmake
├── partitions.csv ← copied by RaftProject.cmake
├── sdkconfig.defaults ← references the file via CONFIG_PARTITION_TABLE_CUSTOM_FILENAME
└── SysTypes.json
sdkconfig.defaults for the OTA-capable layout typically contains:
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="systypes/<SYSTYPE>/partitions.csv"
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y # optional, see "Rollback" belowRaftProject.cmake then runs:
add_custom_command(
OUTPUT ${_partitions_csv_file}
COMMAND ${CMAKE_COMMAND} -E copy "${BUILD_CONFIG_DIR}/partitions.csv" ${_partitions_csv_file}
DEPENDS "${BUILD_CONFIG_DIR}/partitions.csv"
)— so editing partitions.csv triggers a rebuild and the new table is flashed on the next idf.py flash.
The first OTA image in flash is always picked from the factory partition if one exists; otherwise the bootloader chooses ota_0. After a successful OTA write, esp_ota_set_boot_partition() flips the next-boot pointer in otadata to the inactive slot. The mechanics live in OTA Update Flow; the relevant point for the partition table is just that both ota_0 and ota_1 must exist and be the same size for OTA to work at all.
ESP-IDF supports a "pending verify" boot mode where a freshly-OTA'd image must mark itself valid within a configured window or the bootloader rolls back to the previous slot. Enable with:
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=yThen call esp_ota_mark_app_valid_cancel_rollback() from the application after sufficient self-test passes. Raft's ESPOTAUpdate does not call this for you — the new image is unconditionally marked bootable on a clean esp_ota_end(). Apps that want rollback need to either implement the check themselves or set CONFIG_BOOTLOADER_APP_ANTI_ROLLBACK=y for stronger version-monotonic guarantees.
For products that handle PII, keys or BLE bond data, consider enabling NVS encryption:
CONFIG_NVS_ENCRYPTION=y
CONFIG_NVS_ENC_KEY_SOURCE_FROM_FLASH_PARTITION=yThis requires an additional nvs_keys partition. Add to partitions.csv (sample):
nvs_keys, data, nvs_keys, 0x01a000, 0x004000, encrypted
See the NVS Encryption guide. RaftJson's NVS reads/writes are unaffected — the encryption is transparent.
espefuse.py summary and idf.py partition-table show the active table. At runtime, esp_partition_find_first() and the SysManager status REST API report partition usage. The boot log prints the chosen app partition on every boot:
I (492) boot: Loaded app from partition at offset 0x20000
-
OTA Update Flow — what
app0/app1/otadataare used for. -
WebUI Build Pipeline — what fills the
fspartition. -
Configuration and RaftJsonAdvanced — what
nvsis used for. -
SysTypes — where
partitions.csvandsdkconfig.defaultslive. - ESP-IDF Partition Tables — upstream reference.
Getting Started
- Quick Start
- Architecture at a Glance
- Writing Your First SysMod
- Adding a Comms Channel
- Adding an I2C Device Type
- PlatformIO / Arduino
Scaffolding & Building
- Raft CLI
- SysTypes
- Top-Level SysType
- Build Process
- WebUI Build Pipeline
- File System
- Partitions & Flash
- Local Dev Libraries
- Library Developer Guide
Architecture
Built-in SysMods
- NetworkManager
- BLEManager
- WebServer
- MQTTManager
- SerialConsole
- CommandSerial
- CommandSocket
- CommandFile
- FileManager
- LogManager
- ESPOTAUpdate
- StatePublisher
- Remote Logging
- Data Source Registration
Comms & Protocols
- Stack Overview
- Comms Channels
- ProtocolExchange
- RICREST Protocol
- Real-Time Streams
- Adding REST Endpoints
- Built-in REST Endpoints
- File Download (OKTO)
- OTA Update Flow
Devices & Buses
- DeviceManager
- Device Manager REST API
- Device Factory & Classes
- Device Type Records
- Adding an I2C Device Type
- Device Data Publishing
- Data Logger
- I2C Bus
- I2C Device Scanning
- I2C ID & Polling
- MotorControl Overview
- MotorControl Config
- MotorControl Commands
Helpers
Reference