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 ,Learn how to configure and manage the ESP32 partition table. This guide covers Flash connection, partition table setup, initialization, and mounting for applications, NVS, SPIFFS, and FATFS.
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.
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).
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:
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
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));
}}
FAQ
1.What is the partition table in ESP32?
The partition table in ESP32 defines how SPI Flash is divided into different regions, such as application storage, system data (NVS, PHY), and file systems like SPIFFS or FATFS.
2.Where is the ESP32 partition table stored?
By default, the ESP32 partition table is stored at offset 0x8000 in Flash. The bootloader reads it during startup to determine how to load applications and access data.
3.How do I customize the ESP32 partition table?
You can create a custom CSV file (e.g., partitions.csv) and specify it in the project’s CMakeLists.txt or via ESP-IDF menuconfig. This allows defining partitions for OTA, larger NVS, or custom data storage.
4.How do I mount SPIFFS or FATFS on ESP32?
For SPIFFS, use esp_vfs_spiffs_register(). For FATFS, use esp_vfs_fat_spiflash_mount(). Both require defining the correct partition label and initialization code in your ESP-IDF project.
5.Why do I need multiple app partitions in ESP32?
Multiple app partitions are required for OTA (Over-The-Air) updates. With at least two partitions (ota_0 and ota_1), the ESP32 can safely switch between firmware versions during updates.
END
0
Copyright Notice: Our original article was published byFr2ed0m on 2025-09-05, total 9534 words.
Reproduction Note: Unless otherwise specified, all articles are published by cc-4.0 protocol. Please indicate the source of reprint.
In penetration testing, XXL-Job vulnerabilities are often highlighted for their convenience in direct reverse shell attacks. However, real-world scenarios frequently involve “non-outbound networks” or “missing scheduler panels,” which pose greater challenges. This article breaks down the version detection, command execution, and multiple memory shell injection methods for the XXL-Job Executor default token vulnerability, using practical...
This article will start with the working principles of MCP, take the STDIO transmission mode as an example, and provide a comprehensive walkthrough of building a local MCP service, testing it, and integrating it with an LLM (using Cursor as an example), helping developers quickly master the practical application of the MCP protocol. We’ve already...
In the fast-paced world of AI development, building applications based on Large Language Models (LLMs) has become an industry focus. However, before the advent of MCP (Model Context Protocol), developers faced a host of tricky challenges when building LLM applications. These issues not only slowed down development efficiency but also limited the practical implementation of...
Technical Dilemmas in Embedded UI Development: From Requirements to Bottlenecks [Essential for Embedded UI Development] In the development of IoT, industrial control, and portable smart devices, how to build efficient graphical UIs in no-std, OS-less environments? Traditional terminal UI libraries are limited by resource constraints and hardware compatibility, becoming a major development pain point. This...
This article details the internal architecture and SQL execution workflow of Gorm, the popular ORM framework for Go. It shares practical techniques for model definition, querying, and updating, while solving common issues like time zone discrepancies, soft deletion, and transactions. It is tailored for advanced Gorm developers. As the most widely used ORM (Object-Relational Mapping) framework...