From cf60cf4c3ba9aaa1fe40b0f718b9eddc8722143a Mon Sep 17 00:00:00 2001 From: lorol Date: Tue, 3 Nov 2020 16:06:40 -0500 Subject: [PATCH] Adding LITTLEFS after esp_littlefs (IDF) is built-in (#4483) Tools idea: https://github.com/lorol/arduino-esp32fs-plugin --- CMakeLists.txt | 2 + .../examples/LITTLEFS_PlatformIO/.gitignore | 4 + .../examples/LITTLEFS_PlatformIO/README.md | 68 +++++ .../LITTLEFS_PlatformIO/data/file1.txt | 1 + .../data/testfolder/test2.txt | 1 + .../include/.placeholder.txt | 0 .../LITTLEFS_PlatformIO/lib/.placeholder.txt | 0 .../LITTLEFS_PlatformIO/littlefsbuilder.py | 2 + .../LITTLEFS_PlatformIO/partitions_custom.csv | 6 + .../LITTLEFS_PlatformIO/platformio.ini | 35 +++ .../examples/LITTLEFS_PlatformIO/src/main.cpp | 282 ++++++++++++++++++ .../examples/LITTLEFS_test/LITTLEFS_test.ino | 272 +++++++++++++++++ .../examples/LITTLEFS_time/LITTLEFS_time.ino | 214 +++++++++++++ libraries/LITTLEFS/library.properties | 9 + libraries/LITTLEFS/src/LITTLEFS.cpp | 107 +++++++ libraries/LITTLEFS/src/LITTLEFS.h | 38 +++ libraries/Update/src/Update.h | 1 + libraries/Update/src/Updater.cpp | 17 +- package/package_esp32_index.template.json | 75 +++++ 19 files changed, 1130 insertions(+), 4 deletions(-) create mode 100644 libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/.gitignore create mode 100644 libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/README.md create mode 100644 libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/data/file1.txt create mode 100644 libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/data/testfolder/test2.txt create mode 100644 libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/include/.placeholder.txt create mode 100644 libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/lib/.placeholder.txt create mode 100644 libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/littlefsbuilder.py create mode 100644 libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/partitions_custom.csv create mode 100644 libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/platformio.ini create mode 100644 libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/src/main.cpp create mode 100644 libraries/LITTLEFS/examples/LITTLEFS_test/LITTLEFS_test.ino create mode 100644 libraries/LITTLEFS/examples/LITTLEFS_time/LITTLEFS_time.ino create mode 100644 libraries/LITTLEFS/library.properties create mode 100644 libraries/LITTLEFS/src/LITTLEFS.cpp create mode 100644 libraries/LITTLEFS/src/LITTLEFS.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8dae5b9e1..09ec488c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,7 @@ set(LIBRARY_SRCS libraries/FS/src/vfs_api.cpp libraries/HTTPClient/src/HTTPClient.cpp libraries/HTTPUpdate/src/HTTPUpdate.cpp + libraries/LITTLEFS/src/LITTLEFS.cpp libraries/NetBIOS/src/NetBIOS.cpp libraries/Preferences/src/Preferences.cpp libraries/SD_MMC/src/SD_MMC.cpp @@ -130,6 +131,7 @@ set(includedirs libraries/FS/src libraries/HTTPClient/src libraries/HTTPUpdate/src + libraries/LITTLEFS/src libraries/NetBIOS/src libraries/Preferences/src libraries/SD_MMC/src diff --git a/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/.gitignore b/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/.gitignore new file mode 100644 index 000000000..87515a624 --- /dev/null +++ b/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/.gitignore @@ -0,0 +1,4 @@ +.pio +.vscode +mklittlefs.exe +mklittlefs \ No newline at end of file diff --git a/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/README.md b/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/README.md new file mode 100644 index 000000000..773445b6d --- /dev/null +++ b/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/README.md @@ -0,0 +1,68 @@ +# How to run on PlatformIO IDE + +- Download and extract to this project root a **mklittlefs** executable for your OS [from a zipped binary here](https://github.com/earlephilhower/mklittlefs/releases) +- Open **LITTLEFS_PlatformIO** folder +- Run PlatformIO project task: **Upload Filesystem Image** +- Run PlatformIO project task: **Upload and Monitor** +- You will see a Serial output like: +``` +--- Miniterm on COM5 115200,8,N,1 --- +--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- +ets Jun 8 2016 00:22:57 + +rst:0x1 (POWERON_RESET),boot:0x13 (Snfigsip: 0, SPIWP:0xee +clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 +mode:DIO, clock div:2 +load:0x3fff0018,len:4 +load:0x3fff001c,len:1044 +load:0x40078000,len:10044 +load:0x40080400,len:5872 +entry 0x400806ac +Listing directory: / + FILE: /file1.txt SIZE: 3 LAST WRITE: 2020-10-06 15:10:33 + DIR : /testfolder LAST WRITE: 2020-10-06 15:10:33 +Creating Dir: /mydir +Dir created +Writing file: /mydir/hello2.txt +- file written +Listing directory: / + FILE: /file1.txt SIZE: 3 LAST WRITE: 2020-10-06 15:10:33 + DIR : /mydir LAST WRITE: 1970-01-01 00:00:00 +Listing directory: /mydir + FILE: /mydir/hello2.txt SIZE: 6 LAST WRITE: 1970-01-01 00:00:00 + DIR : /testfolder LAST WRITE: 2020-10-06 15:10:33 +Listing directory: /testfolder + FILE: /testfolder/test2.txt SIZE: 3 LAST WRITE: 2020-10-06 15:10:33 +Deleting file: /mydir/hello2.txt +- file deleted +Removing Dir: /mydir +Dir removed +Listing directory: / + FILE: /file1.txt SIZE: 3 LAST WRITE: 2020-10-06 15:10:33 + DIR : /testfolder LAST WRITE: 2020-10-06 15:10:33 +Listing directory: /testfolder + FILE: /testfolder/test2.txt SIZE: 3 LAST WRITE: 2020-10-06 15:10:33 +Writing file: /hello.txt +- file written +Appending to file: /hello.txt +- message appended +Reading file: /hello.txt +- read from file: +Hello World! +Renaming file /hello.txt to /foo.txt +- file renamed +Reading file: /foo.txt +- read from file: +Hello World! +Deleting file: /foo.txt +- file deleted +Testing file I/O with /test.txt +- writing................................................................ + - 1048576 bytes written in 12006 ms +- reading................................................................ +- 1048576 bytes read in 547 ms +Deleting file: /test.txt +- file deleted +Test complete +``` +- If you have a module with more than 4MB flash, you can uncomment **partitions_custom.csv** in **platformio.ini** and modify the csv file accordingly \ No newline at end of file diff --git a/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/data/file1.txt b/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/data/file1.txt new file mode 100644 index 000000000..7c4a013e5 --- /dev/null +++ b/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/data/file1.txt @@ -0,0 +1 @@ +aaa \ No newline at end of file diff --git a/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/data/testfolder/test2.txt b/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/data/testfolder/test2.txt new file mode 100644 index 000000000..01f02e32c --- /dev/null +++ b/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/data/testfolder/test2.txt @@ -0,0 +1 @@ +bbb \ No newline at end of file diff --git a/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/include/.placeholder.txt b/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/include/.placeholder.txt new file mode 100644 index 000000000..e69de29bb diff --git a/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/lib/.placeholder.txt b/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/lib/.placeholder.txt new file mode 100644 index 000000000..e69de29bb diff --git a/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/littlefsbuilder.py b/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/littlefsbuilder.py new file mode 100644 index 000000000..93937e296 --- /dev/null +++ b/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/littlefsbuilder.py @@ -0,0 +1,2 @@ +Import("env") +env.Replace( MKSPIFFSTOOL=env.get("PROJECT_DIR") + '/mklittlefs' ) \ No newline at end of file diff --git a/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/partitions_custom.csv b/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/partitions_custom.csv new file mode 100644 index 000000000..97846fa59 --- /dev/null +++ b/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/partitions_custom.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +ota_0, app, ota_0, 0x10000, 0x1A0000, +ota_1, app, ota_1, , 0x1A0000, +otadata, data, ota, 0x350000, 0x2000, +nvs, data, nvs, , 0x6000, +data, data, spiffs, , 0xA8000, diff --git a/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/platformio.ini b/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/platformio.ini new file mode 100644 index 000000000..43e34ec0d --- /dev/null +++ b/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/platformio.ini @@ -0,0 +1,35 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[platformio] +default_envs = esp32 + +[env] +framework = arduino + +[env:esp32] +platform = espressif32 +;platform = https://github.com/platformio/platform-espressif32.git +;board_build.mcu = esp32 +platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git + +build_flags = + ${env.build_flags} + -D=${PIOENV} + ;-D CONFIG_LITTLEFS_FOR_IDF_3_2 + +lib_deps = https://github.com/lorol/LITTLEFS.git + +board = esp32dev +;board_build.partitions = partitions_custom.csv +monitor_filters = esp32_exception_decoder +monitor_speed = 115200 + +extra_scripts = ./littlefsbuilder.py diff --git a/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/src/main.cpp b/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/src/main.cpp new file mode 100644 index 000000000..fe601ef48 --- /dev/null +++ b/libraries/LITTLEFS/examples/LITTLEFS_PlatformIO/src/main.cpp @@ -0,0 +1,282 @@ +#include +#include "FS.h" +#include +#include + +/* You only need to format LITTLEFS the first time you run a + test or else use the LITTLEFS plugin to create a partition + https://github.com/lorol/arduino-esp32littlefs-plugin */ + +#define FORMAT_LITTLEFS_IF_FAILED true + +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.print(file.name()); + time_t t= file.getLastWrite(); + struct tm * tmstruct = localtime(&t); + Serial.printf(" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour , tmstruct->tm_min, tmstruct->tm_sec); + + if(levels){ + listDir(fs, file.name(), levels -1); + } + } else { + Serial.print(" FILE: "); + Serial.print(file.name()); + Serial.print(" SIZE: "); + + Serial.print(file.size()); + time_t t= file.getLastWrite(); + struct tm * tmstruct = localtime(&t); + Serial.printf(" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour , tmstruct->tm_min, tmstruct->tm_sec); + } + file = root.openNextFile(); + } +} + +void createDir(fs::FS &fs, const char * path){ + Serial.printf("Creating Dir: %s\n", path); + if(fs.mkdir(path)){ + Serial.println("Dir created"); + } else { + Serial.println("mkdir failed"); + } +} + +void removeDir(fs::FS &fs, const char * path){ + Serial.printf("Removing Dir: %s\n", path); + if(fs.rmdir(path)){ + Serial.println("Dir removed"); + } else { + Serial.println("rmdir failed"); + } +} + +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"); + } +} + +// SPIFFS-like write and delete file + +// See: https://github.com/esp8266/Arduino/blob/master/libraries/LittleFS/src/LittleFS.cpp#L60 +void writeFile2(fs::FS &fs, const char * path, const char * message){ + if(!fs.exists(path)){ + if (strchr(path, '/')) { + Serial.printf("Create missing folders of: %s\r\n", path); + char *pathStr = strdup(path); + if (pathStr) { + char *ptr = strchr(pathStr, '/'); + while (ptr) { + *ptr = 0; + fs.mkdir(pathStr); + *ptr = '/'; + ptr = strchr(ptr+1, '/'); + } + } + free(pathStr); + } + } + + Serial.printf("Writing file to: %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(); +} + +// See: https://github.com/esp8266/Arduino/blob/master/libraries/LittleFS/src/LittleFS.h#L149 +void deleteFile2(fs::FS &fs, const char * path){ + Serial.printf("Deleting file and empty folders on path: %s\r\n", path); + + if(fs.remove(path)){ + Serial.println("- file deleted"); + } else { + Serial.println("- delete failed"); + } + + char *pathStr = strdup(path); + if (pathStr) { + char *ptr = strrchr(pathStr, '/'); + if (ptr) { + Serial.printf("Removing all empty folders on path: %s\r\n", path); + } + while (ptr) { + *ptr = 0; + fs.rmdir(pathStr); + ptr = strrchr(pathStr, '/'); + } + free(pathStr); + } +} + +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 %u 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 %u ms\r\n", flen, end); + file.close(); + } else { + Serial.println("- failed to open file for reading"); + } +} + +void setup(){ + Serial.begin(115200); + if(!LITTLEFS.begin(FORMAT_LITTLEFS_IF_FAILED)){ + Serial.println("LITTLEFS Mount Failed"); + return; + } + + listDir(LITTLEFS, "/", 0); + createDir(LITTLEFS, "/mydir"); + writeFile(LITTLEFS, "/mydir/hello2.txt", "Hello2"); + //writeFile(LITTLEFS, "/mydir/newdir2/newdir3/hello3.txt", "Hello3"); + writeFile2(LITTLEFS, "/mydir/newdir2/newdir3/hello3.txt", "Hello3"); + listDir(LITTLEFS, "/", 3); + deleteFile(LITTLEFS, "/mydir/hello2.txt"); + //deleteFile(LITTLEFS, "/mydir/newdir2/newdir3/hello3.txt"); + deleteFile2(LITTLEFS, "/mydir/newdir2/newdir3/hello3.txt"); + removeDir(LITTLEFS, "/mydir"); + listDir(LITTLEFS, "/", 3); + writeFile(LITTLEFS, "/hello.txt", "Hello "); + appendFile(LITTLEFS, "/hello.txt", "World!\r\n"); + readFile(LITTLEFS, "/hello.txt"); + renameFile(LITTLEFS, "/hello.txt", "/foo.txt"); + readFile(LITTLEFS, "/foo.txt"); + deleteFile(LITTLEFS, "/foo.txt"); + testFileIO(LITTLEFS, "/test.txt"); + deleteFile(LITTLEFS, "/test.txt"); + + Serial.println( "Test complete" ); +} + +void loop(){ + +} diff --git a/libraries/LITTLEFS/examples/LITTLEFS_test/LITTLEFS_test.ino b/libraries/LITTLEFS/examples/LITTLEFS_test/LITTLEFS_test.ino new file mode 100644 index 000000000..528bfd187 --- /dev/null +++ b/libraries/LITTLEFS/examples/LITTLEFS_test/LITTLEFS_test.ino @@ -0,0 +1,272 @@ +#include +#include "FS.h" +#include + +/* You only need to format LITTLEFS the first time you run a + test or else use the LITTLEFS plugin to create a partition + https://github.com/lorol/arduino-esp32littlefs-plugin */ + +#define FORMAT_LITTLEFS_IF_FAILED true + +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.name(), levels -1); + } + } else { + Serial.print(" FILE: "); + Serial.print(file.name()); + Serial.print("\tSIZE: "); + Serial.println(file.size()); + } + file = root.openNextFile(); + } +} + +void createDir(fs::FS &fs, const char * path){ + Serial.printf("Creating Dir: %s\n", path); + if(fs.mkdir(path)){ + Serial.println("Dir created"); + } else { + Serial.println("mkdir failed"); + } +} + +void removeDir(fs::FS &fs, const char * path){ + Serial.printf("Removing Dir: %s\n", path); + if(fs.rmdir(path)){ + Serial.println("Dir removed"); + } else { + Serial.println("rmdir failed"); + } +} + +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"); + } +} + +// SPIFFS-like write and delete file + +// See: https://github.com/esp8266/Arduino/blob/master/libraries/LittleFS/src/LittleFS.cpp#L60 +void writeFile2(fs::FS &fs, const char * path, const char * message){ + if(!fs.exists(path)){ + if (strchr(path, '/')) { + Serial.printf("Create missing folders of: %s\r\n", path); + char *pathStr = strdup(path); + if (pathStr) { + char *ptr = strchr(pathStr, '/'); + while (ptr) { + *ptr = 0; + fs.mkdir(pathStr); + *ptr = '/'; + ptr = strchr(ptr+1, '/'); + } + } + free(pathStr); + } + } + + Serial.printf("Writing file to: %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(); +} + +// See: https://github.com/esp8266/Arduino/blob/master/libraries/LittleFS/src/LittleFS.h#L149 +void deleteFile2(fs::FS &fs, const char * path){ + Serial.printf("Deleting file and empty folders on path: %s\r\n", path); + + if(fs.remove(path)){ + Serial.println("- file deleted"); + } else { + Serial.println("- delete failed"); + } + + char *pathStr = strdup(path); + if (pathStr) { + char *ptr = strrchr(pathStr, '/'); + if (ptr) { + Serial.printf("Removing all empty folders on path: %s\r\n", path); + } + while (ptr) { + *ptr = 0; + fs.rmdir(pathStr); + ptr = strrchr(pathStr, '/'); + } + free(pathStr); + } +} + +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 %u 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 %u ms\r\n", flen, end); + file.close(); + } else { + Serial.println("- failed to open file for reading"); + } +} + +void setup(){ + Serial.begin(115200); + if(!LITTLEFS.begin(FORMAT_LITTLEFS_IF_FAILED)){ + Serial.println("LITTLEFS Mount Failed"); + return; + } + Serial.println( "SPIFFS-like write file to new path and delete it w/folders" ); + writeFile2(LITTLEFS, "/new1/new2/new3/hello3.txt", "Hello3"); + listDir(LITTLEFS, "/", 3); + deleteFile2(LITTLEFS, "/new1/new2/new3/hello3.txt"); + + listDir(LITTLEFS, "/", 3); + createDir(LITTLEFS, "/mydir"); + writeFile(LITTLEFS, "/mydir/hello2.txt", "Hello2"); + listDir(LITTLEFS, "/", 1); + deleteFile(LITTLEFS, "/mydir/hello2.txt"); + removeDir(LITTLEFS, "/mydir"); + listDir(LITTLEFS, "/", 1); + writeFile(LITTLEFS, "/hello.txt", "Hello "); + appendFile(LITTLEFS, "/hello.txt", "World!\r\n"); + readFile(LITTLEFS, "/hello.txt"); + renameFile(LITTLEFS, "/hello.txt", "/foo.txt"); + readFile(LITTLEFS, "/foo.txt"); + deleteFile(LITTLEFS, "/foo.txt"); + testFileIO(LITTLEFS, "/test.txt"); + deleteFile(LITTLEFS, "/test.txt"); + + Serial.println( "Test complete" ); +} + +void loop(){ + +} diff --git a/libraries/LITTLEFS/examples/LITTLEFS_time/LITTLEFS_time.ino b/libraries/LITTLEFS/examples/LITTLEFS_time/LITTLEFS_time.ino new file mode 100644 index 000000000..d4f4aaf11 --- /dev/null +++ b/libraries/LITTLEFS/examples/LITTLEFS_time/LITTLEFS_time.ino @@ -0,0 +1,214 @@ +#include "FS.h" +//#include "SPIFFS.h" +#include "LITTLEFS.h" +#include +#include + +#define SPIFFS LITTLEFS + +/* This examples uses "quick re-define" of SPIFFS to run + an existing sketch with LITTLEFS instead of SPIFFS + + You only need to format LITTLEFS the first time you run a + test or else use the LITTLEFS plugin to create a partition + https://github.com/lorol/arduino-esp32littlefs-plugin */ + +#define FORMAT_LITTLEFS_IF_FAILED true + +const char* ssid = "yourssid"; +const char* password = "yourpass"; + +long timezone = 1; +byte daysavetime = 1; + +void listDir(fs::FS &fs, const char * dirname, uint8_t levels){ + Serial.printf("Listing directory: %s\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.print (file.name()); + time_t t= file.getLastWrite(); + struct tm * tmstruct = localtime(&t); + Serial.printf(" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour , tmstruct->tm_min, tmstruct->tm_sec); + if(levels){ + listDir(fs, file.name(), levels -1); + } + } else { + Serial.print(" FILE: "); + Serial.print(file.name()); + Serial.print(" SIZE: "); + Serial.print(file.size()); + time_t t= file.getLastWrite(); + struct tm * tmstruct = localtime(&t); + Serial.printf(" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour , tmstruct->tm_min, tmstruct->tm_sec); + } + file = root.openNextFile(); + } +} + +void createDir(fs::FS &fs, const char * path){ + Serial.printf("Creating Dir: %s\n", path); + if(fs.mkdir(path)){ + Serial.println("Dir created"); + } else { + Serial.println("mkdir failed"); + } +} + +void removeDir(fs::FS &fs, const char * path){ + Serial.printf("Removing Dir: %s\n", path); + if(fs.rmdir(path)){ + Serial.println("Dir removed"); + } else { + Serial.println("rmdir failed"); + } +} + +void readFile(fs::FS &fs, const char * path){ + Serial.printf("Reading file: %s\n", path); + + File file = fs.open(path); + if(!file){ + Serial.println("Failed to open file for reading"); + return; + } + + Serial.print("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\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\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\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\n", path); + if(fs.remove(path)){ + Serial.println("File deleted"); + } else { + Serial.println("Delete failed"); + } +} + +void setup(){ + Serial.begin(115200); + // We start by connecting to a WiFi network + Serial.println(); + Serial.println(); + Serial.print("Connecting to "); + Serial.println(ssid); + + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println("Contacting Time Server"); + configTime(3600*timezone, daysavetime*3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org"); + struct tm tmstruct ; + delay(2000); + tmstruct.tm_year = 0; + getLocalTime(&tmstruct, 5000); + Serial.printf("\nNow is : %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct.tm_year)+1900,( tmstruct.tm_mon)+1, tmstruct.tm_mday,tmstruct.tm_hour , tmstruct.tm_min, tmstruct.tm_sec); + Serial.println(""); + + if(!SPIFFS.begin(FORMAT_LITTLEFS_IF_FAILED)){ + Serial.println("LITTLEFS Mount Failed"); + return; + } + + Serial.println("----list 1----"); + listDir(SPIFFS, "/", 1); + + Serial.println("----remove old dir----"); + removeDir(SPIFFS, "/mydir"); + + Serial.println("----create a new dir----"); + createDir(SPIFFS, "/mydir"); + + Serial.println("----remove the new dir----"); + removeDir(SPIFFS, "/mydir"); + + Serial.println("----create the new again----"); + createDir(SPIFFS, "/mydir"); + + Serial.println("----create and work with file----"); + writeFile(SPIFFS, "/mydir/hello.txt", "Hello "); + appendFile(SPIFFS, "/mydir/hello.txt", "World!\n"); + + Serial.println("----list 2----"); + listDir(SPIFFS, "/", 1); + + Serial.println("----attempt to remove dir w/ file----"); + removeDir(SPIFFS, "/mydir"); + + Serial.println("----remove dir after deleting file----"); + deleteFile(SPIFFS, "/mydir/hello.txt"); + removeDir(SPIFFS, "/mydir"); + + Serial.println("----list 3----"); + listDir(SPIFFS, "/", 1); + + Serial.println( "Test complete" ); + +} + +void loop(){ + +} diff --git a/libraries/LITTLEFS/library.properties b/libraries/LITTLEFS/library.properties new file mode 100644 index 000000000..1b7a7bcaa --- /dev/null +++ b/libraries/LITTLEFS/library.properties @@ -0,0 +1,9 @@ +name=LITTLEFS +version=2.0 +author= +maintainer= +sentence=LittleFS for esp32 +paragraph=LittleFS for esp32 +category=Data Storage +url= +architectures=esp32 diff --git a/libraries/LITTLEFS/src/LITTLEFS.cpp b/libraries/LITTLEFS/src/LITTLEFS.cpp new file mode 100644 index 000000000..15820dc86 --- /dev/null +++ b/libraries/LITTLEFS/src/LITTLEFS.cpp @@ -0,0 +1,107 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +static constexpr const char LFS_NAME[] = "spiffs"; + +#include "vfs_api.h" + +extern "C" { +#include +#include +#include +#include "esp_littlefs.h" +#undef B110 +#undef B1000000 +} + +#include "LITTLEFS.h" + +using namespace fs; + +LITTLEFSFS::LITTLEFSFS() : FS(FSImplPtr(new VFSImpl())) +{ + +} + +bool LITTLEFSFS::begin(bool formatOnFail, const char * basePath, uint8_t maxOpenFilesUnused) +{ + if(esp_littlefs_mounted(LFS_NAME)){ + log_w("LITTLEFS Already Mounted!"); + return true; + } + + esp_vfs_littlefs_conf_t conf = { + .base_path = basePath, + .partition_label = LFS_NAME, + .format_if_mount_failed = false + }; + + esp_err_t err = esp_vfs_littlefs_register(&conf); + if(err == ESP_FAIL && formatOnFail){ + if(format()){ + err = esp_vfs_littlefs_register(&conf); + } + } + if(err != ESP_OK){ + log_e("Mounting LITTLEFS failed! Error: %d", err); + return false; + } + _impl->mountpoint(basePath); + return true; +} + +void LITTLEFSFS::end() +{ + if(esp_littlefs_mounted(LFS_NAME)){ + esp_err_t err = esp_vfs_littlefs_unregister(LFS_NAME); + if(err){ + log_e("Unmounting LITTLEFS failed! Error: %d", err); + return; + } + _impl->mountpoint(NULL); + } +} + +bool LITTLEFSFS::format() +{ + disableCore0WDT(); + esp_err_t err = esp_littlefs_format(LFS_NAME); + enableCore0WDT(); + if(err){ + log_e("Formatting LITTLEFS failed! Error: %d", err); + return false; + } + return true; +} + +size_t LITTLEFSFS::totalBytes() +{ + size_t total,used; + if(esp_littlefs_info(LFS_NAME, &total, &used)){ + return 0; + } + return total; +} + +size_t LITTLEFSFS::usedBytes() +{ + size_t total,used; + if(esp_littlefs_info(LFS_NAME, &total, &used)){ + return 0; + } + return used; +} + +LITTLEFSFS LITTLEFS; + diff --git a/libraries/LITTLEFS/src/LITTLEFS.h b/libraries/LITTLEFS/src/LITTLEFS.h new file mode 100644 index 000000000..fbd6f09e1 --- /dev/null +++ b/libraries/LITTLEFS/src/LITTLEFS.h @@ -0,0 +1,38 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef _LITTLEFS_H_ +#define _LITTLEFS_H_ + +#include "FS.h" + +namespace fs +{ + +class LITTLEFSFS : public FS +{ +public: + LITTLEFSFS(); + bool begin(bool formatOnFail=false, const char * basePath="/littlefs", uint8_t maxOpenFiles=5); + bool format(); + size_t totalBytes(); + size_t usedBytes(); + void end(); +}; + +} + +extern fs::LITTLEFSFS LITTLEFS; + + +#endif diff --git a/libraries/Update/src/Update.h b/libraries/Update/src/Update.h index 9a46a7848..4016b99d4 100644 --- a/libraries/Update/src/Update.h +++ b/libraries/Update/src/Update.h @@ -171,6 +171,7 @@ class UpdateClass { size_t _size; THandlerFunction_Progress _progress_callback; uint32_t _progress; + uint32_t _paroffset; uint32_t _command; const esp_partition_t* _partition; diff --git a/libraries/Update/src/Updater.cpp b/libraries/Update/src/Updater.cpp index cfa28827e..4bdaec854 100644 --- a/libraries/Update/src/Updater.cpp +++ b/libraries/Update/src/Updater.cpp @@ -70,6 +70,7 @@ UpdateClass::UpdateClass() , _size(0) , _progress_callback(NULL) , _progress(0) +, _paroffset(0) , _command(U_FLASH) , _partition(NULL) { @@ -137,9 +138,14 @@ bool UpdateClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) { } else if (command == U_SPIFFS) { _partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL); + _paroffset = 0; if(!_partition){ - _error = UPDATE_ERROR_NO_PARTITION; - return false; + _partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL); + _paroffset = 0x1000; //Offset for ffat, assuming size is already corrected + if(!_partition){ + _error = UPDATE_ERROR_NO_PARTITION; + return false; + } } } else { @@ -192,11 +198,14 @@ bool UpdateClass::_writeBuffer(){ if (!_progress && _progress_callback) { _progress_callback(0, _size); } - if(!ESP.flashEraseSector((_partition->address + _progress)/SPI_FLASH_SEC_SIZE)){ + + if(_command == U_FLASH) _paroffset = 0; //ffat correction + + if(!ESP.flashEraseSector((_partition->address + _paroffset + _progress)/SPI_FLASH_SEC_SIZE)){ //ffat correction _abort(UPDATE_ERROR_ERASE); return false; } - if (!ESP.flashWrite(_partition->address + _progress, (uint32_t*)_buffer, _bufferLen)) { + if (!ESP.flashWrite(_partition->address + _paroffset + _progress, (uint32_t*)_buffer, _bufferLen)) { //ffat correction _abort(UPDATE_ERROR_WRITE); return false; } diff --git a/package/package_esp32_index.template.json b/package/package_esp32_index.template.json index 8faaa5219..b6726aea3 100644 --- a/package/package_esp32_index.template.json +++ b/package/package_esp32_index.template.json @@ -175,6 +175,81 @@ } ] }, + { + "version": "0.3.6", + "name": "mkfatfs", + "systems": [ + { + "host": "i686-pc-linux-gnu", + "url": "https://github.com/lorol/arduino-esp32fatfs-plugin/raw/master/extra/mkfatfs.tar.gz", + "archiveFileName": "mkfatfs.tar.gz", + "checksum": "SHA-256:641322f1107796d7d77eacfdca0377a942e20fc7c228c6b2aa807dda5117e0af", + "size": "112148" + }, + { + "host": "i686-mingw32", + "url": "https://github.com/lorol/arduino-esp32fatfs-plugin/raw/master/extra/mkfatfs.zip", + "archiveFileName": "mkfatfs.zip", + "checksum": "SHA-256:966f378da8bba524197d7d8a919e93b21b4680ead03ae69cac8bb56f8ec864a6", + "size": "612169" + } + ] + }, + { + "version": "3.0.0-gnu12-dc7f933", + "name": "mklittlefs", + "systems": [ + { + "host": "aarch64-linux-gnu", + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/aarch64-linux-gnu.mklittlefs-c41e51a.200706.tar.gz", + "archiveFileName": "aarch64-linux-gnu.mklittlefs-c41e51a.200706.tar.gz", + "checksum": "SHA-256:fc56e389383749e4cf4fab0fcf75cc0ebc41e59383caf6c2eff1c3d9794af200", + "size": "44651" + }, + { + "host": "arm-linux-gnueabihf", + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/arm-linux-gnueabihf.mklittlefs-c41e51a.200706.tar.gz", + "archiveFileName": "arm-linux-gnueabihf.mklittlefs-c41e51a.200706.tar.gz", + "checksum": "SHA-256:52b642dd0545eb3bd8dfb75dde6601df21700e4867763fd2696274be279294c5", + "size": "37211" + }, + { + "host": "i686-pc-linux-gnu", + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/i686-linux-gnu.mklittlefs-c41e51a.200706.tar.gz", + "archiveFileName": "i686-linux-gnu.mklittlefs-c41e51a.200706.tar.gz", + "checksum": "SHA-256:7886051d8ccc54aed0af2e7cdf6ff992bb51638df86f3b545955697720b6d062", + "size": "48033" + }, + { + "host": "i686-mingw32", + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/i686-w64-mingw32.mklittlefs-c41e51a.200706.zip", + "archiveFileName": "i686-w64-mingw32.mklittlefs-c41e51a.200706.zip", + "checksum": "SHA-256:43740db30ce451454f2337331f10ab4ed41bd83dbf0fa0cb4387107388b59f42", + "size": "332655" + }, + { + "host": "x86_64-apple-darwin", + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/x86_64-apple-darwin14.mklittlefs-c41e51a.200706.tar.gz", + "archiveFileName": "x86_64-apple-darwin14.mklittlefs-c41e51a.200706.tar.gz", + "checksum": "SHA-256:e3edd5e05b70db3c7df6b9d626558348ad04804022fe955c799aeb51808c7dc3", + "size": "362608" + }, + { + "host": "x86_64-pc-linux-gnu", + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/x86_64-linux-gnu.mklittlefs-c41e51a.200706.tar.gz", + "archiveFileName": "x86_64-linux-gnu.mklittlefs-c41e51a.200706.tar.gz", + "checksum": "SHA-256:66e84dda0aad747517da3785125e05738a540948aab2b7eaa02855167a1eea53", + "size": "46778" + }, + { + "host": "x86_64-mingw32", + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/x86_64-w64-mingw32.mklittlefs-c41e51a.200706.zip", + "archiveFileName": "x86_64-w64-mingw32.mklittlefs-c41e51a.200706.zip", + "checksum": "SHA-256:2e319077491f8e832e96eb4f2f7a70dd919333cee4b388c394e0e848d031d542", + "size": "345132" + } + ] + }, { "name": "mkspiffs", "version": "0.2.3", -- GitLab