ESP-IDF Programming Guide Choose target... Choose version... Get Started API Reference Hardware Reference API Guides Security Guides Migration Guides Libraries and Frameworks Contributions Guide ESP-IDF Versions Resources Copyrights and Licenses About Switch Between Languages ESP-IDF Programming Guide ESP-IDF Programming Guide Edit on GitHub ESP-IDF Programming Guide [中文] This is the documentation for Espressif IoT Development Framework (esp-idf). ESP-IDF is the official development framework for the ESP32, ESP32-S, ESP32-C, ESP32-H and ESP32-P Series SoCs. This document describes using ESP-IDF with the ESP32-S3 SoC. To switch to a different SoC target, choose target from the dropdown in the upper left. Get Started API Reference API Guides Was this page helpful? Thank you! We received your feedback. If you have any comments, fill in Espressif Documentation Feedback Form. We value your feedback. Let us know how we can improve this page by filling in Espressif Documentation Feedback Form. Next © Copyright 2016 - 2026, Espressif Systems (Shanghai) Co., Ltd. Built with Sphinx using a theme based on Read the Docs Sphinx Theme. Download HTML
ESP-IDF Programming Guide Choose target... Choose version... Get Started API Reference API Conventions Application Protocols Bluetooth® API Error Codes Reference Networking APIs Peripherals API Provisioning API Storage API FAT Filesystem Support Generating and Parsing FATFS on Host Manufacturing Utility Non-Volatile Storage Library NVS Bootloader NVS Encryption NVS Partition Generator Utility NVS Partition Parser Utility SD/SDIO/MMC Driver Partitions API Block Device Layer SPIFFS Filesystem Virtual Filesystem Component Overview FS Registration Synchronous Input/Output Multiplexing Paths File Descriptors eventfd() Well Known VFS Devices Application Examples API Reference Wear Levelling API Storage Security Examples System API Configuration Options Reference Hardware Reference API Guides Security Guides Migration Guides Libraries and Frameworks Contributions Guide ESP-IDF Versions Resources Copyrights and Licenses About Switch Between Languages ESP-IDF Programming Guide API Reference Storage API Virtual Filesystem Component Edit on GitHub Virtual Filesystem Component [中文] Overview Virtual filesystem (VFS) component provides a unified interface for drivers which can perform operations on file-like objects. These can be real filesystems (FAT, SPIFFS, etc.) or device drivers which provide a file-like interface. This component allows C library functions, such as fopen and fprintf, to work with FS drivers. At a high level, each FS driver is mounted at some path prefix. When one of C library functions needs to open a file, the VFS component searches for the FS driver associated with the file path and forwards the call to that driver. VFS also forwards read, write, and other calls for the given file to the same FS driver. For example, one can mount a FAT filesystem driver at the /fat prefix and call fopen("/fat/file.txt", "w"). Then the VFS component calls the function open of the FAT driver and pass the argument /file.txt to it together with appropriate mode flags. All subsequent calls to C library functions for the returned FILE* stream will also be forwarded to the FAT driver. FS Registration To register an FS driver, an application needs to define an instance of the esp_vfs_fs_ops_t structure and populate it with function pointers to FS APIs: Warning The API version without a context pointer is deprecated, and will be removed in the future. See Context Aware Filesystem for details. // Both esp_vfs_fs_ops_t and its subcomponents have to have static storage
static const esp_vfs_dir_ops_t myfs_dir = {
.stat = &myfs_stat,
};
static const esp_vfs_fs_ops_t myfs = {
.write = &myfs_write,
.open = &myfs_open,
.close = &myfs_close,
.read = &myfs_read,
.dir = &myfs_dir,
};
ESP_ERROR_CHECK(esp_vfs_register_fs("/data", &myfs, ESP_VFS_FLAG_STATIC, NULL));
Non-static esp_vfs_fs_ops_t The recommended approach for registering filesystem is to use statically allocated esp_vfs_fs_ops_t alongside ESP_VFS_FLAG_STATIC, as it is more memory efficient. In cases where using static allocation is not possible, ESP_VFS_FLAG_STATIC can be replaced with ESP_VFS_FLAG_DEFAULT. This tells VFS to make a deep copy of the passed structure in RAM, this copy will be managed by VFS component. // Possibly local scope
{
esp_vfs_dir_ops_t myfs_dir = {
.stat = &myfs_stat,
};
bool some_condition = false;
esp_vfs_fs_ops_t myfs = {
.write = some_condition ? &myfs_special_write : &myfs_write,
// ... other members
.dir = &myfs_dir,
};
ESP_ERROR_CHECK(esp_vfs_register_fs("/data", &myfs, ESP_VFS_FLAG_DEFAULT, NULL));
}
Context Aware Filesystem In some cases, it might be beneficial or even necessary to pass some context to the filesystem functions, such as a mountpoint-specific file descriptor table, when multiple instances of FS are mounted. For this reason, esp_vfs_fs_ops_t contains a second version of each member with _p suffix; for example, read function has a corresponding read_p function. These functions take an additional first argument. When registering the FS, ESP_VFS_FLAG_CONTEXT_PTR needs to be specified and the context pointer should be passed as the last argument. ssize_t myfs_write(myfs_t* fs, int fd, const void * data, size_t size);
// In definition of esp_vfs_fs_ops_t:
.write_p = &myfs_write,
// ... other members initialized
// When registering FS, pass the ESP_VFS_FLAG_CONTEXT_PTR flag, alongside FS context pointer as the third and fourth arguments, respectively
// (hypothetical myfs_mount function is used for illustrative purposes)
myfs_t* myfs_inst1 = myfs_mount(partition1->offset, partition1->size);
ESP_ERROR_CHECK(esp_vfs_register_fs("/data1", &myfs, ESP_VFS_FLAG_STATIC | ESP_VFS_FLAG_CONTEXT_PTR, myfs_inst1));
// Can register another instance:
myfs_t* myfs_inst2 = myfs_mount(partition2->offset, partition2->size);
ESP_ERROR_CHECK(esp_vfs_register_fs("/data2", &myfs, ESP_VFS_FLAG_STATIC | ESP_VFS_FLAG_CONTEXT_PTR, myfs_inst2));
Synchronous Input/Output Multiplexing Synchronous input/output multiplexing by select() is supported in the VFS component. The implementation works in the following way. select() is called with file descriptors which could belong to various VFS drivers. The file descriptors are divided into groups each belonging to one VFS driver. The file descriptors belonging to non-socket VFS drivers are handed over to the given VFS drivers by start_select(), described later on this page. This function represents the driver-specific implementation of select() for the given driver. This should be a non-blocking call which means the function should immediately return after setting up the environment for checking events related to the given file descriptors. The file descriptors belonging to the socket VFS driver are handed over to the socket driver by socket_select() described later on this page. This is a blocking call which means that it will return only if there is an event related to socket file descriptors or a non-socket driver signals socket_select() to exit. Results are collected from each VFS driver and all drivers are stopped by de-initialization of the environment for checking events. The select() call ends and returns the appropriate results. Non-Socket VFS Drivers If you want to use select() with a file descriptor belonging to a non-socket VFS driver, then you need to register the driver with functions start_select() and end_select() similarly to the following example: // In definition of esp_vfs_select_ops_t:
.start_select = &uart_start_select,
.end_select = &uart_end_select,
// ... other members initialized
start_select() is called for setting up the environment for detection of read/write/error conditions on file descriptors belonging to the given VFS driver. end_select() is called to stop/deinitialize/free the environment which was setup by start_select(). Note end_select() might be called without a previous start_select() call in some rare circumstances. end_select() should fail gracefully if this is the case (i.e., should not crash but return an error instead). Please refer to the reference implementation for the UART peripheral in esp_driver_uart/src/uart_vfs.c and most particularly to the functions uart_vfs_dev_register(), uart_start_select(), and uart_end_select() for more information. Please check the following examples that demonstrate the use of select() with VFS file descriptors: peripherals/uart/uart_select system/select Socket VFS Drivers A socket VFS driver is using its own internal implementation of select() and non-socket VFS drivers notify it upon read/write/error conditions. A socket VFS driver needs to be registered with the following functions defined: // In definition of esp_vfs_select_ops_t:
.socket_select = &lwip_select,
.get_socket_select_semaphore = &lwip_get_socket_select_semaphore,
.stop_socket_select = &lwip_stop_socket_select,
.stop_socket_select_isr = &lwip_stop_socket_select_isr,
// ... other members initialized
socket_select() is the internal implementation of select() for the socket driver. It works only with file descriptors belonging to the socket VFS. get_socket_select_semaphore() returns the signalization object (semaphore) which is used in non-socket drivers to stop the waiting in socket_select(). stop_socket_select() call is used to stop the waiting in socket_select() by passing the object returned by get_socket_select_semaphore(). stop_socket_select_isr() has the same functionality as stop_socket_select() but it can be used from ISR. Please see lwip/port/vfs_lwip.c for a reference socket driver implementation using LWIP. Note If you use select() for socket file descriptors only then you can disable the CONFIG_VFS_SUPPORT_SELECT option to reduce the code size and improve performance. You should not change the socket driver during