ESP32 Partition Table Guide: Flash Setup, Configuration & Mounting

99 Views
No Comments

This article focuses on the ESP32   -S3, explaining how to set up its SPI Flash partition table and connect it to the Flash. It also covers partition table configuration and initializing and mounting each partition .

ESP32 Partition Table Guide: Flash Setup, Configuration & Mounting

01 Introduction

1. ESP32-S3 Partition Table

  It is a configuration mechanism for dividing the SPI Flash storage space. It works similarly to the partitioning of a computer hard drive, dividing the Flash into multiple areas, each area is used to store different types of data or programs, such as:

  • Applications (e.g., factory, ota_0, ota_1);
  • System data (such as NVS and PHY initialization data);
  • File system (such as SPIFFS, FATFS).

2.  Connecting ESP32-S3 to SPI Flash

ESP32-S3 communicates with external SPI Flash through the SPI/QSPI/OPI interface . The typical connection scheme is as follows:

SPI Flash Signals ESP32-S3 Pin Names Pin number (QFN56)  Remark
SCK/CLK    SPICLK            33   Clock Line     
MOSI/SI  SPID               35   Data Input    
MISO/SO   SPIQ              34   Data Output    
CS#        SPICS0              32   Chip Select      
WP#         SPIWP           31   Write protection     
HOLD# / IO3 SPIHD               30   Pause/IO3 

The SPI Flash is an external, independent chip that must be connected to the ESP32-S3 via a printed circuit board (PCB). The ESP32-S3 chip itself does not have integrated Flash, so all programs and data are stored in the external SPI Flash. Some modules (such as the ESP32 ESP32-S3-WROOM-1-S3) integrate Flash with the SoC on the same PCB, but these are still external components.

ESP32 Partition Table Guide: Flash Setup, Configuration & Mounting

3. Communication protocols and modes

  • Basic SPI mode : uses 4 lines (CLK, CS, MOSI, MISO) and supports standard SPI protocol;
  • QSPI mode : transmits address and data simultaneously through 4 lines, increasing bandwidth by 4 times. It requires Flash chip support (such as qioor qoutmode).
  • OPI mode (Octal SPI): 8-wire parallel transmission, suitable for high-performance requirements, but requires a dedicated Flash model.

4. ESP32-S3 is compatible with SPI Flash from major manufacturers . Typical models are as follows:

1. Winbond 

  • W25Q64JV: 8MB, supports QSPI, voltage 3.3V, package SOIC-8;
  • W25Q128JV: 16MB, maximum clock frequency 133MHz, suitable for large-capacity storage needs.

2. GigaDevice

  • GD25Q32C: 4MB, low-power design (<1mW), supports XIP (execution in place);
  • GD25Q128C: 16MB, operating voltage 1.7V~3.6V, compatible with wide voltage systems.

3. Micron

  • MT25QL128: 16MB, uses Octal SPI interface, suitable for high-speed data throughput scenarios.

02 How to set up the partition table

The partition table is a binary data structure stored in a fixed location in Flash (default 0x8000). It records information such as the starting address, size, type, and subtype of each partition. When the ESP32-S3 boots, the bootloader first reads the partition table and then uses the information in the table to load programs (such as the app partition) or access data (such as the NVS partition) from the corresponding partition.

Partition name        Who is responsible for initialization?   Typical trigger codes /locations  Remark                     
 nvs            nvs_flash_init()   User code `app_main()    Must be called manually           
 phy_init    Inside the Wi-Fi/BT protocol stack        esp_wifi_init() or esp_bt_controller_init()    The protocol stack automatically reads calibration data         
factory        ROM bootloader + CMake link        Power on ROM is directly mapped to address 0x10000           No application layer initialization required  
 vfs(FAT)      esp_vfs_fat_spiflash_mount()` or esp_vfs_fat_register()    User Code              Example: fatfs_spiflash/main.c 
storage (SPIFFS)   esp_spiffs_mount()` or esp_vfs_spiffs_register()`          User Code     Example: spiffsgen/main.c     

Specific process (taking ESP-IDF framework as an example).

1. Partition table format

# Name, type, subtype, starting offset, size, flags (optional) nvs, data, nvs, , 0x40000, # 4MB NVS partition (storage configuration) phy_init, data, phy, , 0x1000, # RF calibration data partition
factory, app, factory, , 0x100000, # 1MB factory app partition (default program) ota_0, app, ota_0, , 0x100000, # 1MB OTA partition 0
ota_1, app, ota_1, , 0x100000, # 1MB OTA partition 1
vfs, data, fat, , 0x200000, # 2MB file system partition

Type: app (application) or data (data);

Subtype: Under the app type, there are factory (default program), ota_0~ota_15 (OTA partition); under the data type, there are nvs, phy (radio data), fat (file system), etc.

Offset: can be omitted (automatically assigned in sequence), but must ensure no overlap;

Size: supports KB (such as 64KB), MB (such as 2MB), or hexadecimal (such as 0x10000).

2. Manually create a partition table

Creation steps:

1) Create a partition table file : Create a new file named partitions.csv in the project root directory and fill in the partition information according to the above format.

2) Specify the partition table path:

  • Add the following to your project’s CMakeLists.txt:
set (PARTITION_TABLE_CSV partitions.csv)   # Point to the custom partition table file
  • Or via the ESP-IDF configuration tool (menuconfig):

3) Go to Partition Table → Partition Table (Custom partition table CSV) → enter the custom CSV file path (e.g. partitions.csv).

3. Automatically create a partition table for the ESP-IDF project in VSCode

In most cases, use the partition table automatically created by VSCode ESP-IDF. VSCode ESP-IDF projects use the framework’s default partition table (default_partitions.csv), which is suitable for most basic scenarios (including necessary partitions such as the factory app, nvs, and phy_init).

If you use the default partition table: No additional operations are required, just compile (Build) and flash (Flash) normally, VS Code will automatically handle the generation and flashing of the partition table.

4. Scenarios where partition table modification is required

1) Need to support OTA upgrade

The default partition table does not contain an OTA partition. To implement wireless upgrade functionality, you must add at least two OTA partitions (such as ota_0 and ota_1). For example:

csvota_0, app, ota_0, , 1M,ota_1, app, ota_1, , 1M,

2) The default partition size is insufficient

If the NVS partition (default 5KB) cannot store device configurations (such as multiple WiFi passwords, sensor calibration data), its capacity needs to be increased (for example, 0x40000, which is 256KB);

If you use the FAT file system to store a large number of logs or files, you need to add or expand a fat-type partition (such as 2M).

3) Custom data partitioning requirements

When you need to store specific data (such as firmware backups and encryption keys) independently, you can add a custom data partition. For example:

csvfirmware_backup, data, 0x80, , 512KB,   # Subtype 0x80 is custom

4) The Flash capacity exceeds the default partition table support range

The default partition table is suitable for Flash memory of 4MB or less. If you use 8MB/16MB Flash memory and need to make full use of the space, you need to re-plan the partition size (for example, expand the app partition to 4MB).

5) Multi-application switching

When you need to run multiple independent applications (such as the main program + debugger) on a device, you need to allocate a separate app partition for each application.

03 Initialize/Mount each partition

1. nvs

As long as you plan to use NVS (non-volatile storage) to save/read key-value data, you must call nvs_flash_init() during the initialization phase. Without it, any nvs_open, nvs_set_*, nvs_get_* will directly return ESP_ERR_NVS_NOT_INITIALIZED.

esp_err_t  ret =  nvs_flash_init ();
if  (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
    ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {ESP_ERROR_CHECK ( nvs_flash_erase ());    
    ESP_ERROR_CHECK (nvs_flash_init ());
}

2.phy_init (inside the Wi-Fi/BT protocol stack, developers only need to start the protocol stack)

/* Wi-Fi example: The protocol stack will automatically read the calibration data at 0xF000*/
esp_netif_init ();
esp_event_loop_create_default ();
esp_netif_create_default_wifi_sta ();
wifi_init_config_t  cfg =  WIFI_INIT_CONFIG_DEFAULT ();
ESP_ERROR_CHECK (esp_wifi_init (&cfg));    // phy_init will be loaded internally

3. Factory (ROM bootloader runs directly, no code is required for the application layer)

4. vfs (FAT) – Mount the 10 MB partition starting at 0x200000 as /vfs

# include  "esp_vfs_fat.h"
# include  "wear_levelling.h"
# define  FAT_PARTITION_LABEL     "vfs"
wl_handle_t  wl_handle;
void  mount_fat (void) {
    esp_vfs_fat_mount_config_t  mount_config = {
        .max_files =  8 ,
        .format_if_mount_failed =  true ,
        .allocation_unit_size =  512
    };
    ESP_ERROR_CHECK (esp_vfs_fat_spiflash_mount_rw_wl (     "/vfs" , FAT_PARTITION_LABEL, &mount_config, &wl_handle));
}

5. storage (SPIFFS) – Mount the 4 MB partition starting at 0xC00000 as /spiffs

# include  "esp_spiffs.h"
# define  SPIFFS_PARTITION_LABEL  "storage"
void  mount_spiffs (void) {
    esp_vfs_spiffs_conf_t  conf = {
        .base_path =  "/spiffs" ,
        .partition_label = SPIFFS_PARTITION_LABEL,
        .max_files =  5 ,
        .format_if_mount_failed =  true
    };
    ESP_ERROR_CHECK (esp_vfs_spiffs_register (&conf));
}

6. app_main() Template

void  app_main (void) {
    /* 1. NVS initialization*/
    esp_err_t  ret =  nvs_flash_init ();
    if  (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
        ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {ESP_ERROR_CHECK ( nvs_flash_erase ());
        ESP_ERROR_CHECK (nvs_flash_init ());
    }
    /* 2. Wi-Fi/BT → Automatically use the phy_init partition*/
    esp_netif_init ();
    esp_event_loop_create_default ();
    esp_netif_create_default_wifi_sta ();
    wifi_init_config_t  wifi_cfg =  WIFI_INIT_CONFIG_DEFAULT ();
    ESP_ERROR_CHECK (esp_wifi_init (&wifi_cfg));
    /* 3. Mount FAT file system*/
    mount_fat ();
    /* 4. Mount the SPIFFS file system*/
    mount_spiffs ();
    /* 5. Main loop or other business logic*/
    for  (;;) {vTaskDelay ( pdMS_TO_TICKS ( 1000));
    }}
END
 0
Pa2sw0rd
Copyright Notice: Our original article was published by Pa2sw0rd on 2025-09-05, total 8391 words.
Reproduction Note: Unless otherwise specified, all articles are published by cc-4.0 protocol. Please indicate the source of reprint.
Comment(No Comments)