Arduino Core 3.3.6
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
@@ -0,0 +1,175 @@
|
||||
# FatFS Integration for Platform-Espressif32
|
||||
|
||||
This platform now supports FatFS as a filesystem option, analogous to the existing LittleFS integration.
|
||||
|
||||
## Features
|
||||
|
||||
- **Build FatFS Image**: Creates a FatFS filesystem image from a directory
|
||||
- **Upload FatFS Image**: Uploads the FatFS image to the ESP32 device
|
||||
- **Download FatFS Image**: Downloads the FatFS image from the device and extracts it
|
||||
|
||||
## Configuration
|
||||
|
||||
### platformio.ini
|
||||
|
||||
```ini
|
||||
[env:myenv]
|
||||
platform = espressif32
|
||||
board = esp32dev
|
||||
framework = arduino
|
||||
|
||||
; Select FatFS as filesystem
|
||||
board_build.filesystem = fatfs
|
||||
|
||||
; Optional: Directory for extracted files (default: unpacked_fs)
|
||||
board_build.unpack_dir = unpacked_fs
|
||||
```
|
||||
|
||||
### Partition Table
|
||||
|
||||
The partition table must contain a FAT partition (Subtype 0x81):
|
||||
|
||||
```csv
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x140000,
|
||||
app1, app, ota_1, 0x150000,0x140000,
|
||||
ffat, data, fat, 0x290000,0x170000,
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Build FatFS Image
|
||||
|
||||
```bash
|
||||
# Place files in data/ directory
|
||||
mkdir -p data
|
||||
echo "Hello FatFS" > data/test.txt
|
||||
|
||||
# Build image
|
||||
pio run -t buildfs
|
||||
```
|
||||
|
||||
### Upload FatFS Image
|
||||
|
||||
```bash
|
||||
pio run -t uploadfs
|
||||
```
|
||||
|
||||
### Download FatFS Image from Device
|
||||
|
||||
```bash
|
||||
pio run -t download_fatfs
|
||||
```
|
||||
|
||||
Files will be extracted to the configured directory (default: `unpacked_fs`).
|
||||
|
||||
## Technical Details
|
||||
|
||||
### Python Dependencies
|
||||
|
||||
The integration uses the `fatfs-ng` package, which is automatically installed.
|
||||
|
||||
### Build Process
|
||||
|
||||
1. A RAM disk is created with the configured FAT data size (partition size minus WL overhead)
|
||||
2. The FatFS is formatted with proper parameters (2 FATs, LFN support)
|
||||
3. All files from the `data/` directory are copied
|
||||
4. The FAT image is wrapped with ESP32 Wear Leveling layer
|
||||
5. The final image is saved as a `.bin` file
|
||||
|
||||
**Important**: The build process automatically adds the ESP32 Wear Leveling layer, which is required by the Arduino FFat library. See [WEAR_LEVELING.md](WEAR_LEVELING.md) for details.
|
||||
|
||||
### Wear Leveling Layer
|
||||
|
||||
ESP32's FFat library requires a wear leveling layer around the FAT filesystem. The build process automatically:
|
||||
- Reserves sectors for wear leveling metadata
|
||||
- Wraps the FAT filesystem with WL_State structures
|
||||
- Calculates proper CRC32 checksums
|
||||
|
||||
### Download Process
|
||||
|
||||
1. The partition table is downloaded from the device
|
||||
2. The FAT partition is identified (Subtype 0x81)
|
||||
3. The filesystem image is downloaded
|
||||
4. The wear leveling layer is automatically detected and removed
|
||||
5. The FAT data is mounted and extracted
|
||||
|
||||
## Extended Features
|
||||
|
||||
The `pyfatfs` package includes extended features for complete directory traversal:
|
||||
|
||||
- **Complete Directory Traversal**: `walk()`, `listdir()`, `stat()`
|
||||
- **Path Operations**: `exists()`, `isfile()`, `isdir()`
|
||||
- **File Operations**: `remove()`, `rmdir()`, `rename()`, `makedirs()`
|
||||
- **Convenience Methods**: `read_file()`, `write_file()`
|
||||
- **Bulk Operations**: `copy_tree_from()`, `copy_tree_to()`
|
||||
|
||||
These features enable full filesystem extraction and manipulation.
|
||||
|
||||
## Comparison: LittleFS vs FatFS
|
||||
|
||||
| Feature | LittleFS | FatFS |
|
||||
|---------|----------|-------|
|
||||
| Wear Leveling | Yes | Yes |
|
||||
| Power-Loss Protection | Yes | Limited |
|
||||
| Compatibility | ESP-IDF specific | Standard FAT |
|
||||
| Sector Size | 4096 | 4096 |
|
||||
| Filesystem Size | Flexible | Larger |
|
||||
|
||||
## Example Code (Arduino)
|
||||
|
||||
```cpp
|
||||
#include <FFat.h>
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Mount FatFS
|
||||
if (!FFat.begin(true)) {
|
||||
Serial.println("FFat Mount Failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Read file
|
||||
File file = FFat.open("/test.txt", "r");
|
||||
if (file) {
|
||||
Serial.println(file.readString());
|
||||
file.close();
|
||||
}
|
||||
|
||||
// Write file
|
||||
file = FFat.open("/output.txt", "w");
|
||||
if (file) {
|
||||
file.println("Hello from ESP32!");
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "No FAT filesystem partition found"
|
||||
|
||||
- Check the partition table
|
||||
- Ensure a partition with subtype `fat` (0x81) exists
|
||||
|
||||
### Build Errors
|
||||
|
||||
```bash
|
||||
# Recreate Python environment
|
||||
rm -rf ~/.platformio/penv
|
||||
pio run
|
||||
```
|
||||
|
||||
## Further Information
|
||||
|
||||
- [FatFS Documentation](http://elm-chan.org/fsw/ff/00index_e.html)
|
||||
- [ESP-IDF FFat Documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/fatfs.html)
|
||||
- [fatfs-ng Repository](https://github.com/Jason2866/pyfatfs)
|
||||
- [Original fatfs-python](https://github.com/krakonos/fatfs-python)
|
||||
@@ -0,0 +1,224 @@
|
||||
# ESP32 FAT Filesystem Test Project
|
||||
|
||||
This project tests the FAT filesystem implementation with ESP32 Wear Leveling support.
|
||||
|
||||
## Overview
|
||||
|
||||
This project demonstrates:
|
||||
- Building FAT filesystem images with wear leveling layer
|
||||
- Uploading FAT images to ESP32
|
||||
- Mounting and reading FAT filesystem on ESP32
|
||||
- Downloading and extracting FAT images from ESP32
|
||||
|
||||
## Requirements
|
||||
|
||||
- PlatformIO
|
||||
- ESP32 development board
|
||||
- USB cable
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
arduino-fatfs/
|
||||
├── data/ # Files to be included in FAT image
|
||||
│ ├── test.txt
|
||||
│ ├── README.md
|
||||
│ └── ...
|
||||
├── src/
|
||||
│ └── ffat.ino # Main Arduino sketch
|
||||
├── partitions.csv # Partition table with FAT partition
|
||||
├── platformio.ini # PlatformIO configuration
|
||||
└── unpacked_fs/ # Downloaded files (created by download_fatfs)
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### 1. Build Firmware
|
||||
|
||||
```bash
|
||||
pio run
|
||||
```
|
||||
|
||||
### 2. Build FAT Filesystem Image
|
||||
|
||||
Place your files in the `data/` directory, then:
|
||||
|
||||
```bash
|
||||
pio run -t buildfs
|
||||
```
|
||||
|
||||
This creates a FAT filesystem image with ESP32 wear leveling layer at:
|
||||
`.pio/build/esp32dev/fatfs.bin`
|
||||
|
||||
### 3. Upload Firmware and Filesystem
|
||||
|
||||
```bash
|
||||
# Upload firmware
|
||||
pio run -t upload
|
||||
|
||||
# Upload filesystem
|
||||
pio run -t uploadfs
|
||||
```
|
||||
|
||||
### 4. Monitor Serial Output
|
||||
|
||||
```bash
|
||||
pio run -t monitor
|
||||
```
|
||||
|
||||
Expected output:
|
||||
```
|
||||
FFat mounted successfully
|
||||
Test begin
|
||||
Total space: 1486848
|
||||
Free space: 1482752
|
||||
Listing directory: /
|
||||
FILE: test.txt SIZE: 12
|
||||
FILE: README.md SIZE: 1234
|
||||
Test complete
|
||||
```
|
||||
|
||||
### 5. Download Filesystem from Device
|
||||
|
||||
To download and extract the filesystem from the device:
|
||||
|
||||
```bash
|
||||
pio run -t download_fatfs
|
||||
```
|
||||
|
||||
Files will be extracted to `unpacked_fs/` directory.
|
||||
|
||||
## Partition Table
|
||||
|
||||
The `partitions.csv` defines the flash layout:
|
||||
|
||||
```csv
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x140000,
|
||||
app1, app, ota_1, 0x150000,0x140000,
|
||||
ffat, data, fat, 0x290000,0x170000,
|
||||
```
|
||||
|
||||
The `ffat` partition:
|
||||
- **Type**: data
|
||||
- **SubType**: fat (0x81)
|
||||
- **Offset**: 0x290000 (2,686,976 bytes)
|
||||
- **Size**: 0x170000 (1,507,328 bytes = ~1.44 MB)
|
||||
|
||||
## Wear Leveling
|
||||
|
||||
The FAT filesystem is automatically wrapped with ESP32's wear leveling layer:
|
||||
|
||||
- **Total partition**: 1,507,328 bytes (368 sectors × 4096 bytes)
|
||||
- **WL overhead**: 20,480 bytes (5 sectors)
|
||||
- **FAT data**: 1,486,848 bytes (363 sectors)
|
||||
|
||||
Structure:
|
||||
```
|
||||
[WL State 1][WL State 2][FAT Data][Temp][WL State 3][WL State 4]
|
||||
```
|
||||
|
||||
See [WEAR_LEVELING.md](../../WEAR_LEVELING.md) for details.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "FFat Mount Failed"
|
||||
|
||||
**Possible causes:**
|
||||
1. Filesystem not uploaded
|
||||
2. Wrong partition table
|
||||
3. Corrupted filesystem
|
||||
|
||||
**Solutions:**
|
||||
```bash
|
||||
# Rebuild and upload filesystem
|
||||
pio run -t buildfs
|
||||
pio run -t uploadfs
|
||||
|
||||
# Or erase flash and start fresh
|
||||
pio run -t erase
|
||||
pio run -t upload
|
||||
pio run -t uploadfs
|
||||
```
|
||||
|
||||
### "No FAT filesystem partition found"
|
||||
|
||||
**Cause:** Partition table doesn't have a FAT partition
|
||||
|
||||
**Solution:** Check `partitions.csv` has a partition with `SubType: fat`
|
||||
|
||||
### Files not appearing
|
||||
|
||||
**Cause:** Files not in `data/` directory when building
|
||||
|
||||
**Solution:**
|
||||
1. Add files to `data/` directory
|
||||
2. Rebuild filesystem: `pio run -t buildfs`
|
||||
3. Upload: `pio run -t uploadfs`
|
||||
|
||||
## Code Example
|
||||
|
||||
```cpp
|
||||
#include "FFat.h"
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Mount FAT filesystem
|
||||
if (!FFat.begin(false)) {
|
||||
Serial.println("FFat Mount Failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// List files
|
||||
File root = FFat.open("/");
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
Serial.printf("File: %s, Size: %d\n",
|
||||
file.name(), file.size());
|
||||
file = root.openNextFile();
|
||||
}
|
||||
|
||||
// Read file
|
||||
File f = FFat.open("/test.txt", "r");
|
||||
if (f) {
|
||||
String content = f.readString();
|
||||
Serial.println(content);
|
||||
f.close();
|
||||
}
|
||||
|
||||
// Write file
|
||||
f = FFat.open("/output.txt", "w");
|
||||
if (f) {
|
||||
f.println("Hello from ESP32!");
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Your code here
|
||||
}
|
||||
```
|
||||
|
||||
## Platform Configuration
|
||||
|
||||
This project uses a custom platform with FAT filesystem support:
|
||||
|
||||
```ini
|
||||
[env:esp32dev]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
board = esp32dev
|
||||
board_build.filesystem = fatfs
|
||||
board_build.partitions = partitions.csv
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [ESP32 FFat Library](https://github.com/espressif/arduino-esp32/tree/master/libraries/FFat)
|
||||
- [ESP-IDF FAT Filesystem](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/fatfs.html)
|
||||
- [ESP-IDF Wear Levelling](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/wear-levelling.html)
|
||||
- [Platform-Espressif32 FAT Integration](../../FATFS_INTEGRATION.md)
|
||||
- [Wear Leveling Implementation](../../platform-espressif32/WEAR_LEVELING.md)
|
||||
@@ -0,0 +1,380 @@
|
||||
# FFat Filesystem Test Guide
|
||||
|
||||
This guide explains how to test the ESP32 FFat filesystem with pre-flashed images using Wear Leveling.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Prepare Test Files
|
||||
|
||||
Add files to the `data/` directory that you want to include in the filesystem:
|
||||
|
||||
```bash
|
||||
# Example files already included:
|
||||
data/
|
||||
├── test.txt
|
||||
├── README.md
|
||||
├── platformio.ini
|
||||
└── partitions.csv
|
||||
```
|
||||
|
||||
### 2. Build Filesystem Image
|
||||
|
||||
Build the FAT filesystem image with Wear Leveling layer:
|
||||
|
||||
```bash
|
||||
pio run -t buildfs
|
||||
```
|
||||
|
||||
This creates `.pio/build/esp32dev/fatfs.bin` with:
|
||||
- Your files from `data/` directory
|
||||
- ESP32 Wear Leveling layer
|
||||
- Proper FAT filesystem structure
|
||||
|
||||
### 3. Upload Firmware and Filesystem
|
||||
|
||||
```bash
|
||||
# Upload firmware
|
||||
pio run -t upload
|
||||
|
||||
# Upload filesystem
|
||||
pio run -t uploadfs
|
||||
```
|
||||
|
||||
### 4. Monitor Serial Output
|
||||
|
||||
```bash
|
||||
pio run -t monitor
|
||||
```
|
||||
|
||||
## Test Configuration
|
||||
|
||||
Edit `src/ffat.ino` to configure tests:
|
||||
|
||||
```cpp
|
||||
// Set to true to format the partition (erases all data!)
|
||||
#define FORMAT_FFAT false
|
||||
|
||||
// Test settings
|
||||
#define TEST_READ_EXISTING true // Test reading pre-flashed files
|
||||
#define TEST_WRITE_NEW true // Test writing new files
|
||||
#define TEST_FILE_IO true // Test I/O performance
|
||||
```
|
||||
|
||||
## Expected Output
|
||||
|
||||
### Successful Mount
|
||||
|
||||
```
|
||||
============================================================
|
||||
ESP32 FFat Filesystem Test
|
||||
Testing pre-flashed image with Wear Leveling
|
||||
============================================================
|
||||
|
||||
Mounting FFat filesystem...
|
||||
✓ FFat mounted successfully!
|
||||
|
||||
=== Filesystem Information ===
|
||||
Total space: 1486848 bytes (1.42 MB)
|
||||
Used space: 12288 bytes (0.01 MB)
|
||||
Free space: 1474560 bytes (1.41 MB)
|
||||
Usage: 0.8%
|
||||
```
|
||||
|
||||
### Reading Pre-Flashed Files
|
||||
|
||||
```
|
||||
============================================================
|
||||
|
||||
=== Testing Pre-Flashed Files ===
|
||||
|
||||
Files in root directory:
|
||||
Listing directory: /
|
||||
FILE: test.txt SIZE: 12
|
||||
FILE: README.md SIZE: 1234
|
||||
FILE: platformio.ini SIZE: 456
|
||||
FILE: partitions.csv SIZE: 234
|
||||
|
||||
Reading test files:
|
||||
|
||||
--- File: /test.txt ---
|
||||
Reading file: /test.txt
|
||||
- read from file:
|
||||
Hello World!
|
||||
|
||||
--- File: /README.md ---
|
||||
Reading file: /README.md
|
||||
- read from file:
|
||||
[README content...]
|
||||
```
|
||||
|
||||
### Write Operations
|
||||
|
||||
```
|
||||
============================================================
|
||||
|
||||
=== Testing Write Operations ===
|
||||
|
||||
1. Creating new file...
|
||||
Writing file: /test_write.txt
|
||||
- file written
|
||||
|
||||
2. Appending to file...
|
||||
Appending to file: /test_write.txt
|
||||
- message appended
|
||||
Appending to file: /test_write.txt
|
||||
- message appended
|
||||
|
||||
3. Reading back written file:
|
||||
Reading file: /test_write.txt
|
||||
- read from file:
|
||||
Hello from ESP32!
|
||||
This line was appended.
|
||||
And another line.
|
||||
|
||||
4. Testing rename...
|
||||
Renaming file /test_write.txt to /renamed.txt
|
||||
- file renamed
|
||||
Reading file: /renamed.txt
|
||||
[content...]
|
||||
|
||||
5. Testing delete...
|
||||
Deleting file: /renamed.txt
|
||||
- file deleted
|
||||
File successfully deleted
|
||||
```
|
||||
|
||||
### Performance Test
|
||||
|
||||
```
|
||||
============================================================
|
||||
|
||||
Testing file I/O with /benchmark.bin
|
||||
- writing................................
|
||||
- 1048576 bytes written in 2345 ms
|
||||
- reading................................
|
||||
- 1048576 bytes read in 1234 ms
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "FFat Mount Failed"
|
||||
|
||||
**Possible causes:**
|
||||
|
||||
1. **No FFat partition in partition table**
|
||||
- Check `partitions.csv` has a `fat` partition
|
||||
- Verify partition is flashed
|
||||
|
||||
2. **Filesystem not flashed**
|
||||
```bash
|
||||
pio run -t buildfs
|
||||
pio run -t uploadfs
|
||||
```
|
||||
|
||||
3. **Missing Wear Leveling layer**
|
||||
- Ensure you're using the updated platform with WL support
|
||||
- Rebuild filesystem image
|
||||
|
||||
4. **Corrupted filesystem**
|
||||
- Set `FORMAT_FFAT true` to reformat
|
||||
- Or erase flash: `pio run -t erase`
|
||||
|
||||
### "File not found"
|
||||
|
||||
If pre-flashed files are not found:
|
||||
|
||||
1. Check files exist in `data/` directory
|
||||
2. Rebuild filesystem: `pio run -t buildfs`
|
||||
3. Upload filesystem: `pio run -t uploadfs`
|
||||
4. Reset ESP32
|
||||
|
||||
### "Write failed"
|
||||
|
||||
If write operations fail:
|
||||
|
||||
1. Check filesystem is not full
|
||||
2. Verify partition has write permissions
|
||||
3. Check for filesystem corruption
|
||||
|
||||
## Advanced Testing
|
||||
|
||||
### Download and Verify Filesystem
|
||||
|
||||
After running tests, download the filesystem to verify changes:
|
||||
|
||||
```bash
|
||||
pio run -t download_fatfs
|
||||
```
|
||||
|
||||
Files will be extracted to `unpacked_fs/` directory.
|
||||
|
||||
### Compare Original and Downloaded
|
||||
|
||||
```bash
|
||||
# Compare original files
|
||||
diff data/test.txt unpacked_fs/test.txt
|
||||
|
||||
# Check for new files created by tests
|
||||
ls -la unpacked_fs/
|
||||
```
|
||||
|
||||
### Test Wear Leveling
|
||||
|
||||
To verify wear leveling is working:
|
||||
|
||||
1. Write many files
|
||||
2. Download filesystem
|
||||
3. Check WL state is valid:
|
||||
|
||||
```python
|
||||
from fatfs import is_esp32_wl_image
|
||||
|
||||
with open('.pio/build/esp32dev/downloaded_fs_*.bin', 'rb') as f:
|
||||
data = f.read()
|
||||
|
||||
if is_esp32_wl_image(data):
|
||||
print("✓ Wear Leveling layer is intact")
|
||||
else:
|
||||
print("✗ Wear Leveling layer is missing or corrupted")
|
||||
```
|
||||
|
||||
## Test Scenarios
|
||||
|
||||
### Scenario 1: Fresh Filesystem
|
||||
|
||||
```cpp
|
||||
#define FORMAT_FFAT true
|
||||
#define TEST_READ_EXISTING false
|
||||
#define TEST_WRITE_NEW true
|
||||
#define TEST_FILE_IO true
|
||||
```
|
||||
|
||||
Tests creating a new filesystem from scratch.
|
||||
|
||||
### Scenario 2: Pre-Flashed Image (Default)
|
||||
|
||||
```cpp
|
||||
#define FORMAT_FFAT false
|
||||
#define TEST_READ_EXISTING true
|
||||
#define TEST_WRITE_NEW true
|
||||
#define TEST_FILE_IO true
|
||||
```
|
||||
|
||||
Tests reading pre-flashed files and writing new ones.
|
||||
|
||||
### Scenario 3: Read-Only Test
|
||||
|
||||
```cpp
|
||||
#define FORMAT_FFAT false
|
||||
#define TEST_READ_EXISTING true
|
||||
#define TEST_WRITE_NEW false
|
||||
#define TEST_FILE_IO false
|
||||
```
|
||||
|
||||
Only tests reading pre-flashed files without modifications.
|
||||
|
||||
### Scenario 4: Performance Only
|
||||
|
||||
```cpp
|
||||
#define FORMAT_FFAT false
|
||||
#define TEST_READ_EXISTING false
|
||||
#define TEST_WRITE_NEW false
|
||||
#define TEST_FILE_IO true
|
||||
```
|
||||
|
||||
Only tests I/O performance.
|
||||
|
||||
## Continuous Integration
|
||||
|
||||
For automated testing:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# test_fatfs.sh
|
||||
|
||||
# Build and upload
|
||||
pio run -t buildfs
|
||||
pio run -t upload
|
||||
pio run -t uploadfs
|
||||
|
||||
# Wait for ESP32 to boot
|
||||
sleep 2
|
||||
|
||||
# Monitor output and check for success
|
||||
pio run -t monitor | tee test_output.log
|
||||
|
||||
# Verify output
|
||||
if grep -q "✓ All tests completed!" test_output.log; then
|
||||
echo "Tests PASSED"
|
||||
exit 0
|
||||
else
|
||||
echo "Tests FAILED"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
### Enable Debug Output
|
||||
|
||||
```cpp
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.setDebugOutput(true); // Enable ESP32 debug output
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Check Partition Table
|
||||
|
||||
```bash
|
||||
# Read partition table from device
|
||||
pio run -t monitor
|
||||
|
||||
# In another terminal
|
||||
esptool.py --port /dev/ttyUSB0 read_flash 0x8000 0x1000 partition_table.bin
|
||||
|
||||
# Parse partition table
|
||||
python -c "
|
||||
import struct
|
||||
with open('partition_table.bin', 'rb') as f:
|
||||
data = f.read()
|
||||
for i in range(0, len(data), 32):
|
||||
entry = data[i:i+32]
|
||||
if entry[:2] == b'\xAA\x50':
|
||||
print(f'Partition at {i}: {entry.hex()}')
|
||||
"
|
||||
```
|
||||
|
||||
### Verify Wear Leveling
|
||||
|
||||
```bash
|
||||
# Download filesystem
|
||||
pio run -t download_fatfs
|
||||
|
||||
# Check WL structure
|
||||
python -c "
|
||||
import glob
|
||||
from fatfs import is_esp32_wl_image, ESP32WearLeveling
|
||||
import struct
|
||||
|
||||
files = glob.glob('.pio/build/esp32dev/downloaded_fs_*.bin')
|
||||
with open(files[0], 'rb') as f:
|
||||
data = f.read()
|
||||
|
||||
if is_esp32_wl_image(data):
|
||||
wl = ESP32WearLeveling()
|
||||
state = data[:48]
|
||||
fields = struct.unpack('<IIIIIIII', state[:32])
|
||||
print(f'WL State:')
|
||||
print(f' pos: {fields[0]}')
|
||||
print(f' max_pos: {fields[1]}')
|
||||
print(f' block_size: {fields[5]}')
|
||||
print(f' version: {fields[6]}')
|
||||
"
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [FFat Library Documentation](https://github.com/espressif/arduino-esp32/tree/master/libraries/FFat)
|
||||
- [ESP-IDF FAT Filesystem](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/fatfs.html)
|
||||
@@ -0,0 +1,181 @@
|
||||
# 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)
|
||||
|
||||
```c
|
||||
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:
|
||||
|
||||
```bash
|
||||
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:
|
||||
|
||||
```bash
|
||||
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):
|
||||
|
||||
```python
|
||||
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()`:
|
||||
```bash
|
||||
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](https://github.com/espressif/esp-idf/tree/master/components/wear_levelling)
|
||||
- [ESP-IDF FAT Filesystem](https://github.com/espressif/esp-idf/tree/master/components/fatfs)
|
||||
- [Arduino-ESP32 FFat Library](https://github.com/espressif/arduino-esp32/tree/master/libraries/FFat)
|
||||
- [mk_esp32fat Tool](https://github.com/TobleMiner/mk_esp32fat) (alternative C implementation)
|
||||
|
||||
## License
|
||||
|
||||
Same as platform-espressif32 (Apache 2.0)
|
||||
@@ -0,0 +1,224 @@
|
||||
# ESP32 FAT Filesystem Test Project
|
||||
|
||||
This project tests the FAT filesystem implementation with ESP32 Wear Leveling support.
|
||||
|
||||
## Overview
|
||||
|
||||
This project demonstrates:
|
||||
- Building FAT filesystem images with wear leveling layer
|
||||
- Uploading FAT images to ESP32
|
||||
- Mounting and reading FAT filesystem on ESP32
|
||||
- Downloading and extracting FAT images from ESP32
|
||||
|
||||
## Requirements
|
||||
|
||||
- PlatformIO
|
||||
- ESP32 development board
|
||||
- USB cable
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
arduino-fatfs/
|
||||
├── data/ # Files to be included in FAT image
|
||||
│ ├── test.txt
|
||||
│ ├── README.md
|
||||
│ └── ...
|
||||
├── src/
|
||||
│ └── ffat.ino # Main Arduino sketch
|
||||
├── partitions.csv # Partition table with FAT partition
|
||||
├── platformio.ini # PlatformIO configuration
|
||||
└── unpacked_fs/ # Downloaded files (created by download_fatfs)
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### 1. Build Firmware
|
||||
|
||||
```bash
|
||||
pio run
|
||||
```
|
||||
|
||||
### 2. Build FAT Filesystem Image
|
||||
|
||||
Place your files in the `data/` directory, then:
|
||||
|
||||
```bash
|
||||
pio run -t buildfs
|
||||
```
|
||||
|
||||
This creates a FAT filesystem image with ESP32 wear leveling layer at:
|
||||
`.pio/build/esp32dev/fatfs.bin`
|
||||
|
||||
### 3. Upload Firmware and Filesystem
|
||||
|
||||
```bash
|
||||
# Upload firmware
|
||||
pio run -t upload
|
||||
|
||||
# Upload filesystem
|
||||
pio run -t uploadfs
|
||||
```
|
||||
|
||||
### 4. Monitor Serial Output
|
||||
|
||||
```bash
|
||||
pio run -t monitor
|
||||
```
|
||||
|
||||
Expected output:
|
||||
```
|
||||
FFat mounted successfully
|
||||
Test begin
|
||||
Total space: 1486848
|
||||
Free space: 1482752
|
||||
Listing directory: /
|
||||
FILE: test.txt SIZE: 12
|
||||
FILE: README.md SIZE: 1234
|
||||
Test complete
|
||||
```
|
||||
|
||||
### 5. Download Filesystem from Device
|
||||
|
||||
To download and extract the filesystem from the device:
|
||||
|
||||
```bash
|
||||
pio run -t download_fatfs
|
||||
```
|
||||
|
||||
Files will be extracted to `unpacked_fs/` directory.
|
||||
|
||||
## Partition Table
|
||||
|
||||
The `partitions.csv` defines the flash layout:
|
||||
|
||||
```csv
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x140000,
|
||||
app1, app, ota_1, 0x150000,0x140000,
|
||||
ffat, data, fat, 0x290000,0x170000,
|
||||
```
|
||||
|
||||
The `ffat` partition:
|
||||
- **Type**: data
|
||||
- **SubType**: fat (0x81)
|
||||
- **Offset**: 0x290000 (2,686,976 bytes)
|
||||
- **Size**: 0x170000 (1,507,328 bytes = ~1.44 MB)
|
||||
|
||||
## Wear Leveling
|
||||
|
||||
The FAT filesystem is automatically wrapped with ESP32's wear leveling layer:
|
||||
|
||||
- **Total partition**: 1,507,328 bytes (368 sectors × 4096 bytes)
|
||||
- **WL overhead**: 20,480 bytes (5 sectors)
|
||||
- **FAT data**: 1,486,848 bytes (363 sectors)
|
||||
|
||||
Structure:
|
||||
```
|
||||
[WL State 1][WL State 2][FAT Data][Temp][WL State 3][WL State 4]
|
||||
```
|
||||
|
||||
See [WEAR_LEVELING.md](../../WEAR_LEVELING.md) for details.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "FFat Mount Failed"
|
||||
|
||||
**Possible causes:**
|
||||
1. Filesystem not uploaded
|
||||
2. Wrong partition table
|
||||
3. Corrupted filesystem
|
||||
|
||||
**Solutions:**
|
||||
```bash
|
||||
# Rebuild and upload filesystem
|
||||
pio run -t buildfs
|
||||
pio run -t uploadfs
|
||||
|
||||
# Or erase flash and start fresh
|
||||
pio run -t erase
|
||||
pio run -t upload
|
||||
pio run -t uploadfs
|
||||
```
|
||||
|
||||
### "No FAT filesystem partition found"
|
||||
|
||||
**Cause:** Partition table doesn't have a FAT partition
|
||||
|
||||
**Solution:** Check `partitions.csv` has a partition with `SubType: fat`
|
||||
|
||||
### Files not appearing
|
||||
|
||||
**Cause:** Files not in `data/` directory when building
|
||||
|
||||
**Solution:**
|
||||
1. Add files to `data/` directory
|
||||
2. Rebuild filesystem: `pio run -t buildfs`
|
||||
3. Upload: `pio run -t uploadfs`
|
||||
|
||||
## Code Example
|
||||
|
||||
```cpp
|
||||
#include "FFat.h"
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Mount FAT filesystem
|
||||
if (!FFat.begin(false)) {
|
||||
Serial.println("FFat Mount Failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// List files
|
||||
File root = FFat.open("/");
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
Serial.printf("File: %s, Size: %d\n",
|
||||
file.name(), file.size());
|
||||
file = root.openNextFile();
|
||||
}
|
||||
|
||||
// Read file
|
||||
File f = FFat.open("/test.txt", "r");
|
||||
if (f) {
|
||||
String content = f.readString();
|
||||
Serial.println(content);
|
||||
f.close();
|
||||
}
|
||||
|
||||
// Write file
|
||||
f = FFat.open("/output.txt", "w");
|
||||
if (f) {
|
||||
f.println("Hello from ESP32!");
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Your code here
|
||||
}
|
||||
```
|
||||
|
||||
## Platform Configuration
|
||||
|
||||
This project uses a custom platform with FAT filesystem support:
|
||||
|
||||
```ini
|
||||
[env:esp32dev]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
board = esp32dev
|
||||
board_build.filesystem = fatfs
|
||||
board_build.partitions = partitions.csv
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [ESP32 FFat Library](https://github.com/espressif/arduino-esp32/tree/master/libraries/FFat)
|
||||
- [ESP-IDF FAT Filesystem](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/fatfs.html)
|
||||
- [ESP-IDF Wear Levelling](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/wear-levelling.html)
|
||||
- [Platform-Espressif32 FAT Integration](../platform-espressif32/FATFS_INTEGRATION.md)
|
||||
- [Wear Leveling Implementation](../platform-espressif32/WEAR_LEVELING.md)
|
||||
@@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x140000,
|
||||
app1, app, ota_1, 0x150000,0x140000,
|
||||
ffat, data, fat, 0x290000,0x170000,
|
||||
|
@@ -0,0 +1,7 @@
|
||||
[env:esp32dev]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
board = esp32dev
|
||||
board_build.filesystem = fatfs
|
||||
board_build.partitions = partitions.csv
|
||||
monitor_speed = 115200
|
||||
@@ -0,0 +1 @@
|
||||
Test file
|
||||
@@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x140000,
|
||||
app1, app, ota_1, 0x150000,0x140000,
|
||||
ffat, data, fat, 0x290000,0x170000,
|
||||
|
@@ -0,0 +1,6 @@
|
||||
[env:esp32dev]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
board = esp32dev
|
||||
board_build.filesystem = fatfs
|
||||
board_build.partitions = partitions.csv
|
||||
@@ -0,0 +1,478 @@
|
||||
#include "FS.h"
|
||||
#include "FFat.h"
|
||||
#include "esp_partition.h"
|
||||
|
||||
// Test configuration
|
||||
// Set to true to format the partition (will erase all data!)
|
||||
// Set to false to test the pre-flashed image from data/ directory
|
||||
#define FORMAT_FFAT false // Test pre-flashed image
|
||||
|
||||
// Test settings
|
||||
#define TEST_READ_EXISTING true // Test reading files from flashed image
|
||||
#define TEST_WRITE_NEW true // Test writing new files
|
||||
#define TEST_FILE_IO true // Test file I/O performance
|
||||
|
||||
void listDir(fs::FS &fs, const char *dirname, uint8_t levels) {
|
||||
Serial.printf("Listing directory: %s\r\n", dirname);
|
||||
|
||||
File root = fs.open(dirname);
|
||||
if (!root) {
|
||||
Serial.println("- failed to open directory");
|
||||
return;
|
||||
}
|
||||
if (!root.isDirectory()) {
|
||||
Serial.println(" - not a directory");
|
||||
return;
|
||||
}
|
||||
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
if (file.isDirectory()) {
|
||||
Serial.print(" DIR : ");
|
||||
Serial.println(file.name());
|
||||
if (levels) {
|
||||
listDir(fs, file.path(), levels - 1);
|
||||
}
|
||||
} else {
|
||||
Serial.print(" FILE: ");
|
||||
Serial.print(file.name());
|
||||
Serial.print("\tSIZE: ");
|
||||
Serial.println(file.size());
|
||||
}
|
||||
file = root.openNextFile();
|
||||
}
|
||||
}
|
||||
|
||||
void readFile(fs::FS &fs, const char *path) {
|
||||
Serial.printf("Reading file: %s\r\n", path);
|
||||
|
||||
File file = fs.open(path);
|
||||
if (!file || file.isDirectory()) {
|
||||
Serial.println("- failed to open file for reading");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("- read from file:");
|
||||
while (file.available()) {
|
||||
Serial.write(file.read());
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
void writeFile(fs::FS &fs, const char *path, const char *message) {
|
||||
Serial.printf("Writing file: %s\r\n", path);
|
||||
|
||||
File file = fs.open(path, FILE_WRITE);
|
||||
if (!file) {
|
||||
Serial.println("- failed to open file for writing");
|
||||
return;
|
||||
}
|
||||
if (file.print(message)) {
|
||||
Serial.println("- file written");
|
||||
} else {
|
||||
Serial.println("- write failed");
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
void appendFile(fs::FS &fs, const char *path, const char *message) {
|
||||
Serial.printf("Appending to file: %s\r\n", path);
|
||||
|
||||
File file = fs.open(path, FILE_APPEND);
|
||||
if (!file) {
|
||||
Serial.println("- failed to open file for appending");
|
||||
return;
|
||||
}
|
||||
if (file.print(message)) {
|
||||
Serial.println("- message appended");
|
||||
} else {
|
||||
Serial.println("- append failed");
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
void renameFile(fs::FS &fs, const char *path1, const char *path2) {
|
||||
Serial.printf("Renaming file %s to %s\r\n", path1, path2);
|
||||
if (fs.rename(path1, path2)) {
|
||||
Serial.println("- file renamed");
|
||||
} else {
|
||||
Serial.println("- rename failed");
|
||||
}
|
||||
}
|
||||
|
||||
void deleteFile(fs::FS &fs, const char *path) {
|
||||
Serial.printf("Deleting file: %s\r\n", path);
|
||||
if (fs.remove(path)) {
|
||||
Serial.println("- file deleted");
|
||||
} else {
|
||||
Serial.println("- delete failed");
|
||||
}
|
||||
}
|
||||
|
||||
void testFileIO(fs::FS &fs, const char *path) {
|
||||
Serial.printf("Testing file I/O with %s\r\n", path);
|
||||
|
||||
static uint8_t buf[512];
|
||||
size_t len = 0;
|
||||
File file = fs.open(path, FILE_WRITE);
|
||||
if (!file) {
|
||||
Serial.println("- failed to open file for writing");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
Serial.print("- writing");
|
||||
uint32_t start = millis();
|
||||
for (i = 0; i < 2048; i++) {
|
||||
if ((i & 0x001F) == 0x001F) {
|
||||
Serial.print(".");
|
||||
}
|
||||
file.write(buf, 512);
|
||||
}
|
||||
Serial.println("");
|
||||
uint32_t end = millis() - start;
|
||||
Serial.printf(" - %u bytes written in %lu ms\r\n", 2048 * 512, end);
|
||||
file.close();
|
||||
|
||||
file = fs.open(path);
|
||||
start = millis();
|
||||
end = start;
|
||||
i = 0;
|
||||
if (file && !file.isDirectory()) {
|
||||
len = file.size();
|
||||
size_t flen = len;
|
||||
start = millis();
|
||||
Serial.print("- reading");
|
||||
while (len) {
|
||||
size_t toRead = len;
|
||||
if (toRead > 512) {
|
||||
toRead = 512;
|
||||
}
|
||||
file.read(buf, toRead);
|
||||
if ((i++ & 0x001F) == 0x001F) {
|
||||
Serial.print(".");
|
||||
}
|
||||
len -= toRead;
|
||||
}
|
||||
Serial.println("");
|
||||
end = millis() - start;
|
||||
Serial.printf("- %u bytes read in %lu ms\r\n", flen, end);
|
||||
file.close();
|
||||
} else {
|
||||
Serial.println("- failed to open file for reading");
|
||||
}
|
||||
}
|
||||
|
||||
void testExistingFiles(fs::FS &fs) {
|
||||
Serial.println("\n=== Testing Pre-Flashed Files ===");
|
||||
|
||||
// List all files in root
|
||||
Serial.println("\nFiles in root directory:");
|
||||
listDir(fs, "/", 2);
|
||||
|
||||
// Test reading specific files that should exist
|
||||
const char* testFiles[] = {
|
||||
"/test.txt",
|
||||
"/README.md",
|
||||
"/platformio.ini",
|
||||
"/partitions.csv"
|
||||
};
|
||||
|
||||
Serial.println("\nReading test files:");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (fs.exists(testFiles[i])) {
|
||||
Serial.printf("\n--- File: %s ---\n", testFiles[i]);
|
||||
readFile(fs, testFiles[i]);
|
||||
} else {
|
||||
Serial.printf("File not found: %s\n", testFiles[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void testWriteOperations(fs::FS &fs) {
|
||||
Serial.println("\n=== Testing Write Operations ===");
|
||||
|
||||
// Test creating new file
|
||||
Serial.println("\n1. Creating new file...");
|
||||
writeFile(fs, "/test_write.txt", "Hello from ESP32!\n");
|
||||
|
||||
// Test appending
|
||||
Serial.println("\n2. Appending to file...");
|
||||
appendFile(fs, "/test_write.txt", "This line was appended.\n");
|
||||
appendFile(fs, "/test_write.txt", "And another line.\n");
|
||||
|
||||
// Read back
|
||||
Serial.println("\n3. Reading back written file:");
|
||||
readFile(fs, "/test_write.txt");
|
||||
|
||||
// Test rename
|
||||
Serial.println("\n4. Testing rename...");
|
||||
renameFile(fs, "/test_write.txt", "/renamed.txt");
|
||||
readFile(fs, "/renamed.txt");
|
||||
|
||||
// Test delete
|
||||
Serial.println("\n5. Testing delete...");
|
||||
deleteFile(fs, "/renamed.txt");
|
||||
|
||||
// Verify deletion
|
||||
if (!fs.exists("/renamed.txt")) {
|
||||
Serial.println("File successfully deleted");
|
||||
} else {
|
||||
Serial.println("ERROR: File still exists!");
|
||||
}
|
||||
}
|
||||
|
||||
void testFileSystem(fs::FS &fs) {
|
||||
Serial.println("\n=== Filesystem Information ===");
|
||||
Serial.printf("Total space: %10u bytes (%.2f MB)\n",
|
||||
FFat.totalBytes(), FFat.totalBytes() / 1024.0 / 1024.0);
|
||||
Serial.printf("Used space: %10u bytes (%.2f MB)\n",
|
||||
FFat.usedBytes(), FFat.usedBytes() / 1024.0 / 1024.0);
|
||||
Serial.printf("Free space: %10u bytes (%.2f MB)\n",
|
||||
FFat.freeBytes(), FFat.freeBytes() / 1024.0 / 1024.0);
|
||||
|
||||
float usage = (FFat.usedBytes() * 100.0) / FFat.totalBytes();
|
||||
Serial.printf("Usage: %.1f%%\n", usage);
|
||||
}
|
||||
|
||||
void printSeparator() {
|
||||
Serial.println("\n============================================================");
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(1000); // Wait for serial monitor
|
||||
|
||||
// Enable ESP-IDF debug logging
|
||||
esp_log_level_set("*", ESP_LOG_DEBUG);
|
||||
esp_log_level_set("vfs_fat", ESP_LOG_VERBOSE);
|
||||
esp_log_level_set("wear_levelling", ESP_LOG_VERBOSE);
|
||||
|
||||
Serial.println("\n\n");
|
||||
printSeparator();
|
||||
Serial.println("ESP32 FFat Filesystem Test");
|
||||
Serial.println("Testing pre-flashed image with Wear Leveling");
|
||||
printSeparator();
|
||||
|
||||
// Format if requested
|
||||
if (FORMAT_FFAT) {
|
||||
Serial.println("\n============================================================");
|
||||
Serial.println("WARNING: Formatting FFat partition...");
|
||||
Serial.println("This will erase all data!");
|
||||
Serial.println("============================================================");
|
||||
delay(2000);
|
||||
|
||||
// First try to mount the WL layer without formatting
|
||||
Serial.println("\nStep 1: Checking if WL layer can be mounted...");
|
||||
if (FFat.begin(false, "/ffat", 10, "ffat")) {
|
||||
Serial.println("✓ WL layer mounted successfully!");
|
||||
Serial.println(" Now formatting filesystem...");
|
||||
FFat.end();
|
||||
} else {
|
||||
Serial.println("✗ WL layer mount failed - will try to format anyway");
|
||||
}
|
||||
|
||||
Serial.println("\nStep 2: Formatting partition...");
|
||||
if (!FFat.format(false, (char*)"ffat")) {
|
||||
Serial.println("ERROR: FFat Format Failed");
|
||||
Serial.println("\nThis means the Wear Leveling layer itself is broken.");
|
||||
Serial.println("The WL structure on flash is not compatible with ESP-IDF.");
|
||||
return;
|
||||
}
|
||||
Serial.println("✓ FFat formatted successfully");
|
||||
|
||||
Serial.println("\nStep 3: Mounting formatted partition...");
|
||||
if (!FFat.begin(false, "/ffat", 10, "ffat")) {
|
||||
Serial.println("ERROR: Cannot mount even after format!");
|
||||
return;
|
||||
}
|
||||
Serial.println("✓ Mounted successfully after format");
|
||||
|
||||
// Write some test files so we have data to compare
|
||||
Serial.println("\nWriting test files...");
|
||||
writeFile(FFat, "/test.txt", "This is a test file created by ESP32\n");
|
||||
writeFile(FFat, "/README.md", "# ESP32 Formatted Filesystem\n\nThis was formatted on device.\n");
|
||||
|
||||
Serial.println("\n*** IMPORTANT: Now download this filesystem ***");
|
||||
Serial.println("Run: pio run -t download_fatfs");
|
||||
Serial.println("This will save the ESP32-formatted image for comparison");
|
||||
|
||||
delay(5000); // Give time to read the message
|
||||
}
|
||||
|
||||
// Mount the filesystem
|
||||
Serial.println("\nMounting FFat filesystem...");
|
||||
Serial.println("Partition label: 'ffat'");
|
||||
Serial.println("Format on fail: false");
|
||||
Serial.println("Max open files: 10");
|
||||
|
||||
if (!FFat.begin(false, "/ffat", 10, "ffat")) { // formatOnFail = false
|
||||
Serial.println("\nERROR: FFat Mount Failed");
|
||||
Serial.println("\nDiagnostics:");
|
||||
Serial.println("1. Checking partition...");
|
||||
|
||||
// Try to get partition info
|
||||
const esp_partition_t* partition = esp_partition_find_first(
|
||||
ESP_PARTITION_TYPE_DATA,
|
||||
ESP_PARTITION_SUBTYPE_DATA_FAT,
|
||||
"ffat"
|
||||
);
|
||||
|
||||
if (partition) {
|
||||
Serial.printf(" ✓ Partition found: %s\n", partition->label);
|
||||
Serial.printf(" Address: 0x%06X\n", partition->address);
|
||||
Serial.printf(" Size: %u bytes (%.2f MB)\n",
|
||||
partition->size, partition->size / 1024.0 / 1024.0);
|
||||
|
||||
// Check FAT boot sector at offset 0 (RAW FAT, no WL layer in flash)
|
||||
Serial.println("\n2. Checking FAT boot sector...");
|
||||
uint8_t buffer[512];
|
||||
if (esp_partition_read(partition, 0, buffer, 512) == ESP_OK) {
|
||||
Serial.println(" First 64 bytes at offset 0 (RAW FAT):");
|
||||
for (int i = 0; i < 64; i++) {
|
||||
Serial.printf("%02X ", buffer[i]);
|
||||
if ((i + 1) % 16 == 0) Serial.println();
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
// Check boot signature
|
||||
if (buffer[510] == 0x55 && buffer[511] == 0xAA) {
|
||||
Serial.println(" ✓ Boot signature found (0x55AA)");
|
||||
|
||||
// Parse boot sector
|
||||
char oem[9];
|
||||
memcpy(oem, buffer + 3, 8);
|
||||
oem[8] = 0;
|
||||
uint16_t bytes_per_sector = buffer[11] | (buffer[12] << 8);
|
||||
uint16_t total_sectors = buffer[19] | (buffer[20] << 8);
|
||||
|
||||
Serial.printf(" OEM Name: '%s'\n", oem);
|
||||
Serial.printf(" Bytes per sector: %u\n", bytes_per_sector);
|
||||
Serial.printf(" Total sectors: %u\n", total_sectors);
|
||||
|
||||
if (bytes_per_sector == 4096 && total_sectors == 362) {
|
||||
Serial.println(" ✓ Correct format for ESP32 FFat!");
|
||||
}
|
||||
} else {
|
||||
Serial.printf(" ✗ Invalid boot signature: 0x%02X%02X (expected 0x55AA)\n",
|
||||
buffer[511], buffer[510]);
|
||||
}
|
||||
}
|
||||
|
||||
// Check FAT table
|
||||
Serial.println("\n3. Checking FAT table...");
|
||||
if (esp_partition_read(partition, 4096, buffer, 32) == ESP_OK) {
|
||||
Serial.println(" FAT1 (first 32 bytes at offset 4096):");
|
||||
for (int i = 0; i < 32; i++) {
|
||||
Serial.printf("%02X ", buffer[i]);
|
||||
if ((i + 1) % 16 == 0) Serial.println();
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
if (buffer[0] == 0xF8 && buffer[1] == 0xFF && buffer[2] == 0xFF) {
|
||||
Serial.println(" ✓ Media descriptor correct (F8 FF FF)");
|
||||
|
||||
// Check if rest is 00
|
||||
bool clean = true;
|
||||
for (int i = 3; i < 32; i++) {
|
||||
if (buffer[i] != 0x00) {
|
||||
clean = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (clean) {
|
||||
Serial.println(" ✓ FAT table clean (all zeros after media descriptor)");
|
||||
} else {
|
||||
Serial.println(" ✗ FAT table has non-zero bytes after media descriptor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check WL metadata at end of partition
|
||||
Serial.println("\n4. Checking WL metadata at end...");
|
||||
// WL state1 should be at partition_size - 3 * sector_size
|
||||
uint32_t wl_state1_offset = partition->size - (3 * 4096);
|
||||
if (esp_partition_read(partition, wl_state1_offset, buffer, 48) == ESP_OK) {
|
||||
Serial.printf(" WL State at offset 0x%06X (first 48 bytes):\n", wl_state1_offset);
|
||||
for (int i = 0; i < 48; i++) {
|
||||
Serial.printf("%02X ", buffer[i]);
|
||||
if ((i + 1) % 16 == 0) Serial.println();
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
// Parse WL_State
|
||||
uint32_t* words = (uint32_t*)buffer;
|
||||
Serial.printf(" pos: %u\n", words[0]);
|
||||
Serial.printf(" max_pos: %u\n", words[1]);
|
||||
Serial.printf(" block_size: %u\n", words[5]);
|
||||
Serial.printf(" version: %u\n", words[6]);
|
||||
|
||||
if (words[5] == 4096 && words[6] == 2) {
|
||||
Serial.println(" ✓ WL metadata looks valid");
|
||||
} else {
|
||||
Serial.println(" ✗ WL metadata invalid");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Serial.println(" ✗ Partition 'ffat' not found!");
|
||||
Serial.println(" Check partition table in platformio.ini");
|
||||
}
|
||||
|
||||
Serial.println("\nPossible causes:");
|
||||
Serial.println("- Corrupted FAT filesystem");
|
||||
Serial.println("- Wrong sector size (should be 4096)");
|
||||
Serial.println("- Wrong sector count (should be 362)");
|
||||
Serial.println("\nTry: pio run -t erase && pio run -t upload && pio run -t uploadfs");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("✓ FFat mounted successfully!");
|
||||
|
||||
// Show filesystem info
|
||||
testFileSystem(FFat);
|
||||
|
||||
// Test reading existing files
|
||||
if (TEST_READ_EXISTING) {
|
||||
printSeparator();
|
||||
testExistingFiles(FFat);
|
||||
}
|
||||
|
||||
// Test write operations
|
||||
if (TEST_WRITE_NEW) {
|
||||
printSeparator();
|
||||
testWriteOperations(FFat);
|
||||
|
||||
// Show updated filesystem info
|
||||
printSeparator();
|
||||
Serial.println("\nFilesystem after write tests:");
|
||||
testFileSystem(FFat);
|
||||
}
|
||||
|
||||
// Test file I/O performance
|
||||
if (TEST_FILE_IO) {
|
||||
printSeparator();
|
||||
testFileIO(FFat, "/benchmark.bin");
|
||||
|
||||
// Clean up benchmark file
|
||||
deleteFile(FFat, "/benchmark.bin");
|
||||
}
|
||||
|
||||
// Final directory listing
|
||||
printSeparator();
|
||||
Serial.println("\nFinal directory listing:");
|
||||
listDir(FFat, "/", 2);
|
||||
|
||||
printSeparator();
|
||||
Serial.println("\n✓ All tests completed!");
|
||||
Serial.println("\nFilesystem remains mounted for further testing.");
|
||||
Serial.println("You can now:");
|
||||
Serial.println("- Download filesystem: pio run -t download_fatfs");
|
||||
Serial.println("- Reset to re-run tests");
|
||||
printSeparator();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Keep the filesystem mounted
|
||||
// You can add interactive commands here if needed
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.pio
|
||||
.vscode
|
||||
unpacked_fs/
|
||||
@@ -0,0 +1,12 @@
|
||||
# LittleFS Test Data
|
||||
|
||||
This directory contains test files that will be included in the LittleFS filesystem image.
|
||||
|
||||
## Files
|
||||
|
||||
- `test.txt` - Simple text file
|
||||
- `README.md` - This file
|
||||
- `platformio.ini` - Copy of project configuration
|
||||
- `partitions.csv` - Copy of partition table
|
||||
|
||||
These files will be flashed to the ESP32's LittleFS partition and can be read by the firmware.
|
||||
@@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x140000,
|
||||
app1, app, ota_1, 0x150000,0x140000,
|
||||
spiffs, data, spiffs, 0x290000,0x170000,
|
||||
|
@@ -0,0 +1,6 @@
|
||||
[env:esp32dev]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
board = esp32dev
|
||||
board_build.filesystem = littlefs
|
||||
board_build.partitions = partitions.csv
|
||||
@@ -0,0 +1,2 @@
|
||||
Hello from LittleFS!
|
||||
This is a test file.
|
||||
@@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x140000,
|
||||
app1, app, ota_1, 0x150000,0x140000,
|
||||
spiffs, data, spiffs, 0x290000,0x170000,
|
||||
|
@@ -0,0 +1,6 @@
|
||||
[env:esp32dev]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
board = esp32dev
|
||||
board_build.filesystem = littlefs
|
||||
board_build.partitions = partitions.csv
|
||||
@@ -0,0 +1,324 @@
|
||||
#include "FS.h"
|
||||
#include "LittleFS.h"
|
||||
|
||||
// Test configuration
|
||||
#define FORMAT_LITTLEFS false // Set to true to format (will erase all data!)
|
||||
|
||||
// Test settings
|
||||
#define TEST_READ_EXISTING true // Test reading files from flashed image
|
||||
#define TEST_WRITE_NEW true // Test writing new files
|
||||
#define TEST_FILE_IO true // Test file I/O performance
|
||||
|
||||
void listDir(fs::FS &fs, const char *dirname, uint8_t levels) {
|
||||
Serial.printf("Listing directory: %s\r\n", dirname);
|
||||
|
||||
File root = fs.open(dirname);
|
||||
if (!root) {
|
||||
Serial.println("- failed to open directory");
|
||||
return;
|
||||
}
|
||||
if (!root.isDirectory()) {
|
||||
Serial.println(" - not a directory");
|
||||
return;
|
||||
}
|
||||
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
if (file.isDirectory()) {
|
||||
Serial.print(" DIR : ");
|
||||
Serial.println(file.name());
|
||||
if (levels) {
|
||||
listDir(fs, file.path(), levels - 1);
|
||||
}
|
||||
} else {
|
||||
Serial.print(" FILE: ");
|
||||
Serial.print(file.name());
|
||||
Serial.print("\tSIZE: ");
|
||||
Serial.println(file.size());
|
||||
}
|
||||
file = root.openNextFile();
|
||||
}
|
||||
}
|
||||
|
||||
void readFile(fs::FS &fs, const char *path) {
|
||||
Serial.printf("Reading file: %s\r\n", path);
|
||||
|
||||
File file = fs.open(path);
|
||||
if (!file || file.isDirectory()) {
|
||||
Serial.println("- failed to open file for reading");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("- read from file:");
|
||||
while (file.available()) {
|
||||
Serial.write(file.read());
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
void writeFile(fs::FS &fs, const char *path, const char *message) {
|
||||
Serial.printf("Writing file: %s\r\n", path);
|
||||
|
||||
File file = fs.open(path, FILE_WRITE);
|
||||
if (!file) {
|
||||
Serial.println("- failed to open file for writing");
|
||||
return;
|
||||
}
|
||||
if (file.print(message)) {
|
||||
Serial.println("- file written");
|
||||
} else {
|
||||
Serial.println("- write failed");
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
void appendFile(fs::FS &fs, const char *path, const char *message) {
|
||||
Serial.printf("Appending to file: %s\r\n", path);
|
||||
|
||||
File file = fs.open(path, FILE_APPEND);
|
||||
if (!file) {
|
||||
Serial.println("- failed to open file for appending");
|
||||
return;
|
||||
}
|
||||
if (file.print(message)) {
|
||||
Serial.println("- message appended");
|
||||
} else {
|
||||
Serial.println("- append failed");
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
void renameFile(fs::FS &fs, const char *path1, const char *path2) {
|
||||
Serial.printf("Renaming file %s to %s\r\n", path1, path2);
|
||||
if (fs.rename(path1, path2)) {
|
||||
Serial.println("- file renamed");
|
||||
} else {
|
||||
Serial.println("- rename failed");
|
||||
}
|
||||
}
|
||||
|
||||
void deleteFile(fs::FS &fs, const char *path) {
|
||||
Serial.printf("Deleting file: %s\r\n", path);
|
||||
if (fs.remove(path)) {
|
||||
Serial.println("- file deleted");
|
||||
} else {
|
||||
Serial.println("- delete failed");
|
||||
}
|
||||
}
|
||||
|
||||
void testFileIO(fs::FS &fs, const char *path) {
|
||||
Serial.printf("Testing file I/O with %s\r\n", path);
|
||||
|
||||
static uint8_t buf[512];
|
||||
size_t len = 0;
|
||||
File file = fs.open(path, FILE_WRITE);
|
||||
if (!file) {
|
||||
Serial.println("- failed to open file for writing");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
Serial.print("- writing");
|
||||
uint32_t start = millis();
|
||||
for (i = 0; i < 2048; i++) {
|
||||
if ((i & 0x001F) == 0x001F) {
|
||||
Serial.print(".");
|
||||
}
|
||||
file.write(buf, 512);
|
||||
}
|
||||
Serial.println("");
|
||||
uint32_t end = millis() - start;
|
||||
Serial.printf(" - %u bytes written in %lu ms\r\n", 2048 * 512, end);
|
||||
file.close();
|
||||
|
||||
file = fs.open(path);
|
||||
start = millis();
|
||||
end = start;
|
||||
i = 0;
|
||||
if (file && !file.isDirectory()) {
|
||||
len = file.size();
|
||||
size_t flen = len;
|
||||
start = millis();
|
||||
Serial.print("- reading");
|
||||
while (len) {
|
||||
size_t toRead = len;
|
||||
if (toRead > 512) {
|
||||
toRead = 512;
|
||||
}
|
||||
file.read(buf, toRead);
|
||||
if ((i++ & 0x001F) == 0x001F) {
|
||||
Serial.print(".");
|
||||
}
|
||||
len -= toRead;
|
||||
}
|
||||
Serial.println("");
|
||||
end = millis() - start;
|
||||
Serial.printf("- %u bytes read in %lu ms\r\n", flen, end);
|
||||
file.close();
|
||||
} else {
|
||||
Serial.println("- failed to open file for reading");
|
||||
}
|
||||
}
|
||||
|
||||
void testExistingFiles(fs::FS &fs) {
|
||||
Serial.println("\n=== Testing Pre-Flashed Files ===");
|
||||
|
||||
// List all files in root
|
||||
Serial.println("\nFiles in root directory:");
|
||||
listDir(fs, "/", 2);
|
||||
|
||||
// Test reading specific files that should exist
|
||||
const char* testFiles[] = {
|
||||
"/test.txt",
|
||||
"/README.md",
|
||||
"/platformio.ini",
|
||||
"/partitions.csv"
|
||||
};
|
||||
|
||||
Serial.println("\nReading test files:");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (fs.exists(testFiles[i])) {
|
||||
Serial.printf("\n--- File: %s ---\n", testFiles[i]);
|
||||
readFile(fs, testFiles[i]);
|
||||
} else {
|
||||
Serial.printf("File not found: %s\n", testFiles[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void testWriteOperations(fs::FS &fs) {
|
||||
Serial.println("\n=== Testing Write Operations ===");
|
||||
|
||||
// Test creating new file
|
||||
Serial.println("\n1. Creating new file...");
|
||||
writeFile(fs, "/test_write.txt", "Hello from ESP32!\n");
|
||||
|
||||
// Test appending
|
||||
Serial.println("\n2. Appending to file...");
|
||||
appendFile(fs, "/test_write.txt", "This line was appended.\n");
|
||||
appendFile(fs, "/test_write.txt", "And another line.\n");
|
||||
|
||||
// Read back
|
||||
Serial.println("\n3. Reading back written file:");
|
||||
readFile(fs, "/test_write.txt");
|
||||
|
||||
// Test rename
|
||||
Serial.println("\n4. Testing rename...");
|
||||
renameFile(fs, "/test_write.txt", "/renamed.txt");
|
||||
readFile(fs, "/renamed.txt");
|
||||
|
||||
// Test delete
|
||||
Serial.println("\n5. Testing delete...");
|
||||
deleteFile(fs, "/renamed.txt");
|
||||
|
||||
// Verify deletion
|
||||
if (!fs.exists("/renamed.txt")) {
|
||||
Serial.println("File successfully deleted");
|
||||
} else {
|
||||
Serial.println("ERROR: File still exists!");
|
||||
}
|
||||
}
|
||||
|
||||
void testFileSystem(fs::FS &fs) {
|
||||
Serial.println("\n=== Filesystem Information ===");
|
||||
Serial.printf("Total space: %10u bytes (%.2f MB)\n",
|
||||
LittleFS.totalBytes(), LittleFS.totalBytes() / 1024.0 / 1024.0);
|
||||
Serial.printf("Used space: %10u bytes (%.2f MB)\n",
|
||||
LittleFS.usedBytes(), LittleFS.usedBytes() / 1024.0 / 1024.0);
|
||||
|
||||
float usage = (LittleFS.usedBytes() * 100.0) / LittleFS.totalBytes();
|
||||
Serial.printf("Usage: %.1f%%\n", usage);
|
||||
}
|
||||
|
||||
void printSeparator() {
|
||||
Serial.println("\n============================================================");
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(1000); // Wait for serial monitor
|
||||
|
||||
Serial.println("\n\n");
|
||||
printSeparator();
|
||||
Serial.println("ESP32 LittleFS Filesystem Test");
|
||||
Serial.println("Testing pre-flashed LittleFS image");
|
||||
printSeparator();
|
||||
|
||||
// Format if requested
|
||||
if (FORMAT_LITTLEFS) {
|
||||
Serial.println("\n============================================================");
|
||||
Serial.println("WARNING: Formatting LittleFS partition...");
|
||||
Serial.println("This will erase all data!");
|
||||
Serial.println("============================================================");
|
||||
delay(2000);
|
||||
|
||||
if (!LittleFS.format()) {
|
||||
Serial.println("ERROR: LittleFS Format Failed");
|
||||
return;
|
||||
}
|
||||
Serial.println(" LittleFS formatted successfully");
|
||||
}
|
||||
|
||||
// Mount the filesystem
|
||||
Serial.println("\nMounting LittleFS filesystem...");
|
||||
Serial.println("Format on fail: false");
|
||||
|
||||
if (!LittleFS.begin(false)) { // formatOnFail = false
|
||||
Serial.println("\nERROR: LittleFS Mount Failed");
|
||||
Serial.println("\nPossible causes:");
|
||||
Serial.println("- Filesystem not uploaded");
|
||||
Serial.println("- Corrupted LittleFS filesystem");
|
||||
Serial.println("- Wrong partition table");
|
||||
Serial.println("\nTry: pio run -t uploadfs");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println(" LittleFS mounted successfully!");
|
||||
|
||||
// Show filesystem info
|
||||
testFileSystem(LittleFS);
|
||||
|
||||
// Test reading existing files
|
||||
if (TEST_READ_EXISTING) {
|
||||
printSeparator();
|
||||
testExistingFiles(LittleFS);
|
||||
}
|
||||
|
||||
// Test write operations
|
||||
if (TEST_WRITE_NEW) {
|
||||
printSeparator();
|
||||
testWriteOperations(LittleFS);
|
||||
|
||||
// Show updated filesystem info
|
||||
printSeparator();
|
||||
Serial.println("\nFilesystem after write tests:");
|
||||
testFileSystem(LittleFS);
|
||||
}
|
||||
|
||||
// Test file I/O performance
|
||||
if (TEST_FILE_IO) {
|
||||
printSeparator();
|
||||
testFileIO(LittleFS, "/benchmark.bin");
|
||||
|
||||
// Clean up benchmark file
|
||||
deleteFile(LittleFS, "/benchmark.bin");
|
||||
}
|
||||
|
||||
// Final directory listing
|
||||
printSeparator();
|
||||
Serial.println("\nFinal directory listing:");
|
||||
listDir(LittleFS, "/", 2);
|
||||
|
||||
printSeparator();
|
||||
Serial.println("\n All tests completed!");
|
||||
Serial.println("\nFilesystem remains mounted for further testing.");
|
||||
Serial.println("You can now:");
|
||||
Serial.println("- Download filesystem: pio run -t download_fs");
|
||||
Serial.println("- Reset to re-run tests");
|
||||
printSeparator();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Keep the filesystem mounted
|
||||
// You can add interactive commands here if needed
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.pio
|
||||
.vscode
|
||||
unpacked_fs/
|
||||
@@ -0,0 +1,247 @@
|
||||
# ESP32 SPIFFS Filesystem Test Project
|
||||
|
||||
This project tests the SPIFFS filesystem implementation on ESP32.
|
||||
|
||||
## Overview
|
||||
|
||||
This project demonstrates:
|
||||
- Building SPIFFS filesystem images
|
||||
- Uploading SPIFFS images to ESP32
|
||||
- Mounting and reading SPIFFS filesystem on ESP32
|
||||
- Downloading and extracting SPIFFS images from ESP32
|
||||
|
||||
## Requirements
|
||||
|
||||
- PlatformIO
|
||||
- ESP32 development board
|
||||
- USB cable
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
arduino-spiffs/
|
||||
├── data/ # Files to be included in SPIFFS image
|
||||
│ ├── test.txt
|
||||
│ ├── README.md
|
||||
│ └── ...
|
||||
├── src/
|
||||
│ └── spiffs_test.ino # Main Arduino sketch
|
||||
├── partitions.csv # Partition table with SPIFFS partition
|
||||
├── platformio.ini # PlatformIO configuration
|
||||
└── unpacked_fs/ # Downloaded files (created by download_spiffs)
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### 1. Build Firmware
|
||||
|
||||
```bash
|
||||
pio run
|
||||
```
|
||||
|
||||
### 2. Build SPIFFS Filesystem Image
|
||||
|
||||
Place your files in the `data/` directory, then:
|
||||
|
||||
```bash
|
||||
pio run -t buildfs
|
||||
```
|
||||
|
||||
This creates a SPIFFS filesystem image at:
|
||||
`.pio/build/esp32dev/spiffs.bin`
|
||||
|
||||
### 3. Upload Firmware and Filesystem
|
||||
|
||||
```bash
|
||||
# Upload firmware
|
||||
pio run -t upload
|
||||
|
||||
# Upload filesystem
|
||||
pio run -t uploadfs
|
||||
```
|
||||
|
||||
### 4. Monitor Serial Output
|
||||
|
||||
```bash
|
||||
pio run -t monitor
|
||||
```
|
||||
|
||||
Expected output:
|
||||
```
|
||||
SPIFFS mounted successfully
|
||||
Total space: 1507328
|
||||
Used space: 12345
|
||||
Listing directory: /
|
||||
FILE: test.txt SIZE: 12
|
||||
FILE: README.md SIZE: 456
|
||||
Test complete
|
||||
```
|
||||
|
||||
### 5. Download Filesystem from Device
|
||||
|
||||
To download and extract the filesystem from the device:
|
||||
|
||||
```bash
|
||||
pio run -t download_spiffs
|
||||
```
|
||||
|
||||
Files will be extracted to `unpacked_fs/` directory.
|
||||
|
||||
## Partition Table
|
||||
|
||||
The `partitions.csv` defines the flash layout:
|
||||
|
||||
```csv
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x140000,
|
||||
app1, app, ota_1, 0x150000,0x140000,
|
||||
spiffs, data, spiffs, 0x290000,0x170000,
|
||||
```
|
||||
|
||||
The `spiffs` partition:
|
||||
- **Type**: data
|
||||
- **SubType**: spiffs (0x82)
|
||||
- **Offset**: 0x290000 (2,686,976 bytes)
|
||||
- **Size**: 0x170000 (1,507,328 bytes = ~1.44 MB)
|
||||
|
||||
## SPIFFS Configuration
|
||||
|
||||
Default SPIFFS configuration (can be customized in `platformio.ini`):
|
||||
|
||||
```ini
|
||||
board_build.spiffs.page_size = 256
|
||||
board_build.spiffs.block_size = 4096
|
||||
board_build.spiffs.obj_name_len = 32
|
||||
board_build.spiffs.meta_len = 4
|
||||
board_build.spiffs.use_magic = true
|
||||
board_build.spiffs.use_magic_len = true
|
||||
board_build.spiffs.aligned_obj_ix_tables = false
|
||||
```
|
||||
|
||||
These match ESP-IDF defaults:
|
||||
- `CONFIG_SPIFFS_PAGE_SIZE = 256`
|
||||
- `CONFIG_SPIFFS_OBJ_NAME_LEN = 32`
|
||||
- `CONFIG_SPIFFS_META_LENGTH = 4`
|
||||
- `CONFIG_SPIFFS_USE_MAGIC = true`
|
||||
- `CONFIG_SPIFFS_USE_MAGIC_LENGTH = true`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "SPIFFS Mount Failed"
|
||||
|
||||
**Possible causes:**
|
||||
1. Filesystem not uploaded
|
||||
2. Wrong partition table
|
||||
3. Corrupted filesystem
|
||||
|
||||
**Solutions:**
|
||||
```bash
|
||||
# Rebuild and upload filesystem
|
||||
pio run -t buildfs
|
||||
pio run -t uploadfs
|
||||
|
||||
# Or erase flash and start fresh
|
||||
pio run -t erase
|
||||
pio run -t upload
|
||||
pio run -t uploadfs
|
||||
```
|
||||
|
||||
### "No SPIFFS filesystem partition found"
|
||||
|
||||
**Cause:** Partition table doesn't have a SPIFFS partition
|
||||
|
||||
**Solution:** Check `partitions.csv` has a partition with `SubType: spiffs`
|
||||
|
||||
### Files not appearing
|
||||
|
||||
**Cause:** Files not in `data/` directory when building
|
||||
|
||||
**Solution:**
|
||||
1. Add files to `data/` directory
|
||||
2. Rebuild filesystem: `pio run -t buildfs`
|
||||
3. Upload: `pio run -t uploadfs`
|
||||
|
||||
## Code Example
|
||||
|
||||
```cpp
|
||||
#include "FS.h"
|
||||
#include "SPIFFS.h"
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Mount SPIFFS filesystem
|
||||
if (!SPIFFS.begin(false)) {
|
||||
Serial.println("SPIFFS Mount Failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// List files
|
||||
File root = SPIFFS.open("/");
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
Serial.printf("File: %s, Size: %d\n",
|
||||
file.name(), file.size());
|
||||
file = root.openNextFile();
|
||||
}
|
||||
|
||||
// Read file
|
||||
File f = SPIFFS.open("/test.txt", "r");
|
||||
if (f) {
|
||||
String content = f.readString();
|
||||
Serial.println(content);
|
||||
f.close();
|
||||
}
|
||||
|
||||
// Write file
|
||||
f = SPIFFS.open("/output.txt", "w");
|
||||
if (f) {
|
||||
f.println("Hello from ESP32!");
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Your code here
|
||||
}
|
||||
```
|
||||
|
||||
## Platform Configuration
|
||||
|
||||
This project uses a custom platform with SPIFFS filesystem support:
|
||||
|
||||
```ini
|
||||
[env:esp32dev]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
board = esp32dev
|
||||
board_build.filesystem = spiffs
|
||||
board_build.partitions = partitions.csv
|
||||
```
|
||||
|
||||
## Comparison with Other Filesystems
|
||||
|
||||
| Feature | SPIFFS | LittleFS | FatFS |
|
||||
|---------|--------|----------|-------|
|
||||
| Wear Leveling | Built-in | Built-in | Requires WL layer |
|
||||
| Max File Size | ~1MB | Limited by partition | 4GB |
|
||||
| Directories | No | Yes | Yes |
|
||||
| Performance | Medium | Fast | Fast |
|
||||
| RAM Usage | Low | Low | Medium |
|
||||
| Reliability | Good | Excellent | Good |
|
||||
|
||||
## References
|
||||
|
||||
- [ESP32 SPIFFS Library](https://github.com/espressif/arduino-esp32/tree/master/libraries/SPIFFS)
|
||||
- [ESP-IDF SPIFFS](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/spiffs.html)
|
||||
- [SPIFFS Specification](https://github.com/pellepl/spiffs)
|
||||
|
||||
## Notes
|
||||
|
||||
- SPIFFS does not support directories (flat filesystem)
|
||||
- File names are limited to 32 characters by default
|
||||
- SPIFFS has built-in wear leveling
|
||||
- Maximum file size depends on available RAM
|
||||
- SPIFFS is being deprecated in favor of LittleFS in newer ESP-IDF versions
|
||||
@@ -0,0 +1,12 @@
|
||||
# SPIFFS Test Data
|
||||
|
||||
This directory contains test files that will be included in the SPIFFS filesystem image.
|
||||
|
||||
## Files
|
||||
|
||||
- `test.txt` - Simple text file
|
||||
- `README.md` - This file
|
||||
- `platformio.ini` - Copy of project configuration
|
||||
- `partitions.csv` - Copy of partition table
|
||||
|
||||
These files will be flashed to the ESP32's SPIFFS partition and can be read by the firmware.
|
||||
@@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x140000,
|
||||
app1, app, ota_1, 0x150000,0x140000,
|
||||
spiffs, data, spiffs, 0x290000,0x170000,
|
||||
|
@@ -0,0 +1,6 @@
|
||||
[env:esp32dev]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
board = esp32dev
|
||||
board_build.filesystem = spiffs
|
||||
board_build.partitions = partitions.csv
|
||||
@@ -0,0 +1,2 @@
|
||||
Hello from SPIFFS!
|
||||
This is a test file.
|
||||
@@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x140000,
|
||||
app1, app, ota_1, 0x150000,0x140000,
|
||||
spiffs, data, spiffs, 0x290000,0x170000,
|
||||
|
@@ -0,0 +1,6 @@
|
||||
[env:esp32dev]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
board = esp32dev
|
||||
board_build.filesystem = spiffs
|
||||
board_build.partitions = partitions.csv
|
||||
@@ -0,0 +1,324 @@
|
||||
#include "FS.h"
|
||||
#include "SPIFFS.h"
|
||||
|
||||
// Test configuration
|
||||
#define FORMAT_SPIFFS false // Set to true to format (will erase all data!)
|
||||
|
||||
// Test settings
|
||||
#define TEST_READ_EXISTING true // Test reading files from flashed image
|
||||
#define TEST_WRITE_NEW true // Test writing new files
|
||||
#define TEST_FILE_IO true // Test file I/O performance
|
||||
|
||||
void listDir(fs::FS &fs, const char *dirname, uint8_t levels) {
|
||||
Serial.printf("Listing directory: %s\r\n", dirname);
|
||||
|
||||
File root = fs.open(dirname);
|
||||
if (!root) {
|
||||
Serial.println("- failed to open directory");
|
||||
return;
|
||||
}
|
||||
if (!root.isDirectory()) {
|
||||
Serial.println(" - not a directory");
|
||||
return;
|
||||
}
|
||||
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
if (file.isDirectory()) {
|
||||
Serial.print(" DIR : ");
|
||||
Serial.println(file.name());
|
||||
if (levels) {
|
||||
listDir(fs, file.path(), levels - 1);
|
||||
}
|
||||
} else {
|
||||
Serial.print(" FILE: ");
|
||||
Serial.print(file.name());
|
||||
Serial.print("\tSIZE: ");
|
||||
Serial.println(file.size());
|
||||
}
|
||||
file = root.openNextFile();
|
||||
}
|
||||
}
|
||||
|
||||
void readFile(fs::FS &fs, const char *path) {
|
||||
Serial.printf("Reading file: %s\r\n", path);
|
||||
|
||||
File file = fs.open(path);
|
||||
if (!file || file.isDirectory()) {
|
||||
Serial.println("- failed to open file for reading");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("- read from file:");
|
||||
while (file.available()) {
|
||||
Serial.write(file.read());
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
void writeFile(fs::FS &fs, const char *path, const char *message) {
|
||||
Serial.printf("Writing file: %s\r\n", path);
|
||||
|
||||
File file = fs.open(path, FILE_WRITE);
|
||||
if (!file) {
|
||||
Serial.println("- failed to open file for writing");
|
||||
return;
|
||||
}
|
||||
if (file.print(message)) {
|
||||
Serial.println("- file written");
|
||||
} else {
|
||||
Serial.println("- write failed");
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
void appendFile(fs::FS &fs, const char *path, const char *message) {
|
||||
Serial.printf("Appending to file: %s\r\n", path);
|
||||
|
||||
File file = fs.open(path, FILE_APPEND);
|
||||
if (!file) {
|
||||
Serial.println("- failed to open file for appending");
|
||||
return;
|
||||
}
|
||||
if (file.print(message)) {
|
||||
Serial.println("- message appended");
|
||||
} else {
|
||||
Serial.println("- append failed");
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
void renameFile(fs::FS &fs, const char *path1, const char *path2) {
|
||||
Serial.printf("Renaming file %s to %s\r\n", path1, path2);
|
||||
if (fs.rename(path1, path2)) {
|
||||
Serial.println("- file renamed");
|
||||
} else {
|
||||
Serial.println("- rename failed");
|
||||
}
|
||||
}
|
||||
|
||||
void deleteFile(fs::FS &fs, const char *path) {
|
||||
Serial.printf("Deleting file: %s\r\n", path);
|
||||
if (fs.remove(path)) {
|
||||
Serial.println("- file deleted");
|
||||
} else {
|
||||
Serial.println("- delete failed");
|
||||
}
|
||||
}
|
||||
|
||||
void testFileIO(fs::FS &fs, const char *path) {
|
||||
Serial.printf("Testing file I/O with %s\r\n", path);
|
||||
|
||||
static uint8_t buf[512];
|
||||
size_t len = 0;
|
||||
File file = fs.open(path, FILE_WRITE);
|
||||
if (!file) {
|
||||
Serial.println("- failed to open file for writing");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
Serial.print("- writing");
|
||||
uint32_t start = millis();
|
||||
for (i = 0; i < 2048; i++) {
|
||||
if ((i & 0x001F) == 0x001F) {
|
||||
Serial.print(".");
|
||||
}
|
||||
file.write(buf, 512);
|
||||
}
|
||||
Serial.println("");
|
||||
uint32_t end = millis() - start;
|
||||
Serial.printf(" - %u bytes written in %lu ms\r\n", 2048 * 512, end);
|
||||
file.close();
|
||||
|
||||
file = fs.open(path);
|
||||
start = millis();
|
||||
end = start;
|
||||
i = 0;
|
||||
if (file && !file.isDirectory()) {
|
||||
len = file.size();
|
||||
size_t flen = len;
|
||||
start = millis();
|
||||
Serial.print("- reading");
|
||||
while (len) {
|
||||
size_t toRead = len;
|
||||
if (toRead > 512) {
|
||||
toRead = 512;
|
||||
}
|
||||
file.read(buf, toRead);
|
||||
if ((i++ & 0x001F) == 0x001F) {
|
||||
Serial.print(".");
|
||||
}
|
||||
len -= toRead;
|
||||
}
|
||||
Serial.println("");
|
||||
end = millis() - start;
|
||||
Serial.printf("- %u bytes read in %lu ms\r\n", flen, end);
|
||||
file.close();
|
||||
} else {
|
||||
Serial.println("- failed to open file for reading");
|
||||
}
|
||||
}
|
||||
|
||||
void testExistingFiles(fs::FS &fs) {
|
||||
Serial.println("\n=== Testing Pre-Flashed Files ===");
|
||||
|
||||
// List all files in root
|
||||
Serial.println("\nFiles in root directory:");
|
||||
listDir(fs, "/", 2);
|
||||
|
||||
// Test reading specific files that should exist
|
||||
const char* testFiles[] = {
|
||||
"/test.txt",
|
||||
"/README.md",
|
||||
"/platformio.ini",
|
||||
"/partitions.csv"
|
||||
};
|
||||
|
||||
Serial.println("\nReading test files:");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (fs.exists(testFiles[i])) {
|
||||
Serial.printf("\n--- File: %s ---\n", testFiles[i]);
|
||||
readFile(fs, testFiles[i]);
|
||||
} else {
|
||||
Serial.printf("File not found: %s\n", testFiles[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void testWriteOperations(fs::FS &fs) {
|
||||
Serial.println("\n=== Testing Write Operations ===");
|
||||
|
||||
// Test creating new file
|
||||
Serial.println("\n1. Creating new file...");
|
||||
writeFile(fs, "/test_write.txt", "Hello from ESP32!\n");
|
||||
|
||||
// Test appending
|
||||
Serial.println("\n2. Appending to file...");
|
||||
appendFile(fs, "/test_write.txt", "This line was appended.\n");
|
||||
appendFile(fs, "/test_write.txt", "And another line.\n");
|
||||
|
||||
// Read back
|
||||
Serial.println("\n3. Reading back written file:");
|
||||
readFile(fs, "/test_write.txt");
|
||||
|
||||
// Test rename
|
||||
Serial.println("\n4. Testing rename...");
|
||||
renameFile(fs, "/test_write.txt", "/renamed.txt");
|
||||
readFile(fs, "/renamed.txt");
|
||||
|
||||
// Test delete
|
||||
Serial.println("\n5. Testing delete...");
|
||||
deleteFile(fs, "/renamed.txt");
|
||||
|
||||
// Verify deletion
|
||||
if (!fs.exists("/renamed.txt")) {
|
||||
Serial.println("File successfully deleted");
|
||||
} else {
|
||||
Serial.println("ERROR: File still exists!");
|
||||
}
|
||||
}
|
||||
|
||||
void testFileSystem(fs::FS &fs) {
|
||||
Serial.println("\n=== Filesystem Information ===");
|
||||
Serial.printf("Total space: %10u bytes (%.2f MB)\n",
|
||||
SPIFFS.totalBytes(), SPIFFS.totalBytes() / 1024.0 / 1024.0);
|
||||
Serial.printf("Used space: %10u bytes (%.2f MB)\n",
|
||||
SPIFFS.usedBytes(), SPIFFS.usedBytes() / 1024.0 / 1024.0);
|
||||
|
||||
float usage = (SPIFFS.usedBytes() * 100.0) / SPIFFS.totalBytes();
|
||||
Serial.printf("Usage: %.1f%%\n", usage);
|
||||
}
|
||||
|
||||
void printSeparator() {
|
||||
Serial.println("\n============================================================");
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(1000); // Wait for serial monitor
|
||||
|
||||
Serial.println("\n\n");
|
||||
printSeparator();
|
||||
Serial.println("ESP32 SPIFFS Filesystem Test");
|
||||
Serial.println("Testing pre-flashed SPIFFS image");
|
||||
printSeparator();
|
||||
|
||||
// Format if requested
|
||||
if (FORMAT_SPIFFS) {
|
||||
Serial.println("\n============================================================");
|
||||
Serial.println("WARNING: Formatting SPIFFS partition...");
|
||||
Serial.println("This will erase all data!");
|
||||
Serial.println("============================================================");
|
||||
delay(2000);
|
||||
|
||||
if (!SPIFFS.format()) {
|
||||
Serial.println("ERROR: SPIFFS Format Failed");
|
||||
return;
|
||||
}
|
||||
Serial.println("✓ SPIFFS formatted successfully");
|
||||
}
|
||||
|
||||
// Mount the filesystem
|
||||
Serial.println("\nMounting SPIFFS filesystem...");
|
||||
Serial.println("Format on fail: false");
|
||||
|
||||
if (!SPIFFS.begin(false)) { // formatOnFail = false
|
||||
Serial.println("\nERROR: SPIFFS Mount Failed");
|
||||
Serial.println("\nPossible causes:");
|
||||
Serial.println("- Filesystem not uploaded");
|
||||
Serial.println("- Corrupted SPIFFS filesystem");
|
||||
Serial.println("- Wrong partition table");
|
||||
Serial.println("\nTry: pio run -t uploadfs");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("✓ SPIFFS mounted successfully!");
|
||||
|
||||
// Show filesystem info
|
||||
testFileSystem(SPIFFS);
|
||||
|
||||
// Test reading existing files
|
||||
if (TEST_READ_EXISTING) {
|
||||
printSeparator();
|
||||
testExistingFiles(SPIFFS);
|
||||
}
|
||||
|
||||
// Test write operations
|
||||
if (TEST_WRITE_NEW) {
|
||||
printSeparator();
|
||||
testWriteOperations(SPIFFS);
|
||||
|
||||
// Show updated filesystem info
|
||||
printSeparator();
|
||||
Serial.println("\nFilesystem after write tests:");
|
||||
testFileSystem(SPIFFS);
|
||||
}
|
||||
|
||||
// Test file I/O performance
|
||||
if (TEST_FILE_IO) {
|
||||
printSeparator();
|
||||
testFileIO(SPIFFS, "/benchmark.bin");
|
||||
|
||||
// Clean up benchmark file
|
||||
deleteFile(SPIFFS, "/benchmark.bin");
|
||||
}
|
||||
|
||||
// Final directory listing
|
||||
printSeparator();
|
||||
Serial.println("\nFinal directory listing:");
|
||||
listDir(SPIFFS, "/", 2);
|
||||
|
||||
printSeparator();
|
||||
Serial.println("\n✓ All tests completed!");
|
||||
Serial.println("\nFilesystem remains mounted for further testing.");
|
||||
Serial.println("You can now:");
|
||||
Serial.println("- Download filesystem: pio run -t download_spiffs");
|
||||
Serial.println("- Reset to re-run tests");
|
||||
printSeparator();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Keep the filesystem mounted
|
||||
// You can add interactive commands here if needed
|
||||
}
|
||||
Reference in New Issue
Block a user