Skip to content

Commit 7206b2f

Browse files
lbernstoneme-no-dev
authored andcommitted
FAT on SPI Flash Library (espressif#1809)
* First commit of FFat library * Fixed reboot loops if no fat present. Added CMakeLists * Functionalize the partition checks * Cleanup, especially in format * Dont format if mounted. More wording cleanup * 16M ffat should only be on 16M board * Fix some casting issues that trip up the compiler when building as ESP-IDF component
1 parent 3028ec4 commit 7206b2f

File tree

8 files changed

+415
-12
lines changed

8 files changed

+415
-12
lines changed

CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ set(LIBRARY_SRCS
4040
libraries/DNSServer/src/DNSServer.cpp
4141
libraries/EEPROM/EEPROM.cpp
4242
libraries/ESPmDNS/src/ESPmDNS.cpp
43+
libraries/FFat/src/FFat.cpp
4344
libraries/FS/src/FS.cpp
4445
libraries/FS/src/vfs_api.cpp
4546
libraries/HTTPClient/src/HTTPClient.cpp
@@ -175,6 +176,7 @@ set(COMPONENT_ADD_INCLUDEDIRS
175176
libraries/DNSServer/src
176177
libraries/ESP32/src
177178
libraries/ESPmDNS/src
179+
libraries/FFat/src
178180
libraries/FS/src
179181
libraries/HTTPClient/src
180182
libraries/NetBIOS/src

boards.txt

+5
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ esp32.menu.PartitionScheme.no_ota.upload.maximum_size=2097152
4646
esp32.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (Large APPS with OTA)
4747
esp32.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs
4848
esp32.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080
49+
esp32.menu.PartitionScheme.fatflash=16M Fat
50+
esp32.menu.PartitionScheme.fatflash.build.partitions=ffat
4951

5052
esp32.menu.FlashMode.qio=QIO
5153
esp32.menu.FlashMode.qio.build.flash_mode=dio
@@ -70,6 +72,9 @@ esp32.menu.FlashSize.4M.build.flash_size=4MB
7072
esp32.menu.FlashSize.2M=2MB (16Mb)
7173
esp32.menu.FlashSize.2M.build.flash_size=2MB
7274
esp32.menu.FlashSize.2M.build.partitions=minimal
75+
esp32.menu.FlashSize.16M=16MB (128Mb)
76+
esp32.menu.FlashSize.16M.build.flash_size=16MB
77+
esp32.menu.FlashSize.16M.build.partitions=ffat
7378

7479
esp32.menu.UploadSpeed.921600=921600
7580
esp32.menu.UploadSpeed.921600.upload.speed=921600
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
#include "FS.h"
2+
#include "FFat.h"
3+
4+
// You only need to format FFat the first time you run a test
5+
#define FORMAT_FFAT true
6+
7+
void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
8+
Serial.printf("Listing directory: %s\r\n", dirname);
9+
10+
File root = fs.open(dirname);
11+
if(!root){
12+
Serial.println("- failed to open directory");
13+
return;
14+
}
15+
if(!root.isDirectory()){
16+
Serial.println(" - not a directory");
17+
return;
18+
}
19+
20+
File file = root.openNextFile();
21+
while(file){
22+
if(file.isDirectory()){
23+
Serial.print(" DIR : ");
24+
Serial.println(file.name());
25+
if(levels){
26+
listDir(fs, file.name(), levels -1);
27+
}
28+
} else {
29+
Serial.print(" FILE: ");
30+
Serial.print(file.name());
31+
Serial.print("\tSIZE: ");
32+
Serial.println(file.size());
33+
}
34+
file = root.openNextFile();
35+
}
36+
}
37+
38+
void readFile(fs::FS &fs, const char * path){
39+
Serial.printf("Reading file: %s\r\n", path);
40+
41+
File file = fs.open(path);
42+
if(!file || file.isDirectory()){
43+
Serial.println("- failed to open file for reading");
44+
return;
45+
}
46+
47+
Serial.println("- read from file:");
48+
while(file.available()){
49+
Serial.write(file.read());
50+
}
51+
}
52+
53+
void writeFile(fs::FS &fs, const char * path, const char * message){
54+
Serial.printf("Writing file: %s\r\n", path);
55+
56+
File file = fs.open(path, FILE_WRITE);
57+
if(!file){
58+
Serial.println("- failed to open file for writing");
59+
return;
60+
}
61+
if(file.print(message)){
62+
Serial.println("- file written");
63+
} else {
64+
Serial.println("- frite failed");
65+
}
66+
}
67+
68+
void appendFile(fs::FS &fs, const char * path, const char * message){
69+
Serial.printf("Appending to file: %s\r\n", path);
70+
71+
File file = fs.open(path, FILE_APPEND);
72+
if(!file){
73+
Serial.println("- failed to open file for appending");
74+
return;
75+
}
76+
if(file.print(message)){
77+
Serial.println("- message appended");
78+
} else {
79+
Serial.println("- append failed");
80+
}
81+
}
82+
83+
void renameFile(fs::FS &fs, const char * path1, const char * path2){
84+
Serial.printf("Renaming file %s to %s\r\n", path1, path2);
85+
if (fs.rename(path1, path2)) {
86+
Serial.println("- file renamed");
87+
} else {
88+
Serial.println("- rename failed");
89+
}
90+
}
91+
92+
void deleteFile(fs::FS &fs, const char * path){
93+
Serial.printf("Deleting file: %s\r\n", path);
94+
if(fs.remove(path)){
95+
Serial.println("- file deleted");
96+
} else {
97+
Serial.println("- delete failed");
98+
}
99+
}
100+
101+
void testFileIO(fs::FS &fs, const char * path){
102+
Serial.printf("Testing file I/O with %s\r\n", path);
103+
104+
static uint8_t buf[512];
105+
size_t len = 0;
106+
File file = fs.open(path, FILE_WRITE);
107+
if(!file){
108+
Serial.println("- failed to open file for writing");
109+
return;
110+
}
111+
112+
size_t i;
113+
Serial.print("- writing" );
114+
uint32_t start = millis();
115+
for(i=0; i<2048; i++){
116+
if ((i & 0x001F) == 0x001F){
117+
Serial.print(".");
118+
}
119+
file.write(buf, 512);
120+
}
121+
Serial.println("");
122+
uint32_t end = millis() - start;
123+
Serial.printf(" - %u bytes written in %u ms\r\n", 2048 * 512, end);
124+
file.close();
125+
126+
file = fs.open(path);
127+
start = millis();
128+
end = start;
129+
i = 0;
130+
if(file && !file.isDirectory()){
131+
len = file.size();
132+
size_t flen = len;
133+
start = millis();
134+
Serial.print("- reading" );
135+
while(len){
136+
size_t toRead = len;
137+
if(toRead > 512){
138+
toRead = 512;
139+
}
140+
file.read(buf, toRead);
141+
if ((i++ & 0x001F) == 0x001F){
142+
Serial.print(".");
143+
}
144+
len -= toRead;
145+
}
146+
Serial.println("");
147+
end = millis() - start;
148+
Serial.printf("- %u bytes read in %u ms\r\n", flen, end);
149+
file.close();
150+
} else {
151+
Serial.println("- failed to open file for reading");
152+
}
153+
}
154+
155+
void setup(){
156+
Serial.begin(115200);
157+
Serial.setDebugOutput(true);
158+
if (FORMAT_FFAT) FFat.format();
159+
if(!FFat.begin()){
160+
Serial.println("FFat Mount Failed");
161+
return;
162+
}
163+
164+
Serial.printf("Total space: %10lu\n", FFat.totalBytes());
165+
Serial.printf("Free space: %10lu\n", FFat.freeBytes());
166+
listDir(FFat, "/", 0);
167+
writeFile(FFat, "/hello.txt", "Hello ");
168+
appendFile(FFat, "/hello.txt", "World!\r\n");
169+
readFile(FFat, "/hello.txt");
170+
renameFile(FFat, "/hello.txt", "/foo.txt");
171+
readFile(FFat, "/foo.txt");
172+
deleteFile(FFat, "/foo.txt");
173+
testFileIO(FFat, "/test.txt");
174+
Serial.printf("Free space: %10lu\n", FFat.freeBytes());
175+
deleteFile(FFat, "/test.txt");
176+
Serial.println( "Test complete" );
177+
}
178+
179+
void loop(){
180+
181+
}

libraries/FFat/library.properties

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
name=FFat
2+
version=1.0
3+
author=Hristo Gochkov, Ivan Grokhtkov, Larry Bernstone
4+
maintainer=Hristo Gochkov <[email protected]>
5+
sentence=ESP32 FAT on Flash File System
6+
paragraph=
7+
category=Data Storage
8+
url=
9+
architectures=esp32

libraries/FFat/src/FFat.cpp

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "vfs_api.h"
16+
extern "C" {
17+
#include "esp_vfs_fat.h"
18+
#include "diskio.h"
19+
#include "diskio_wl.h"
20+
#include "vfs_fat_internal.h"
21+
}
22+
#include "FFat.h"
23+
24+
using namespace fs;
25+
26+
F_Fat::F_Fat(FSImplPtr impl)
27+
: FS(impl)
28+
{}
29+
30+
const esp_partition_t *check_ffat_partition(const char* label)
31+
{
32+
const esp_partition_t* ck_part = esp_partition_find_first(
33+
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, label);
34+
if (!ck_part) {
35+
log_e("No FAT partition found with label %s", label);
36+
return NULL;
37+
}
38+
return ck_part;
39+
}
40+
41+
bool F_Fat::begin(bool formatOnFail, const char * basePath, uint8_t maxOpenFiles, const char * partitionLabel)
42+
{
43+
if(_wl_handle){
44+
log_w("Already Mounted!");
45+
return true;
46+
}
47+
48+
if (!check_ffat_partition(partitionLabel)) return false;
49+
50+
esp_vfs_fat_mount_config_t conf = {
51+
.format_if_mount_failed = formatOnFail,
52+
.max_files = maxOpenFiles
53+
};
54+
esp_err_t err = esp_vfs_fat_spiflash_mount(basePath, partitionLabel, &conf, &_wl_handle);
55+
if(err){
56+
log_e("Mounting FFat partition failed! Error: %d", err);
57+
return false;
58+
}
59+
_impl->mountpoint(basePath);
60+
return true;
61+
}
62+
63+
void F_Fat::end()
64+
{
65+
if(_wl_handle){
66+
esp_err_t err = esp_vfs_fat_spiflash_unmount(_impl->mountpoint(), _wl_handle);
67+
if(err){
68+
log_e("Unmounting FFat partition failed! Error: %d", err);
69+
return;
70+
}
71+
_wl_handle = NULL;
72+
_impl->mountpoint(NULL);
73+
}
74+
}
75+
76+
bool F_Fat::format(bool full_wipe, char* partitionLabel)
77+
{
78+
esp_err_t result;
79+
if(_wl_handle){
80+
log_w("Already Mounted!");
81+
return false;
82+
}
83+
wl_handle_t temp_handle;
84+
// Attempt to mount to see if there is already data
85+
const esp_partition_t *ffat_partition = check_ffat_partition(partitionLabel);
86+
if (!ffat_partition) return false;
87+
result = wl_mount(ffat_partition, &temp_handle);
88+
89+
if (result == ESP_OK) {
90+
// Wipe disk- quick just wipes the FAT. Full zeroes the whole disk
91+
uint32_t wipe_size = full_wipe ? wl_size(temp_handle) : 16384;
92+
wl_erase_range(temp_handle, 0, wipe_size);
93+
wl_unmount(temp_handle);
94+
}
95+
// Now do a mount with format_if_fail (which it will)
96+
esp_vfs_fat_mount_config_t conf = {
97+
.format_if_mount_failed = true,
98+
.max_files = 1
99+
};
100+
result = esp_vfs_fat_spiflash_mount("/format_ffat", partitionLabel, &conf, &temp_handle);
101+
esp_vfs_fat_spiflash_unmount("/format_ffat", temp_handle);
102+
return result;
103+
}
104+
105+
size_t F_Fat::totalBytes()
106+
{
107+
FATFS *fs;
108+
DWORD free_clust, tot_sect, sect_size;
109+
110+
BYTE pdrv = ff_diskio_get_pdrv_wl(_wl_handle);
111+
char drv[3] = {(char)(48+pdrv), ':', 0};
112+
FRESULT res = f_getfree(drv, &free_clust, &fs);
113+
tot_sect = (fs->n_fatent - 2) * fs->csize;
114+
sect_size = CONFIG_WL_SECTOR_SIZE;
115+
return tot_sect * sect_size;
116+
}
117+
118+
size_t F_Fat::freeBytes()
119+
{
120+
121+
FATFS *fs;
122+
DWORD free_clust, free_sect, sect_size;
123+
124+
BYTE pdrv = ff_diskio_get_pdrv_wl(_wl_handle);
125+
char drv[3] = {(char)(48+pdrv), ':', 0};
126+
FRESULT res = f_getfree(drv, &free_clust, &fs);
127+
free_sect = free_clust * fs->csize;
128+
sect_size = CONFIG_WL_SECTOR_SIZE;
129+
return free_sect * sect_size;
130+
}
131+
132+
bool F_Fat::exists(const char* path)
133+
{
134+
File f = open(path, "r");
135+
return (f == true) && !f.isDirectory();
136+
}
137+
138+
bool F_Fat::exists(const String& path)
139+
{
140+
return exists(path.c_str());
141+
}
142+
143+
144+
F_Fat FFat = F_Fat(FSImplPtr(new VFSImpl()));

0 commit comments

Comments
 (0)