This ADR documents the analysis, findings, and decisions regarding the implementation of USB Mass Storage Class (MSC) support on an ESP32-S3 device using FFat (ESP32 FAT on Flash File System). The objective is to enable the ESP32 to act as a mass storage device, allowing:
- USB host (e.g., a computer) to access stored files as if the ESP32 were a USB flash drive.
- The ESP32 application to access and modify files via FFat.
- Synchronization between USB MSC access and internal application access.
The project is built with PlatformIO and uses the Arduino-ESP32 framework.
Several existing projects and examples were analyzed for feasibility:
-
Espressif's "TinyUSB Mass Storage Device Example" (ESP-IDF)
- Uses
esp_tinyusb, which integrates with ESP-IDF. - Relies on
esp_partition_write()via ESP-IDF’s Wear Leveling API. - Issue: Requires ESP-IDF; not directly compatible with Arduino-ESP32.
- Uses
-
chegewara/EspTinyUSB "flashdisk" Example
- Implements USB MSC with
disk_read()anddisk_write()from ESP-IDF FatFS. - Issue: Library is not well-maintained and might be unstable.
- Implements USB MSC with
-
Espressif's "SD2USBMSC" Example
- Uses SD card storage with raw block access.
- Issue: Designed for SD cards, not for internal flash.
-
Adafruit's "msc_esp32_file_browser" Example
- Exposes flash storage via USB MSC and a web server.
- Uses
Adafruit_SPIFlash, which relies on TinyUSB. - Issue: Introduces additional dependencies; Arduino-ESP32 already includes TinyUSB, causing conflicts.
- Arduino-ESP32 framework includes TinyUSB, but PlatformIO does not allow excluding components.
- FFat does not expose
disk_read()ordisk_write()for direct block-level access. - Direct partition access (
esp_partition_read()andesp_partition_write()) is needed for USB MSC. - Concurrency between USB MSC and FFat must be managed to avoid filesystem corruption.
- ESP-IDF solutions are not directly compatible with Arduino-ESP32.
Chosen Approach: Direct Partition Access with FFat Synchronization
- USB MSC reads/writes directly to the flash partition using
esp_partition_write()andesp_partition_read(). - Application accesses files via FFat, using
FFat.begin()andFFat.end()for synchronization. - Synchronization via unmount/mount cycle (
FFat.end()before USB access,FFat.begin()after). - FreeRTOS Task for File System Operations
- A dedicated FreeRTOS task processes USB events.
- USB events do not perform filesystem operations directly.
- USB MSC Read/Write Callbacks
- File System Synchronization Task
| Approach | Reason for Rejection |
|---|---|
Using esp_tinyusb |
Requires ESP-IDF, not compatible with Arduino-ESP32 |
| Adafruit TinyUSB | Introduces conflicts with built-in TinyUSB in Arduino-ESP32 |
Direct FFat Access via fread() |
FFat does not support block-level read/write |
- ✅ Works within Arduino-ESP32 and PlatformIO.
- ✅ Uses built-in ESP32 partition management.
- ✅ Keeps dependencies minimal.
- ✅ Provides clean USB MSC integration.
- ⚠ FFat remounting may cause latency when switching access modes.
- ⚠ USB MSC cannot modify FFat metadata dynamically, requiring unmount/remount.
- ⚠ Large writes via USB MSC could impact flash lifespan as no wear leveling is used.
- Task-Tracker-Device Issue #6
- Espressif TinyUSB MSC Example
- Adafruit TinyUSB Arduino
- ESP32 FatFS
- USB MSC example from Espressif for Arduino-ESP32
- issue in Adafruit_TinyUSB_Arduino with macro redefinitions for ESP32S2 and ESP32S3 (#484)
- issue in Adafruit_TinyUSB_Arduino with Linker Error on esp32s3 with platformIO (#473)
- example for using USB MSC for firmware updates
- example for using LittleFS with PlatformIO and Arduino-ESP32
- "FSBrowser" example providing SPIFFS via Web
- Espressif's "TinyUSB Mass Storage Device Example" für ESP-IDF
- Espressif's additions to TinyUSB "esp_tinyusb" extends espressif/tinyusb
- Wear Levelling API