6.3 KiB
ESP32 Wear Leveling Implementation for FAT Filesystem
Overview
This implementation adds ESP32 Wear Leveling layer support to FAT filesystem images created with fatfs-python. The wear leveling layer is required by the ESP32 Arduino Core's FFat library, which uses ESP-IDF's esp_vfs_fat_spiflash_mount_rw_wl() function.
Problem
The ESP32 Arduino Core expects FAT partitions to be wrapped with a wear leveling layer:
- Without WL: Raw FAT filesystem → Mount fails
- With WL: WL State + FAT filesystem + WL metadata → Mount succeeds
Wear Leveling Structure
┌─────────────────────────────────────────────────────────────┐
│ Sector 0: WL State Copy 1 │
├─────────────────────────────────────────────────────────────┤
│ Sector 1: WL State Copy 2 │
├─────────────────────────────────────────────────────────────┤
│ Sector 2-N: FAT Filesystem Data │
│ (Boot sector, FATs, Root dir, Data area) │
├─────────────────────────────────────────────────────────────┤
│ Sector N+1: Temp Sector (for WL operations) │
├─────────────────────────────────────────────────────────────┤
│ Sector N+2: WL State Copy 3 │
├─────────────────────────────────────────────────────────────┤
│ Sector N+3: WL State Copy 4 │
└─────────────────────────────────────────────────────────────┘
WL_State Structure (48 bytes)
typedef struct {
uint32_t pos; // Current position (0)
uint32_t max_pos; // Maximum position (number of FAT sectors)
uint32_t move_count; // Move counter (0)
uint32_t access_count; // Access counter (0)
uint32_t max_count; // Maximum count (update_rate * fat_sectors)
uint32_t block_size; // Block/sector size (4096)
uint32_t version; // WL version (2)
uint32_t device_id; // Device ID (0)
uint8_t reserved[12]; // Reserved (0xFF)
uint32_t crc32; // CRC32 of structure
} WL_State;
Configuration
Default Values
- Sector Size: 4096 bytes (ESP32 standard)
- Update Rate: 16 (triggers WL after 16 * sectors writes)
- WL State Sectors: 2 copies at start, 2 at end (4 total)
- Temp Sectors: 1 sector for WL operations
Overhead Calculation
Total Sectors = Partition Size / Sector Size
WL Overhead = (2 + 2 + 1) = 5 sectors
FAT Sectors = Total Sectors - 5
Example for 1.5 MB partition:
- Total: 1,507,328 bytes / 4096 = 368 sectors
- WL Overhead: 5 sectors = 20,480 bytes
- FAT Data: 363 sectors = 1,486,848 bytes
Usage
Building FAT Image with WL
The build_fatfs_image() function in main.py automatically wraps FAT images:
pio run -t buildfs
Output:
Building FS image from 'data' directory to .pio/build/esp32dev/fatfs.bin
Wrapping FAT image with ESP32 Wear Leveling layer...
Partition size: 1507328 bytes (368 sectors)
FAT data size: 1486848 bytes (363 sectors)
WL overhead: 5 sectors
Successfully created wear-leveling FAT image
Downloading and Extracting
The download_fatfs target automatically detects and extracts WL-wrapped images:
pio run -t download_fatfs
Output:
Detected Wear Leveling layer, extracting FAT data...
Extracted FAT data: 1486848 bytes
Extracting files:
FILE: /test.txt (12 bytes)
Successfully extracted 1 file(s) to unpacked_fs
Technical Details
CRC32 Calculation
The WL_State CRC32 is calculated over the first 44 bytes (excluding the CRC field itself):
state_data = struct.pack('<IIIIIIII12s',
pos, max_pos, move_count, access_count, max_count,
block_size, version, device_id, reserved)
crc = zlib.crc32(state_data) & 0xFFFFFFFF
Sector Alignment
All data must be aligned to sector boundaries (4096 bytes):
- WL State is padded with 0xFF to fill the sector
- FAT data is padded with 0xFF to sector boundary
- Total image size must equal partition size exactly
Erased Flash Value
Unused areas are filled with 0xFF (erased flash state):
- Reserved bytes in WL_State:
0xFF - Padding after FAT data:
0xFF - Temp sector:
0xFF
Compatibility
ESP-IDF Versions
- Tested with ESP-IDF v4.x and v5.x
- Compatible with Arduino-ESP32 core 2.x and 3.x
Sector Sizes
- Supported: 4096 bytes (recommended)
- Theoretical: 512, 1024, 2048 bytes (not tested)
FAT Types
- FAT12 (small partitions)
- FAT16 (medium partitions)
- FAT32 (large partitions, >32MB)
Troubleshooting
"FFat Mount Failed"
Cause: Image doesn't have wear leveling layer
Solution: Rebuild with updated build_fatfs_image():
pio run -t buildfs
pio run -t uploadfs
"Invalid sector size"
Cause: Sector size mismatch between build and ESP32 config
Solution: Ensure CONFIG_WL_SECTOR_SIZE=4096 in sdkconfig
"Partition too small"
Cause: FAT data + WL overhead exceeds partition size
Solution: Increase partition size in partitions.csv or reduce data
References
- ESP-IDF Wear Levelling Component
- ESP-IDF FAT Filesystem
- Arduino-ESP32 FFat Library
- mk_esp32fat Tool (alternative C implementation)
License
Same as platform-espressif32 (Apache 2.0)