├── .gitignore ├── README.md ├── README1.md ├── examples ├── ListFiles │ └── ListFiles.ino ├── LittleFS_Program_Simple_Datalogger-dates │ └── LittleFS_Program_Simple_Datalogger-dates.ino ├── LittleFS_Usage │ └── LittleFS_Usage.ino ├── Simple_DataLogger │ ├── LittleFS_PSRAM_Simple_Datalogger │ │ └── LittleFS_PSRAM_Simple_Datalogger.ino │ ├── LittleFS_Program_Simple_Datalogger │ │ └── LittleFS_Program_Simple_Datalogger.ino │ ├── LittleFS_QSPI_Simple_Datalogger │ │ └── LittleFS_QSPI_Simple_Datalogger.ino │ └── LittleFS_SPI_Simple_Datalogger │ │ └── LittleFS_SPI_Simple_Datalogger.ino ├── Test_Integrity │ ├── MRAMSPI │ │ ├── MRAMSPI.ino │ │ └── functions.ino │ ├── PROG │ │ ├── PROG.ino │ │ └── functions.ino │ ├── PSRAM │ │ ├── PSRAM.ino │ │ └── functions.ino │ ├── QSPI │ │ ├── QSPI.ino │ │ └── functions.ino │ ├── RAM │ │ ├── RAM.ino │ │ └── functions.ino │ └── SPI │ │ ├── SPI.ino │ │ └── functions.ino └── Write_Speed_Test │ └── Write_Speed_Test.ino ├── keywords.txt ├── library.properties └── src ├── LittleFS.cpp ├── LittleFS.h ├── LittleFS_NAND.cpp └── littlefs ├── DESIGN.md ├── LICENSE.md ├── README.md ├── SPEC.md ├── lfs.c ├── lfs.h ├── lfs_util.c └── lfs_util.h /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.cmd 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LittleFS 2 | 3 | This is a wrapper for the LittleFS File System for the Teensy family of microprocessors and provides support for RAM Disks, NOR and NAND Flash chips, and FRAM chips. For the NOR, NAND Flash support is provided for SPI and QSPI in the case of the Teensy 4.1. For FRAM only SPI is supported. 4 | 5 | In addition, LittleFS is linked with the SD library so the user can create, delete, read/write files to any of the supported memory type. See usuage section. 6 | 7 | ## Supported Chips 8 | 9 | ### NOR Flash 10 | 11 | #### 12 | MFG | PART # | Size 13 | ------------ | ------------- |------------ 14 | Winbond | W25Q16JV*IQ/W25Q16FV | 16Mb 15 | ... | W25Q32JV*IQ/W25Q32FV | 32Mb 16 | ... | W25Q64JV*IQ/W25Q64FV | 64Mb 17 | ... | W25Q128JV*IQ/W25Q128FV | 128Mb 18 | ... | W25Q256JV*IQ | 256Mb 19 | ... | Winbond W25Q512JV*IQ | 512Mb 20 | ... | W25Q64JV*IM (DTR) | 64Mb 21 | ... | W25Q128JV*IM (DTR) | 128Mb 22 | ... | W25Q256JV*IM (DTR) | 256Mb 23 | ... | W25Q512JV*IM (DTR) | 512Mb 24 | Adesto/Atmel | AT25SF041 | 4Mb 25 | Spansion | S25FL208K | 8Mb 26 | 27 | ### NAND Flash 28 | 29 | #### 30 | MFG | PART # | Size 31 | ------------ | ------------- |------------ 32 | Winbond | W25N01G | 1Gb 33 | ... | W25N02G | 2Gb 34 | ... | W25M02 | 2Gb 35 | 36 | 37 | ### FRAM 38 | 39 | #### 40 | MFG | PART # | Size 41 | ------------ | ------------- |------------ 42 | Cypress | CY15B108QN-40SXI | 8Mb 43 | ... | FM25V10-G | 1Mb 44 | ... | FM25V10-G rev1 | 1Mb 45 | ... | CY15B104Q-SXI | 4Mb 46 | ... | CY15B102Q-SXI | 2Mb 47 | ROHM | MR45V100A | 1Mb 48 | Fujitsu | MB85RS2MTAPNF | 2Mb 49 | Fujitsu | MB85RS4MT | 4Mb 50 | 51 | ## USAGE 52 | 53 | ### Program Memory 54 | 55 | NOTE: This option is only available on the Teensy 4.0, Teensy 4.1 and Teensy Micromod boards. 56 | 57 | Creates the filesystem using program memory. The begin function expects a size in bytes, for the amount of the program memory you wish to use. It must be at least 65536 and smaller than the actual unused program space. All interrupts are disabled during writing and erasing, so use of this storage does come with pretty substantial impact on interrupt latency for other libraries. Uploading new code by Teensy Loader completely wipes the unused program space, erasing all stored files. But the filesystem persists across reboots and power cycling. 58 | 59 | To create a disk in program memory the following constructor is used: 60 | ``` 61 | LittleFS_Program myfs; 62 | ``` 63 | 64 | In setup space is allocated int program memory by specifing space in the begin statement: 65 | ```cpp 66 | if (!myfs.begin(1024 * 1024 * 6)) { 67 | Serial.printf("Error starting %s\n", "Program flash DISK"); 68 | } 69 | ``` 70 | 71 | ### RAM Disk 72 | 73 | A RAM disk can be setup to use space on the PSRAM chip in the case of the Teensy 4.1 or to use DMAMEM or RAM space on the Teensy itself. If using DMAMEM or RAM the amount of available space to create these disks to going to be dependent on the Teensy model you are using so be careful. Not to worry though it will let you know if you are out of space on compile. 74 | 75 | To create a RAM disk you need to first allocate the space based on the type of memory area, for the Teensy 4.1 for example you can use these settings: 76 | 1. ```EXTMEM char buf[8 * 1024 * 1024]; ``` This allocates all 8Mb of the PSRAM for the RAM disk. 77 | 2. ```DMAMEM char buf[490000]; ``` This allocates 490K of the DMAMEM 78 | 3. ```char buf[390000]; ``` This allocates 390K of RAM 79 | 80 | Afer allocating space you specify that you want to create a RAM Disk: 81 | 82 | ```cpp 83 | LittleFS_RAM myfs; 84 | ``` 85 | 86 | In setup you have to specify to begin using the memory area: 87 | ```cpp 88 | if (!myfs.begin(buf, sizeof(buf))) { 89 | Serial.printf("Error starting %s\n", "RAM DISK); 90 | } 91 | ``` 92 | 93 | At this point you can access or create files in the same manner as you would with an SD Card using the SD Library bundled with Teensyduino. See the examples section for creating and writing a file. 94 | 95 | ### QSPI 96 | 97 | QSPI is only supported on the Teensy 4.1. These are the Flash chips that you would solder onto the bottom side of the Teesny 4.1. To access Flash NOR or NAND chips QSPI is similar to the RAM disk: 98 | 1. For a NAND Flash chip use the following construtor: ```LittleFS_QPINAND myfs;``` 99 | 2. For a NOR flash ```LittleFS_QSPIFlash myfs; ``` 100 | 101 | And then in setup all you need for the NAND or NOR QSPI is: 102 | ```cpp 103 | if (!myfs.begin() { 104 | Serial.printf("Error starting %s\n", "QSPI"); 105 | } 106 | ``` 107 | 108 | ### SPI 109 | For SPI, three constructors are availble - NOR Flash, NAND Flash and FRAM 110 | 1. ```LittleFS_SPIFlash myfs;``` 111 | 2. ```LittleFS_SPINAND myfs;``` 112 | 3. ```LittleFS_SPIFram myfs;``` 113 | 114 | For SPI the ```begin``` statement requires the user to specify the Chip Select pin and optionally which SPI port to use: 115 | 116 | ```myfs.begin(CSpin, SPIport);``` 117 | 118 | By default the SPI port is SPI, use SPI1, SPI2 etc for other ports. 119 | 120 | ## Examples 121 | 122 | Several examples are provided. A simple example is as follows for a datalogger for a SPI NAND 123 | 124 | ```cpp 125 | /* 126 | LittleFS datalogger 127 | 128 | This example shows how to log data from three analog sensors 129 | to an storage device such as a FLASH. 130 | 131 | This example code is in the public domain. 132 | */ 133 | 134 | #include 135 | 136 | LittleFS_SPINAND myfs; //Specifies to use an SPI NOR Flash attached to SPI 137 | 138 | const int chipSelect = 4; 139 | 140 | void setup() 141 | { 142 | 143 | // Open serial communications and wait for port to open: 144 | Serial.begin(9600); 145 | while (!Serial) { 146 | // wait for serial port to connect. 147 | } 148 | 149 | 150 | Serial.print("Initializing SPI FLASH..."); 151 | 152 | // see if the Flash is present and can be initialized: 153 | if (!myfs.begin(chipSelect, SPI)) { 154 | Serial.printf("Error starting %s\n", "SPI FLASH"); 155 | while (1) { 156 | // Flash error, so don't do anything more - stay stuck here 157 | } 158 | } 159 | Serial.println("Flash initialized."); 160 | } 161 | 162 | void loop() 163 | { 164 | // make a string for assembling the data to log: 165 | String dataString = ""; 166 | 167 | // read three sensors and append to the string: 168 | for (int analogPin = 0; analogPin < 3; analogPin++) { 169 | int sensor = analogRead(analogPin); 170 | dataString += String(sensor); 171 | if (analogPin < 2) { 172 | dataString += ","; 173 | } 174 | } 175 | 176 | // open the file. 177 | File dataFile = myfs.open("datalog.txt", FILE_WRITE); 178 | 179 | // if the file is available, write to it: 180 | if (dataFile) { 181 | dataFile.println(dataString); 182 | dataFile.close(); 183 | // print to the serial port too: 184 | Serial.println(dataString); 185 | } else { 186 | // if the file isn't open, pop up an error: 187 | Serial.println("error opening datalog.txt"); 188 | } 189 | delay(100); // run at a reasonable not-too-fast speed 190 | } 191 | ``` 192 | This is an example taked from the SD library. Basically instead of specifying the SD library you specify to use the littleFS library on the first line as shown in the example. Then using our methods for specifing which memory to use all we did was substitute "myfs" for where "SD" was specified before. 193 | 194 | ## [Aditional Methods](https://github.com/mjs513/LittleFS/blob/main/README1.md) 195 | -------------------------------------------------------------------------------- /README1.md: -------------------------------------------------------------------------------- 1 | ### Additional Methods 2 | 3 | ### Formatting 4 | 5 | ```myfs.formatUnused()``` formatUnused() can be used ahead of logging on slower media to assure best write times. It only formats unused space on the media. Can come in handy on large NAND chips. 6 | 7 | ```myfs.quickFormat()``` performs a quick format of the media specified 8 | 9 | ```myfs.lowLevelFormat(char, Serial Port)``` performs a low level format. Uses the specified character, e.g, "." to show progress and is sent to the specified Serial port. 10 | 11 | ### File Operations 12 | 13 | ```file.peek()``` Return the next available byte without consuming it. (SDFat class reference) 14 | 15 | ```file.available()``` The number of bytes available from the current position to EOF 16 | 17 | ```file.flush()``` Ensures that any bytes written to the file are physically saved 18 | 19 | ```file.truncate(bytes)``` Truncate a file to a specified length 20 | 21 | ```file.seek(position, mode)``` Sets the position in a file based on mode. There are 3 modes available: 22 | 23 | 1. SeekSet - Sets a file's position. 24 | 2. SeekCur - Set the files position to current position + pos 25 | 3. SeekEnd - Set the files position to end-of-file + pos 26 | 27 | ```file.position()``` The current position in a file 28 | 29 | ```file.size()``` The total number of bytes in a file 30 | 31 | ### Directory Operations 32 | 33 | ```myfs.mkdir(name)``` Make a subdirectory in the volume working directory with specified name, e.g., "structureData1", must use quotes. 34 | ```myfs.remove(name)``` Remove a file from the volume working directory, e.g, "structureData1/temp_test.txt", which removes temp_test.txt in sub-directory structuredData1. 35 | ```myfs.rename(from name, to name)``` Rename a file or subdirectory. Example: ```myfs.rename("file10", "file10.txt");``` 36 | ```myfs.rmdir(name)``` Remove a subdirectory from the working directory, example ```myfs.rmdir("test3")``` 37 | 38 | 39 | -------------------------------------------------------------------------------- /examples/ListFiles/ListFiles.ino: -------------------------------------------------------------------------------- 1 | // Print a list of all files stored on a flash memory chip 2 | 3 | #include 4 | 5 | 6 | // Flash chip on Teensy Audio Shield or Prop Shield 7 | LittleFS_SPIFlash myfs; 8 | const int chipSelect = 6; 9 | 10 | 11 | void setup() { 12 | //Uncomment these lines for Teensy 3.x Audio Shield (Rev C) 13 | //SPI.setMOSI(7); // Audio shield has MOSI on pin 7 14 | //SPI.setSCK(14); // Audio shield has SCK on pin 14 15 | 16 | // Open serial communications and wait for port to open: 17 | Serial.begin(9600); 18 | while (!Serial) { ; // wait for Arduino Serial Monitor 19 | } 20 | 21 | Serial.println("Initializing Flash Chip"); 22 | 23 | if (!myfs.begin(chipSelect)) { 24 | Serial.println("initialization failed!"); 25 | return; 26 | } 27 | 28 | Serial.print("Space Used = "); 29 | Serial.println(myfs.usedSize()); 30 | Serial.print("Filesystem Size = "); 31 | Serial.println(myfs.totalSize()); 32 | 33 | printDirectory(myfs); 34 | } 35 | 36 | 37 | void loop() { 38 | } 39 | 40 | 41 | void printDirectory(FS &fs) { 42 | Serial.println("Directory\n---------"); 43 | printDirectory(fs.open("/"), 0); 44 | Serial.println(); 45 | } 46 | 47 | void printDirectory(File dir, int numSpaces) { 48 | while(true) { 49 | File entry = dir.openNextFile(); 50 | if (! entry) { 51 | //Serial.println("** no more files **"); 52 | break; 53 | } 54 | printSpaces(numSpaces); 55 | Serial.print(entry.name()); 56 | if (entry.isDirectory()) { 57 | Serial.println("/"); 58 | printDirectory(entry, numSpaces+2); 59 | } else { 60 | // files have sizes, directories do not 61 | printSpaces(36 - numSpaces - strlen(entry.name())); 62 | Serial.print(" "); 63 | Serial.print(entry.size(), DEC); 64 | DateTimeFields datetime; 65 | if (entry.getModifyTime(datetime)) { 66 | printSpaces(4); 67 | printTime(datetime); 68 | } 69 | Serial.println(); 70 | } 71 | entry.close(); 72 | } 73 | } 74 | 75 | void printSpaces(int num) { 76 | for (int i=0; i < num; i++) { 77 | Serial.print(" "); 78 | } 79 | } 80 | 81 | void printTime(const DateTimeFields tm) { 82 | const char *months[12] = { 83 | "January","February","March","April","May","June", 84 | "July","August","September","October","November","December" 85 | }; 86 | if (tm.hour < 10) Serial.print('0'); 87 | Serial.print(tm.hour); 88 | Serial.print(':'); 89 | if (tm.min < 10) Serial.print('0'); 90 | Serial.print(tm.min); 91 | Serial.print(" "); 92 | Serial.print(tm.mon < 12 ? months[tm.mon] : "???"); 93 | Serial.print(" "); 94 | Serial.print(tm.mday); 95 | Serial.print(", "); 96 | Serial.print(tm.year + 1900); 97 | } 98 | -------------------------------------------------------------------------------- /examples/LittleFS_Program_Simple_Datalogger-dates/LittleFS_Program_Simple_Datalogger-dates.ino: -------------------------------------------------------------------------------- 1 | /* 2 | LittleFS datalogger 3 | 4 | This example shows how to log data from three analog sensors 5 | to an storage device such as a FLASH. 6 | 7 | This example code is in the public domain. 8 | */ 9 | #include 10 | 11 | LittleFS_Program myfs; 12 | 13 | // NOTE: This option is only available on the Teensy 4.0, Teensy 4.1 and Teensy Micromod boards. 14 | // With the additonal option for security on the T4 the maximum flash available for a 15 | // program disk with LittleFS is 960 blocks of 1024 bytes 16 | #define PROG_FLASH_SIZE 1024 * 1024 * 1 // Specify size to use of onboard Teensy Program Flash chip 17 | // This creates a LittleFS drive in Teensy PCB FLash. 18 | 19 | File dataFile; // Specifes that dataFile is of File type 20 | 21 | int record_count = 0; 22 | bool write_data = false; 23 | uint32_t diskSize; 24 | 25 | void setup() 26 | { 27 | 28 | // Open serial communications and wait for port to open: 29 | Serial.begin(115200); 30 | while (!Serial) { 31 | // wait for serial port to connect. 32 | } 33 | Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__); 34 | 35 | Serial.println("Initializing LittleFS ..."); 36 | 37 | // see if the Flash is present and can be initialized: 38 | // lets check to see if the T4 is setup for security first 39 | #if ARDUINO_TEENSY40 40 | if ((IOMUXC_GPR_GPR11 & 0x100) == 0x100) { 41 | //if security is active max disk size is 960x1024 42 | if (PROG_FLASH_SIZE > 960 * 1024) { 43 | diskSize = 960 * 1024; 44 | Serial.printf("Security Enables defaulted to %u bytes\n", diskSize); 45 | } else { 46 | diskSize = PROG_FLASH_SIZE; 47 | Serial.printf("Security Not Enabled using %u bytes\n", diskSize); 48 | } 49 | } 50 | #else 51 | diskSize = PROG_FLASH_SIZE; 52 | #endif 53 | 54 | // checks that the LittFS program has started with the disk size specified 55 | if (!myfs.begin(diskSize)) { 56 | Serial.printf("Error starting %s\n", "PROGRAM FLASH DISK"); 57 | while (1) { 58 | // Error, so don't do anything more - stay stuck here 59 | } 60 | } 61 | Serial.println("LittleFS initialized."); 62 | 63 | menu(); 64 | 65 | } 66 | 67 | void loop() 68 | { 69 | if ( Serial.available() ) { 70 | char rr; 71 | rr = Serial.read(); 72 | switch (rr) { 73 | case 'l': listFiles(); break; 74 | case 'e': eraseFiles(); break; 75 | case 's': 76 | { 77 | Serial.println("\nLogging Data!!!"); 78 | write_data = true; // sets flag to continue to write data until new command is received 79 | // opens a file or creates a file if not present, FILE_WRITE will append data to 80 | // to the file created. 81 | dataFile = myfs.open("datalog.txt", FILE_WRITE); 82 | logData(); 83 | } 84 | break; 85 | case 'x': stopLogging(); break; 86 | case 'd': dumpLog(); break; 87 | case '\r': 88 | case '\n': 89 | case 'h': menu(); break; 90 | } 91 | while (Serial.read() != -1) ; // remove rest of characters. 92 | } 93 | 94 | if (write_data) logData(); 95 | } 96 | 97 | void logData() 98 | { 99 | // make a string for assembling the data to log: 100 | String dataString = ""; 101 | 102 | // read three sensors and append to the string: 103 | for (int analogPin = 0; analogPin < 3; analogPin++) { 104 | int sensor = analogRead(analogPin); 105 | dataString += String(sensor); 106 | if (analogPin < 2) { 107 | dataString += ","; 108 | } 109 | } 110 | 111 | // if the file is available, write to it: 112 | if (dataFile) { 113 | dataFile.println(dataString); 114 | // print to the serial port too: 115 | Serial.println(dataString); 116 | record_count += 1; 117 | } else { 118 | // if the file isn't open, pop up an error: 119 | Serial.println("error opening datalog.txt"); 120 | } 121 | delay(100); // run at a reasonable not-too-fast speed for testing 122 | } 123 | 124 | void stopLogging() 125 | { 126 | Serial.println("\nStopped Logging Data!!!"); 127 | write_data = false; 128 | // Closes the data file. 129 | dataFile.close(); 130 | Serial.printf("Records written = %d\n", record_count); 131 | } 132 | 133 | 134 | void dumpLog() 135 | { 136 | Serial.println("\nDumping Log!!!"); 137 | // open the file. 138 | dataFile = myfs.open("datalog.txt"); 139 | 140 | // if the file is available, write to it: 141 | if (dataFile) { 142 | while (dataFile.available()) { 143 | Serial.write(dataFile.read()); 144 | } 145 | dataFile.close(); 146 | } 147 | // if the file isn't open, pop up an error: 148 | else { 149 | Serial.println("error opening datalog.txt"); 150 | } 151 | } 152 | 153 | void menu() 154 | { 155 | Serial.println(); 156 | Serial.println("Menu Options:"); 157 | Serial.println("\tl - List files on disk"); 158 | Serial.println("\te - Erase files on disk"); 159 | Serial.println("\ts - Start Logging data (Restarting logger will append records to existing log)"); 160 | Serial.println("\tx - Stop Logging data"); 161 | Serial.println("\td - Dump Log"); 162 | Serial.println("\th - Menu"); 163 | Serial.println(); 164 | } 165 | 166 | void listFiles() 167 | { 168 | Serial.print("\n Space Used = "); 169 | Serial.println(myfs.usedSize()); 170 | Serial.print("Filesystem Size = "); 171 | Serial.println(myfs.totalSize()); 172 | 173 | printDirectory(myfs); 174 | } 175 | 176 | void eraseFiles() 177 | { 178 | myfs.lowLevelFormat(); // performs a quick format of the created di 179 | Serial.println("\nFiles erased !"); 180 | } 181 | 182 | void printDirectory(FS &fs) { 183 | Serial.println("Directory\n---------"); 184 | printDirectory(fs.open("/"), 0); 185 | Serial.println(); 186 | } 187 | 188 | void printDirectory(File dir, int numSpaces) { 189 | while(true) { 190 | File entry = dir.openNextFile(); 191 | if (! entry) { 192 | //Serial.println("** no more files **"); 193 | break; 194 | } 195 | printSpaces(numSpaces); 196 | Serial.print(entry.name()); 197 | if (entry.isDirectory()) { 198 | Serial.println("/"); 199 | printDirectory(entry, numSpaces+2); 200 | } else { 201 | // files have sizes, directories do not 202 | printSpaces(36 - numSpaces - strlen(entry.name())); 203 | Serial.print(" "); 204 | Serial.print(entry.size(), DEC); 205 | DateTimeFields datetime; 206 | if (entry.getModifyTime(datetime)) { 207 | printSpaces(4); 208 | printTime(datetime); 209 | } 210 | Serial.println(); 211 | } 212 | entry.close(); 213 | } 214 | } 215 | 216 | 217 | void printSpaces(int num) { 218 | for (int i = 0; i < num; i++) { 219 | Serial.print(" "); 220 | } 221 | } 222 | 223 | void printTime(const DateTimeFields tm) { 224 | const char *months[12] = { 225 | "January","February","March","April","May","June", 226 | "July","August","September","October","November","December" 227 | }; 228 | if (tm.hour < 10) Serial.print('0'); 229 | Serial.print(tm.hour); 230 | Serial.print(':'); 231 | if (tm.min < 10) Serial.print('0'); 232 | Serial.print(tm.min); 233 | Serial.print(" "); 234 | Serial.print(tm.mon < 12 ? months[tm.mon] : "???"); 235 | Serial.print(" "); 236 | Serial.print(tm.mday); 237 | Serial.print(", "); 238 | Serial.print(tm.year + 1900); 239 | } 240 | -------------------------------------------------------------------------------- /examples/LittleFS_Usage/LittleFS_Usage.ino: -------------------------------------------------------------------------------- 1 | /* 2 | LittleFS usage from the LittleFS library 3 | 4 | Starting with Teensyduino 1.54, support for LittleFS has been added. 5 | 6 | LittleFS is a wrapper for the LittleFS File System for the Teensy family of microprocessors and provides support for RAM Disks, NOR and NAND Flash chips, and FRAM chips. For the NOR, NAND Flash support is provided for SPI and QSPI in the case of the Teensy 4.1. For FRAM only SPI is supported. It is also linked to SDFat so many of the same commands can be used as for an SD Card. 7 | 8 | This example shows the use of some of the commands provided in LittleFS using a SPI Flash chip such as the W25Q128. 9 | 10 | See the readme for the LittleFS library for more information: https://github.com/PaulStoffregen/LittleFS 11 | 12 | */ 13 | 14 | #include 15 | 16 | 17 | // Some variables for later use 18 | uint64_t fTot, totSize1; 19 | 20 | // To use SPI flash we need to create a instance of the library telling it to use SPI flash. 21 | /* Other options include: 22 | LittleFS_QSPIFlash myfs; 23 | LittleFS_Program myfs; 24 | LittleFS_SPIFlash myfs; 25 | LittleFS_SPIFram myfs; 26 | LittleFS_SPINAND myfs; 27 | LittleFS_QPINAND myfs; 28 | LittleFS_RAM myfs; 29 | */ 30 | LittleFS_SPIFlash myfs; 31 | 32 | // Since we are using SPI we need to tell the library what the chip select pin 33 | #define chipSelect 6 // use for access flash on audio or prop shield 34 | 35 | // Specifies that the file, file1 and file3 are File types, same as you would do for creating files 36 | // on a SD Card 37 | File file, file1, file2; 38 | 39 | 40 | void setup() 41 | { 42 | // Open serial communications and wait for port to open: 43 | Serial.begin(115200); 44 | while (!Serial) { 45 | // wait for serial port to connect. 46 | } 47 | Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__); 48 | 49 | Serial.print("Initializing LittleFS ..."); 50 | 51 | // see if the Flash is present and can be initialized: 52 | // Note: SPI is default so if you are using SPI and not SPI for instance 53 | // you can just specify myfs.begin(chipSelect). 54 | if (!myfs.begin(chipSelect, SPI)) { 55 | Serial.printf("Error starting %s\n", "SPI FLASH"); 56 | while (1) { 57 | // Error, so don't do anything more - stay stuck here 58 | } 59 | } 60 | myfs.quickFormat(); 61 | Serial.println("LittleFS initialized."); 62 | 63 | 64 | // To get the current space used and Filesystem size 65 | Serial.println("\n---------------"); 66 | Serial.printf("Bytes Used: %llu, Bytes Total:%llu\n", myfs.usedSize(), myfs.totalSize()); 67 | waitforInput(); 68 | 69 | // Now lets create a file and write some data. Note: basically the same usage for 70 | // creating and writing to a file using SD library. 71 | Serial.println("\n---------------"); 72 | Serial.println("Now lets create a file with some data in it"); 73 | Serial.println("---------------"); 74 | char someData[128]; 75 | memset( someData, 'z', 128 ); 76 | file = myfs.open("bigfile.txt", FILE_WRITE); 77 | file.write(someData, sizeof(someData)); 78 | 79 | for (uint16_t j = 0; j < 100; j++) 80 | file.write(someData, sizeof(someData)); 81 | file.close(); 82 | 83 | // We can also get the size of the file just created. Note we have to open and 84 | // thes close the file unless we do file size before we close it in the previous step 85 | file = myfs.open("bigfile.txt", FILE_WRITE); 86 | Serial.printf("File Size of bigfile.txt (bytes): %u\n", file.size()); 87 | file.close(); 88 | 89 | // Now that we initialized the FS and created a file lets print the directory. 90 | // Note: Since we are going to be doing print directory and getting disk usuage 91 | // lets make it a function which can be copied and used in your own sketches. 92 | listFiles(); 93 | waitforInput(); 94 | 95 | // Now lets rename the file 96 | Serial.println("\n---------------"); 97 | Serial.println("Rename bigfile to file10"); 98 | myfs.rename("bigfile.txt", "file10.txt"); 99 | listFiles(); 100 | waitforInput(); 101 | 102 | // To delete the file 103 | Serial.println("\n---------------"); 104 | Serial.println("Delete file10.txt"); 105 | myfs.remove("file10.txt"); 106 | listFiles(); 107 | waitforInput(); 108 | 109 | Serial.println("\n---------------"); 110 | Serial.println("Create a directory and a subfile"); 111 | myfs.mkdir("structureData1"); 112 | 113 | file = myfs.open("structureData1/temp_test.txt", FILE_WRITE); 114 | file.println("SOME DATA TO TEST"); 115 | file.close(); 116 | listFiles(); 117 | waitforInput(); 118 | 119 | Serial.println("\n---------------"); 120 | Serial.println("Rename directory"); 121 | myfs.rename("structureData1", "structuredData"); 122 | listFiles(); 123 | waitforInput(); 124 | 125 | Serial.println("\n---------------"); 126 | Serial.println("Lets remove them now..."); 127 | //Note have to remove directories files first 128 | myfs.remove("structuredData/temp_test.txt"); 129 | myfs.rmdir("structuredData"); 130 | listFiles(); 131 | waitforInput(); 132 | 133 | Serial.println("\n---------------"); 134 | Serial.println("Now lets create a file and read the data back..."); 135 | 136 | // LittleFS also supports truncate function similar to SDFat. As shown in this 137 | // example, you can truncate files. 138 | // 139 | Serial.println(); 140 | Serial.println("Writing to datalog.bin using LittleFS functions"); 141 | file1 = myfs.open("datalog.bin", FILE_WRITE); 142 | unsigned int len = file1.size(); 143 | Serial.print("datalog.bin started with "); 144 | Serial.print(len); 145 | Serial.println(" bytes"); 146 | if (len > 0) { 147 | // reduce the file to zero if it already had data 148 | file1.truncate(); 149 | } 150 | file1.print("Just some test data written to the file (by SdFat functions)"); 151 | file1.write((uint8_t) 0); 152 | file1.close(); 153 | 154 | // You can also use regular SD type functions, even to access the same file. Just 155 | // remember to close the file before opening as a regular SD File. 156 | // 157 | Serial.println(); 158 | Serial.println("Reading to datalog.bin using LittleFS functions"); 159 | file2 = myfs.open("datalog.bin"); 160 | if (file2) { 161 | char mybuffer[100]; 162 | int index = 0; 163 | while (file2.available()) { 164 | char c = file2.read(); 165 | mybuffer[index] = c; 166 | if (c == 0) break; // end of string 167 | index = index + 1; 168 | if (index == 99) break; // buffer full 169 | } 170 | mybuffer[index] = 0; 171 | Serial.print(" Read from file: "); 172 | Serial.println(mybuffer); 173 | } else { 174 | Serial.println("unable to open datalog.bin :("); 175 | } 176 | file2.close(); 177 | 178 | 179 | Serial.println("\nBasic Usage Example Finished"); 180 | } 181 | 182 | void loop() {} 183 | 184 | void listFiles() 185 | { 186 | Serial.println("---------------"); 187 | printDirectory(myfs); 188 | Serial.printf("Bytes Used: %llu, Bytes Total:%llu\n", myfs.usedSize(), myfs.totalSize()); 189 | } 190 | 191 | void printDirectory(FS &fs) { 192 | Serial.println("Directory\n---------"); 193 | printDirectory(fs.open("/"), 0); 194 | Serial.println(); 195 | } 196 | 197 | void printDirectory(File dir, int numSpaces) { 198 | while(true) { 199 | File entry = dir.openNextFile(); 200 | if (! entry) { 201 | //Serial.println("** no more files **"); 202 | break; 203 | } 204 | printSpaces(numSpaces); 205 | Serial.print(entry.name()); 206 | if (entry.isDirectory()) { 207 | Serial.println("/"); 208 | printDirectory(entry, numSpaces+2); 209 | } else { 210 | // files have sizes, directories do not 211 | printSpaces(36 - numSpaces - strlen(entry.name())); 212 | Serial.print(" "); 213 | Serial.println(entry.size(), DEC); 214 | } 215 | entry.close(); 216 | } 217 | } 218 | 219 | void printSpaces(int num) { 220 | for (int i=0; i < num; i++) { 221 | Serial.print(" "); 222 | } 223 | } 224 | 225 | void waitforInput() 226 | { 227 | Serial.println("Press anykey to continue"); 228 | while (Serial.read() == -1) ; 229 | while (Serial.read() != -1) ; 230 | } 231 | -------------------------------------------------------------------------------- /examples/Simple_DataLogger/LittleFS_PSRAM_Simple_Datalogger/LittleFS_PSRAM_Simple_Datalogger.ino: -------------------------------------------------------------------------------- 1 | /* 2 | LittleFS datalogger 3 | 4 | This example shows how to log data from three analog sensors 5 | to an storage device such as a FLASH. 6 | 7 | This example code is in the public domain. 8 | */ 9 | #include 10 | 11 | // NOTE: This option is only available on the Teensy 4.1 board with added bottomside PSRAM chip(s) in place. 12 | 13 | // This declares the LittleFS Media type and gives a text name to Identify in use 14 | LittleFS_RAM myfs; 15 | 16 | #define MYPSRAM 7 // compile time PSRAM size and is T_4.1 specific either 8 or 16, or smaller portion 17 | EXTMEM char buf[MYPSRAM * 1024 * 1024]; // Contents preserved with Power on Restart and Upload 18 | File dataFile; // Specifes that dataFile is of File type 19 | 20 | int record_count = 0; 21 | bool write_data = false; 22 | 23 | void setup() 24 | { 25 | 26 | // Open serial communications and wait for port to open: 27 | Serial.begin(115200); 28 | while (!Serial) { 29 | // wait for serial port to connect. 30 | } 31 | Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__); 32 | 33 | Serial.print("Initializing LittleFS ..."); 34 | 35 | // see if you are able to create a RAM disk in the space you a lotted 36 | // buf = is the name of the array you created, sizedf(buf) is how large the 37 | // array is, in our case 390 * 1024. 38 | if (!myfs.begin(buf, sizeof(buf))) { 39 | Serial.printf("Error starting %s\n", "PSRAM RAM DISK"); 40 | while (1) { 41 | // Error, so don't do anything more - stay stuck here 42 | } 43 | } 44 | Serial.println("LittleFS initialized."); 45 | 46 | menu(); 47 | 48 | } 49 | 50 | void loop() 51 | { 52 | if ( Serial.available() ) { 53 | char rr; 54 | rr = Serial.read(); 55 | switch (rr) { 56 | case 'l': listFiles(); break; 57 | case 'e': eraseFiles(); break; 58 | case 's': 59 | { 60 | Serial.println("\nLogging Data!!!"); 61 | write_data = true; // sets flag to continue to write data until new command is received 62 | // opens a file or creates a file if not present, FILE_WRITE will append data to 63 | // to the file created. 64 | dataFile = myfs.open("datalog.txt", FILE_WRITE); 65 | logData(); 66 | } 67 | break; 68 | case 'x': stopLogging(); break; 69 | 70 | case 'd': dumpLog(); break; 71 | case '\r': 72 | case '\n': 73 | case 'h': menu(); break; 74 | } 75 | while (Serial.read() != -1) ; // remove rest of characters. 76 | } 77 | 78 | if(write_data) logData(); 79 | } 80 | 81 | void logData() 82 | { 83 | // make a string for assembling the data to log: 84 | String dataString = ""; 85 | 86 | // read three sensors and append to the string: 87 | for (int analogPin = 0; analogPin < 3; analogPin++) { 88 | int sensor = analogRead(analogPin); 89 | dataString += String(sensor); 90 | if (analogPin < 2) { 91 | dataString += ","; 92 | } 93 | } 94 | 95 | // if the file is available, write to it: 96 | if (dataFile) { 97 | dataFile.println(dataString); 98 | // print to the serial port too: 99 | Serial.println(dataString); 100 | record_count += 1; 101 | } else { 102 | // if the file isn't open, pop up an error: 103 | Serial.println("error opening datalog.txt"); 104 | } 105 | delay(100); // run at a reasonable not-too-fast speed for testing 106 | } 107 | 108 | void stopLogging() 109 | { 110 | Serial.println("\nStopped Logging Data!!!"); 111 | write_data = false; 112 | // Closes the data file. 113 | dataFile.close(); 114 | Serial.printf("Records written = %d\n", record_count); 115 | } 116 | 117 | 118 | void dumpLog() 119 | { 120 | Serial.println("\nDumping Log!!!"); 121 | // open the file. 122 | dataFile = myfs.open("datalog.txt"); 123 | 124 | // if the file is available, write to it: 125 | if (dataFile) { 126 | while (dataFile.available()) { 127 | Serial.write(dataFile.read()); 128 | } 129 | dataFile.close(); 130 | } 131 | // if the file isn't open, pop up an error: 132 | else { 133 | Serial.println("error opening datalog.txt"); 134 | } 135 | } 136 | 137 | void menu() 138 | { 139 | Serial.println(); 140 | Serial.println("Menu Options:"); 141 | Serial.println("\tl - List files on disk"); 142 | Serial.println("\te - Erase files on disk"); 143 | Serial.println("\ts - Start Logging data (Restarting logger will append records to existing log)"); 144 | Serial.println("\tx - Stop Logging data"); 145 | Serial.println("\td - Dump Log"); 146 | Serial.println("\th - Menu"); 147 | Serial.println(); 148 | } 149 | 150 | void listFiles() 151 | { 152 | Serial.print("\n Space Used = "); 153 | Serial.println(myfs.usedSize()); 154 | Serial.print("Filesystem Size = "); 155 | Serial.println(myfs.totalSize()); 156 | 157 | printDirectory(myfs); 158 | } 159 | 160 | void eraseFiles() 161 | { 162 | myfs.quickFormat(); // performs a quick format of the created di 163 | Serial.println("\nFiles erased !"); 164 | } 165 | 166 | void printDirectory(FS &fs) { 167 | Serial.println("Directory\n---------"); 168 | printDirectory(fs.open("/"), 0); 169 | Serial.println(); 170 | } 171 | 172 | void printDirectory(File dir, int numSpaces) { 173 | while(true) { 174 | File entry = dir.openNextFile(); 175 | if (! entry) { 176 | //Serial.println("** no more files **"); 177 | break; 178 | } 179 | printSpaces(numSpaces); 180 | Serial.print(entry.name()); 181 | if (entry.isDirectory()) { 182 | Serial.println("/"); 183 | printDirectory(entry, numSpaces+2); 184 | } else { 185 | // files have sizes, directories do not 186 | printSpaces(36 - numSpaces - strlen(entry.name())); 187 | Serial.print(" "); 188 | Serial.println(entry.size(), DEC); 189 | } 190 | entry.close(); 191 | } 192 | } 193 | 194 | void printSpaces(int num) { 195 | for (int i=0; i < num; i++) { 196 | Serial.print(" "); 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /examples/Simple_DataLogger/LittleFS_Program_Simple_Datalogger/LittleFS_Program_Simple_Datalogger.ino: -------------------------------------------------------------------------------- 1 | /* 2 | LittleFS datalogger 3 | 4 | This example shows how to log data from three analog sensors 5 | to an storage device such as a FLASH. 6 | 7 | This example code is in the public domain. 8 | */ 9 | #include 10 | 11 | LittleFS_Program myfs; 12 | 13 | // NOTE: This option is only available on the Teensy 4.0, Teensy 4.1 and Teensy Micromod boards. 14 | // With the additonal option for security on the T4 the maximum flash available for a 15 | // program disk with LittleFS is 960 blocks of 1024 bytes 16 | #define PROG_FLASH_SIZE 1024 * 1024 * 1 // Specify size to use of onboard Teensy Program Flash chip 17 | // This creates a LittleFS drive in Teensy PCB FLash. 18 | 19 | File dataFile; // Specifes that dataFile is of File type 20 | 21 | int record_count = 0; 22 | bool write_data = false; 23 | uint32_t diskSize; 24 | 25 | void setup() 26 | { 27 | 28 | // Open serial communications and wait for port to open: 29 | Serial.begin(115200); 30 | while (!Serial) { 31 | // wait for serial port to connect. 32 | } 33 | Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__); 34 | 35 | Serial.println("Initializing LittleFS ..."); 36 | 37 | // see if the Flash is present and can be initialized: 38 | // lets check to see if the T4 is setup for security first 39 | #if ARDUINO_TEENSY40 40 | if ((IOMUXC_GPR_GPR11 & 0x100) == 0x100) { 41 | //if security is active max disk size is 960x1024 42 | if(PROG_FLASH_SIZE > 960*1024){ 43 | diskSize = 960*1024; 44 | Serial.printf("Security Enables defaulted to %u bytes\n", diskSize); 45 | } else { 46 | diskSize = PROG_FLASH_SIZE; 47 | Serial.printf("Security Not Enabled using %u bytes\n", diskSize); 48 | } 49 | } 50 | #else 51 | diskSize = PROG_FLASH_SIZE; 52 | #endif 53 | 54 | // checks that the LittFS program has started with the disk size specified 55 | if (!myfs.begin(diskSize)) { 56 | Serial.printf("Error starting %s\n", "PROGRAM FLASH DISK"); 57 | while (1) { 58 | // Error, so don't do anything more - stay stuck here 59 | } 60 | } 61 | Serial.println("LittleFS initialized."); 62 | 63 | menu(); 64 | 65 | } 66 | 67 | void loop() 68 | { 69 | if ( Serial.available() ) { 70 | char rr; 71 | rr = Serial.read(); 72 | switch (rr) { 73 | case 'l': listFiles(); break; 74 | case 'e': eraseFiles(); break; 75 | case 's': 76 | { 77 | Serial.println("\nLogging Data!!!"); 78 | write_data = true; // sets flag to continue to write data until new command is received 79 | // opens a file or creates a file if not present, FILE_WRITE will append data to 80 | // to the file created. 81 | dataFile = myfs.open("datalog.txt", FILE_WRITE); 82 | logData(); 83 | } 84 | break; 85 | case 'x': stopLogging(); break; 86 | case 'd': dumpLog(); break; 87 | case '\r': 88 | case '\n': 89 | case 'h': menu(); break; 90 | } 91 | while (Serial.read() != -1) ; // remove rest of characters. 92 | } 93 | 94 | if(write_data) logData(); 95 | } 96 | 97 | void logData() 98 | { 99 | // make a string for assembling the data to log: 100 | String dataString = ""; 101 | 102 | // read three sensors and append to the string: 103 | for (int analogPin = 0; analogPin < 3; analogPin++) { 104 | int sensor = analogRead(analogPin); 105 | dataString += String(sensor); 106 | if (analogPin < 2) { 107 | dataString += ","; 108 | } 109 | } 110 | 111 | // if the file is available, write to it: 112 | if (dataFile) { 113 | dataFile.println(dataString); 114 | // print to the serial port too: 115 | Serial.println(dataString); 116 | record_count += 1; 117 | } else { 118 | // if the file isn't open, pop up an error: 119 | Serial.println("error opening datalog.txt"); 120 | } 121 | delay(100); // run at a reasonable not-too-fast speed for testing 122 | } 123 | 124 | void stopLogging() 125 | { 126 | Serial.println("\nStopped Logging Data!!!"); 127 | write_data = false; 128 | // Closes the data file. 129 | dataFile.close(); 130 | Serial.printf("Records written = %d\n", record_count); 131 | } 132 | 133 | 134 | void dumpLog() 135 | { 136 | Serial.println("\nDumping Log!!!"); 137 | // open the file. 138 | dataFile = myfs.open("datalog.txt"); 139 | 140 | // if the file is available, write to it: 141 | if (dataFile) { 142 | while (dataFile.available()) { 143 | Serial.write(dataFile.read()); 144 | } 145 | dataFile.close(); 146 | } 147 | // if the file isn't open, pop up an error: 148 | else { 149 | Serial.println("error opening datalog.txt"); 150 | } 151 | } 152 | 153 | void menu() 154 | { 155 | Serial.println(); 156 | Serial.println("Menu Options:"); 157 | Serial.println("\tl - List files on disk"); 158 | Serial.println("\te - Erase files on disk"); 159 | Serial.println("\ts - Start Logging data (Restarting logger will append records to existing log)"); 160 | Serial.println("\tx - Stop Logging data"); 161 | Serial.println("\td - Dump Log"); 162 | Serial.println("\th - Menu"); 163 | Serial.println(); 164 | } 165 | 166 | void listFiles() 167 | { 168 | Serial.print("\n Space Used = "); 169 | Serial.println(myfs.usedSize()); 170 | Serial.print("Filesystem Size = "); 171 | Serial.println(myfs.totalSize()); 172 | 173 | printDirectory(myfs); 174 | } 175 | 176 | void eraseFiles() 177 | { 178 | myfs.quickFormat(); // performs a quick format of the created di 179 | Serial.println("\nFiles erased !"); 180 | } 181 | 182 | void printDirectory(FS &fs) { 183 | Serial.println("Directory\n---------"); 184 | printDirectory(fs.open("/"), 0); 185 | Serial.println(); 186 | } 187 | 188 | void printDirectory(File dir, int numSpaces) { 189 | while(true) { 190 | File entry = dir.openNextFile(); 191 | if (! entry) { 192 | //Serial.println("** no more files **"); 193 | break; 194 | } 195 | printSpaces(numSpaces); 196 | Serial.print(entry.name()); 197 | if (entry.isDirectory()) { 198 | Serial.println("/"); 199 | printDirectory(entry, numSpaces+2); 200 | } else { 201 | // files have sizes, directories do not 202 | printSpaces(36 - numSpaces - strlen(entry.name())); 203 | Serial.print(" "); 204 | Serial.println(entry.size(), DEC); 205 | } 206 | entry.close(); 207 | } 208 | } 209 | 210 | void printSpaces(int num) { 211 | for (int i=0; i < num; i++) { 212 | Serial.print(" "); 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /examples/Simple_DataLogger/LittleFS_QSPI_Simple_Datalogger/LittleFS_QSPI_Simple_Datalogger.ino: -------------------------------------------------------------------------------- 1 | /* 2 | LittleFS datalogger 3 | 4 | This example shows how to log data from three analog sensors 5 | to an storage device such as a FLASH. 6 | 7 | This example code is in the public domain. 8 | */ 9 | #include 10 | 11 | // LittleFS supports creating file systems (FS) in multiple memory types. Depending on the 12 | // memory type you want to use you would uncomment one of the following constructors 13 | 14 | //LittleFS_QSPIFlash myfs; // Used to create FS on QSPI NOR flash chips located on the bottom of the T4.1 such as the W25Q16JV*IQ/W25Q16FV, for the full list of supported NOR flash see https://github.com/PaulStoffregen/LittleFS#nor-flash 15 | LittleFS_QPINAND myfs; // Used to create FS on QSPI NAND flash chips located on the bottom of the T4.1 such as the W25N01G. for the full list of supported NAND flash see https://github.com/PaulStoffregen/LittleFS#nand-flash 16 | 17 | File dataFile; // Specifes that dataFile is of File type 18 | 19 | int record_count = 0; 20 | bool write_data = false; 21 | 22 | void setup() 23 | { 24 | 25 | // Open serial communications and wait for port to open: 26 | Serial.begin(115200); 27 | while (!Serial) { 28 | // wait for serial port to connect. 29 | } 30 | Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__); 31 | 32 | Serial.print("Initializing LittleFS ..."); 33 | 34 | // see if the Flash is present and can be initialized: 35 | if (!myfs.begin()) { 36 | Serial.printf("Error starting %s\n", "QSPI FLASH"); 37 | while (1) { 38 | // Error, so don't do anything more - stay stuck here 39 | } 40 | } 41 | Serial.println("LittleFS initialized."); 42 | 43 | menu(); 44 | 45 | } 46 | 47 | void loop() 48 | { 49 | if ( Serial.available() ) { 50 | char rr; 51 | rr = Serial.read(); 52 | switch (rr) { 53 | case 'l': listFiles(); break; 54 | case 'e': eraseFiles(); break; 55 | case 's': 56 | { 57 | Serial.println("\nLogging Data!!!"); 58 | write_data = true; // sets flag to continue to write data until new command is received 59 | // opens a file or creates a file if not present, FILE_WRITE will append data to 60 | // to the file created. 61 | dataFile = myfs.open("datalog.txt", FILE_WRITE); 62 | logData(); 63 | } 64 | break; 65 | case 'x': stopLogging(); break; 66 | case 'd': dumpLog(); break; 67 | case '\r': 68 | case '\n': 69 | case 'h': menu(); break; 70 | } 71 | while (Serial.read() != -1) ; // remove rest of characters. 72 | } 73 | 74 | if(write_data) logData(); 75 | } 76 | 77 | void logData() 78 | { 79 | // make a string for assembling the data to log: 80 | String dataString = ""; 81 | 82 | // read three sensors and append to the string: 83 | for (int analogPin = 0; analogPin < 3; analogPin++) { 84 | int sensor = analogRead(analogPin); 85 | dataString += String(sensor); 86 | if (analogPin < 2) { 87 | dataString += ","; 88 | } 89 | } 90 | 91 | // if the file is available, write to it: 92 | if (dataFile) { 93 | dataFile.println(dataString); 94 | // print to the serial port too: 95 | Serial.println(dataString); 96 | record_count += 1; 97 | } else { 98 | // if the file isn't open, pop up an error: 99 | Serial.println("error opening datalog.txt"); 100 | } 101 | delay(100); // run at a reasonable not-too-fast speed for testing 102 | } 103 | 104 | void stopLogging() 105 | { 106 | Serial.println("\nStopped Logging Data!!!"); 107 | write_data = false; 108 | // Closes the data file. 109 | dataFile.close(); 110 | Serial.printf("Records written = %d\n", record_count); 111 | } 112 | 113 | 114 | void dumpLog() 115 | { 116 | Serial.println("\nDumping Log!!!"); 117 | // open the file. 118 | dataFile = myfs.open("datalog.txt"); 119 | 120 | // if the file is available, write to it: 121 | if (dataFile) { 122 | while (dataFile.available()) { 123 | Serial.write(dataFile.read()); 124 | } 125 | dataFile.close(); 126 | } 127 | // if the file isn't open, pop up an error: 128 | else { 129 | Serial.println("error opening datalog.txt"); 130 | } 131 | } 132 | 133 | void menu() 134 | { 135 | Serial.println(); 136 | Serial.println("Menu Options:"); 137 | Serial.println("\tl - List files on disk"); 138 | Serial.println("\te - Erase files on disk"); 139 | Serial.println("\ts - Start Logging data (Restarting logger will append records to existing log)"); 140 | Serial.println("\tx - Stop Logging data"); 141 | Serial.println("\td - Dump Log"); 142 | Serial.println("\th - Menu"); 143 | Serial.println(); 144 | } 145 | 146 | void listFiles() 147 | { 148 | Serial.print("\n Space Used = "); 149 | Serial.println(myfs.usedSize()); 150 | Serial.print("Filesystem Size = "); 151 | Serial.println(myfs.totalSize()); 152 | 153 | printDirectory(myfs); 154 | } 155 | 156 | void eraseFiles() 157 | { 158 | myfs.quickFormat(); // performs a quick format of the created di 159 | Serial.println("\nFiles erased !"); 160 | } 161 | 162 | void printDirectory(FS &fs) { 163 | Serial.println("Directory\n---------"); 164 | printDirectory(fs.open("/"), 0); 165 | Serial.println(); 166 | } 167 | 168 | void printDirectory(File dir, int numSpaces) { 169 | while(true) { 170 | File entry = dir.openNextFile(); 171 | if (! entry) { 172 | //Serial.println("** no more files **"); 173 | break; 174 | } 175 | printSpaces(numSpaces); 176 | Serial.print(entry.name()); 177 | if (entry.isDirectory()) { 178 | Serial.println("/"); 179 | printDirectory(entry, numSpaces+2); 180 | } else { 181 | // files have sizes, directories do not 182 | printSpaces(36 - numSpaces - strlen(entry.name())); 183 | Serial.print(" "); 184 | Serial.println(entry.size(), DEC); 185 | } 186 | entry.close(); 187 | } 188 | } 189 | 190 | void printSpaces(int num) { 191 | for (int i=0; i < num; i++) { 192 | Serial.print(" "); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /examples/Simple_DataLogger/LittleFS_SPI_Simple_Datalogger/LittleFS_SPI_Simple_Datalogger.ino: -------------------------------------------------------------------------------- 1 | /* 2 | LittleFS datalogger 3 | 4 | This example shows how to log data from three analog sensors 5 | to an storage device such as a FLASH. 6 | 7 | This example code is in the public domain. 8 | */ 9 | #include 10 | 11 | // LittleFS supports creating file systems (FS) in multiple memory types. Depending on the 12 | // memory type you want to use you would uncomment one of the following constructors 13 | 14 | LittleFS_SPIFlash myfs; // Used to create FS on SPI NOR flash chips such as the W25Q16JV*IQ/W25Q16FV, 15 | // for the full list of supported NOR flash see 16 | // https://github.com/PaulStoffregen/LittleFS#nor-flash 17 | 18 | //LittleFS_SPINAND myfs; // Used to create FS on SPI NAND flash chips on a SPI port 19 | // such as SPI, SPI1, SPI2 etc. For the full list of supported 20 | // NAND Flash chips see https://github.com/PaulStoffregen/LittleFS#nand-flash 21 | 22 | //LittleFS_SPIFram myfs; // Used to create FS on FRAM memory chips such as the FM25V10-G. 23 | // For the full list of supported chips see https://github.com/PaulStoffregen/LittleFS#fram 24 | 25 | //Chip select pin for SPI lash chips such as the W25N01G. For the full list of supported NAND flash see 26 | // https://github.com/PaulStoffregen/LittleFS#nand-flash. 27 | // The chipSelect pin can be any digital pin. However, if using the Teensy Audio shield chipSelect is pin 6. 28 | const int chipSelect = 6; 29 | 30 | File dataFile; // Specifes that dataFile is of File type 31 | 32 | int record_count = 0; 33 | bool write_data = false; 34 | 35 | void setup() 36 | { 37 | 38 | // Open serial communications and wait for port to open: 39 | Serial.begin(115200); 40 | while (!Serial) { 41 | // wait for serial port to connect. 42 | } 43 | Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__); 44 | 45 | Serial.print("Initializing LittleFS ..."); 46 | 47 | // see if the Flash is present and can be initialized: 48 | // Note: SPI is default so if you are using SPI and not SPI for instance 49 | // you can just specify myfs.begin(chipSelect). 50 | if (!myfs.begin(chipSelect, SPI)) { 51 | Serial.printf("Error starting %s\n", "SPI FLASH"); 52 | while (1) { 53 | // Error, so don't do anything more - stay stuck here 54 | } 55 | } 56 | Serial.println("LittleFS initialized."); 57 | 58 | menu(); 59 | 60 | } 61 | 62 | void loop() 63 | { 64 | if ( Serial.available() ) { 65 | char rr; 66 | rr = Serial.read(); 67 | switch (rr) { 68 | case 'l': listFiles(); break; 69 | case 'e': eraseFiles(); break; 70 | case 's': 71 | { 72 | Serial.println("\nLogging Data!!!"); 73 | write_data = true; // sets flag to continue to write data until new command is received 74 | // opens a file or creates a file if not present, FILE_WRITE will append data to 75 | // to the file created. 76 | dataFile = myfs.open("datalog.txt", FILE_WRITE); 77 | logData(); 78 | } 79 | break; 80 | case 'x': stopLogging(); break; 81 | case 'd': dumpLog(); break; 82 | case '\r': 83 | case '\n': 84 | case 'h': menu(); break; 85 | } 86 | while (Serial.read() != -1) ; // remove rest of characters. 87 | } 88 | 89 | if(write_data) logData(); 90 | } 91 | 92 | void logData() 93 | { 94 | // make a string for assembling the data to log: 95 | String dataString = ""; 96 | 97 | // read three sensors and append to the string: 98 | for (int analogPin = 0; analogPin < 3; analogPin++) { 99 | int sensor = analogRead(analogPin); 100 | dataString += String(sensor); 101 | if (analogPin < 2) { 102 | dataString += ","; 103 | } 104 | } 105 | 106 | // if the file is available, write to it: 107 | if (dataFile) { 108 | dataFile.println(dataString); 109 | // print to the serial port too: 110 | Serial.println(dataString); 111 | record_count += 1; 112 | } else { 113 | // if the file isn't open, pop up an error: 114 | Serial.println("error opening datalog.txt"); 115 | } 116 | delay(100); // run at a reasonable not-too-fast speed for testing 117 | } 118 | 119 | void stopLogging() 120 | { 121 | Serial.println("\nStopped Logging Data!!!"); 122 | write_data = false; 123 | // Closes the data file. 124 | dataFile.close(); 125 | Serial.printf("Records written = %d\n", record_count); 126 | } 127 | 128 | 129 | void dumpLog() 130 | { 131 | Serial.println("\nDumping Log!!!"); 132 | // open the file. 133 | dataFile = myfs.open("datalog.txt"); 134 | 135 | // if the file is available, write to it: 136 | if (dataFile) { 137 | while (dataFile.available()) { 138 | Serial.write(dataFile.read()); 139 | } 140 | dataFile.close(); 141 | } 142 | // if the file isn't open, pop up an error: 143 | else { 144 | Serial.println("error opening datalog.txt"); 145 | } 146 | } 147 | 148 | void menu() 149 | { 150 | Serial.println(); 151 | Serial.println("Menu Options:"); 152 | Serial.println("\tl - List files on disk"); 153 | Serial.println("\te - Erase files on disk"); 154 | Serial.println("\ts - Start Logging data (Restarting logger will append records to existing log)"); 155 | Serial.println("\tx - Stop Logging data"); 156 | Serial.println("\td - Dump Log"); 157 | Serial.println("\th - Menu"); 158 | Serial.println(); 159 | } 160 | 161 | void listFiles() 162 | { 163 | Serial.print("\n Space Used = "); 164 | Serial.println(myfs.usedSize()); 165 | Serial.print("Filesystem Size = "); 166 | Serial.println(myfs.totalSize()); 167 | 168 | printDirectory(myfs); 169 | } 170 | 171 | void eraseFiles() 172 | { 173 | myfs.quickFormat(); // performs a quick format of the created di 174 | Serial.println("\nFiles erased !"); 175 | } 176 | 177 | void printDirectory(FS &fs) { 178 | Serial.println("Directory\n---------"); 179 | printDirectory(fs.open("/"), 0); 180 | Serial.println(); 181 | } 182 | 183 | void printDirectory(File dir, int numSpaces) { 184 | while(true) { 185 | File entry = dir.openNextFile(); 186 | if (! entry) { 187 | //Serial.println("** no more files **"); 188 | break; 189 | } 190 | printSpaces(numSpaces); 191 | Serial.print(entry.name()); 192 | if (entry.isDirectory()) { 193 | Serial.println("/"); 194 | printDirectory(entry, numSpaces+2); 195 | } else { 196 | // files have sizes, directories do not 197 | printSpaces(36 - numSpaces - strlen(entry.name())); 198 | Serial.print(" "); 199 | Serial.println(entry.size(), DEC); 200 | } 201 | entry.close(); 202 | } 203 | } 204 | 205 | void printSpaces(int num) { 206 | for (int i=0; i < num; i++) { 207 | Serial.print(" "); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /examples/Test_Integrity/MRAMSPI/MRAMSPI.ino: -------------------------------------------------------------------------------- 1 | #include "LittleFS.h" 2 | 3 | /* NOTES on LittleFS and Integrity testa 4 | - Sketches are a Media specific subset of tests done during development 5 | - When the Media is properly connected, it allows repeated FS file access to fill and verify function 6 | - The goal then was to repeatedly Create files and then Add to the file, or Delete the file 7 | - This allowed verifying File and Directory data integrtty as the Media was filled and reused as the data moved 8 | - Other than for Media and device testing - the code provides examples of support for the commands shown. 9 | - It will also provide a rough estimate of data I/O speed for the Media at hand 10 | - There is a Case Sensitive command set active when run displayed on start or when '?' is entered 11 | - For the primary looping enter number 1-9, h, or k and 'c' for continuous. A running count will stop with '0' 12 | - 'd' for directory will walk the directory and verify against the expected number of files added/deleted 13 | - Many commands were added as LittleFS developed, early work has lots of verbose settings now TOGGLED off for speed 14 | - 15 | - Each named sketch works on a specific Media type as presented 16 | - there are various commands provided to demonstrate usage 17 | - All .size() functions return a 64 bit uint64_t - take care when printing 18 | */ 19 | 20 | // Set for SPI usage of Mag or Ferrous RAM chips 21 | LittleFS_SPIFram myfs; 22 | const int FlashChipSelect = 10; // AUDIO BOARD is 10, other set as connected. 23 | char szDiskMem[] = "FRAM_DISK"; 24 | 25 | // Adjust these for amount of disk space used in iterations 26 | #define MAXNUM 5 // Number of files : ALPHA A-Z is MAX of 26, less for fewer files 27 | #define NUMDIRS 0 // Number of Directories to use 0 is Rootonly 28 | #define BIGADD 256 // bytes added each pass - bigger will quickly consume more space 29 | #define SUBADD 128 // bytes added each pass (*times file number) 30 | #define MAXFILL 2048 // 66000 // ZERO to disable :: Prevent iterations from over filling - require this much free 31 | 32 | // These can likely be left unchanged 33 | #define MYBLKSIZE 2048 // 2048 34 | #define SLACK_SPACE 40960 // allow for overhead slack space :: WORKS on FLASH {some need more with small alloc units} 35 | uint32_t DELSTART = 3; // originally was 3 + higher bias more to writes and larger files - lower odd 36 | #define DELDELAY 0 // delay before DEL files : delayMicroseconds 37 | #define ADDDELAY 0 // delay on ADD FILE : delayMicroseconds 38 | 39 | // Various Globals 40 | const uint32_t lowOffset = 'a' - 'A'; 41 | const uint32_t lowShift = 13; 42 | uint32_t errsLFS = 0, warnLFS = 0; // Track warnings or Errors 43 | uint32_t lCnt = 0, LoopCnt = 0; // loop counters 44 | uint64_t rdCnt = 0, wrCnt = 0; // Track Bytes Read and Written 45 | int filecount = 0; 46 | int loopLimit = 0; // -1 continuous, otherwise # to count down to 0 47 | bool pauseDir = false; // Start Pause on each off 48 | bool showDir = false; // false Start Dir on each off 49 | bool bDirVerify = false; // false Start Dir on each off 50 | bool bWriteVerify = true; // Verify on Write Toggle 51 | bool bAutoFormat = false; // false Auto formatUnused() off 52 | bool bCheckFormat = false; // false CheckFormat 53 | bool bCheckUsed = false; // false CheckUsed 54 | uint32_t res = 0; // for formatUnused 55 | File file3; // Single FILE used for all functions 56 | 57 | void setup() { 58 | while (!Serial) ; // wait 59 | Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__); 60 | Serial.println("LittleFS Test : File Integrity"); delay(5); 61 | 62 | if (!myfs.begin( FlashChipSelect )) { 63 | Serial.printf("Error starting %s\n", szDiskMem); 64 | while( 1 ); 65 | } 66 | filecount = printDirectoryFilecount( myfs.open("/") ); // Set base value of filecount for disk 67 | printDirectory(); 68 | parseCmd( '?' ); 69 | makeRootDirs(); 70 | checkInput( 1 ); 71 | filecount = printDirectoryFilecount( myfs.open("/") ); // Set base value of filecount for disk 72 | printDirectory(); 73 | } 74 | 75 | void loop() { 76 | char szDir[16]; 77 | LoopCnt++; 78 | uint32_t chStep; 79 | if ( loopLimit != 0 ) { 80 | for ( uint32_t ii = 0; ii <= NUMDIRS && ( loopLimit != 0 ); ii++ ) 81 | { 82 | if ( ii == 0 ) 83 | sprintf( szDir, "/" ); 84 | else 85 | sprintf( szDir, "/%lu_dir", ii ); 86 | chStep = fileCycle(szDir); 87 | if ( bAutoFormat && !(lCnt % 5) ) res = loopAutoFormat( 20, res ); 88 | while ( chStep != fileCycle(szDir) && ( loopLimit != 0 ) ) { 89 | if ( bAutoFormat && !(lCnt % 5) ) res = loopAutoFormat( 12, res ); // how often and how many depends on media and sizes 90 | checkInput( 0 ); // user input can 0 loopLimit 91 | } 92 | } 93 | checkInput( 0 ); 94 | if ( loopLimit > 0 ) // -1 means continuous 95 | loopLimit--; 96 | } 97 | else 98 | checkInput( 1 ); 99 | } 100 | -------------------------------------------------------------------------------- /examples/Test_Integrity/MRAMSPI/functions.ino: -------------------------------------------------------------------------------- 1 | 2 | char szInputs[] = "0123456789RdDwcghkFqvplmusSBbyYxfan+-?"; 3 | uint32_t lastTime; 4 | void checkInput( int step ) { // prompt for input without user input with step != 0 5 | uint32_t nowTime = micros(); 6 | 7 | char retVal = 0, temp; 8 | char *pTemp; 9 | if ( step != 0 ) { 10 | nowTime -= lastTime; 11 | Serial.printf( "[%6.2f M](%6.5f M elap) Awaiting input %s loops left %d >", millis() / 60000.0, nowTime / 60000000.0, szInputs, loopLimit ); 12 | } 13 | else { 14 | if ( !Serial.available() ) return; 15 | nowTime -= lastTime; 16 | Serial.printf( "[%6.2f M](%6.2f elap) Awaiting input %s loops left %d >", millis() / 60000.0, nowTime / 60000000.0, szInputs, loopLimit ); 17 | //Serial.printf( "[%6.2f] Awaiting input %s loops left %d >", millis() / 60000.0, szInputs, loopLimit ); 18 | while ( Serial.available() ) { 19 | temp = Serial.read( ); 20 | if ( (pTemp = strchr(szInputs, temp)) ) { 21 | retVal = pTemp[0]; 22 | parseCmd( retVal ); 23 | } 24 | } 25 | } 26 | while ( !Serial.available() ); 27 | while ( Serial.available() ) { 28 | temp = Serial.read(); 29 | if ( (pTemp = strchr(szInputs, temp)) ) { 30 | retVal = pTemp[0]; 31 | parseCmd( retVal ); 32 | } 33 | } 34 | Serial.print( '\n' ); 35 | if ( '?' == retVal ) checkInput( 1 ); // recurse on '?' to allow command show and response 36 | return; 37 | } 38 | void parseCmd( char chIn ) { // pass chIn == '?' for help 39 | uint32_t timeMe; 40 | char szNone[] = ""; 41 | switch (chIn ) { 42 | case '?': 43 | Serial.printf( "\n%s\n", " 1-9 '#' passes continue Loop before Pause\n\ 44 | 'c, or 0': Continuous Loop, or stop Loop in progress\n\ 45 | 'h, or k': start Hundred or Thousand Loops\n\ 46 | 'd' Directory of LittleFS Media\n\ 47 | 'D' Walk all Dir Files verify Read Size\n\ 48 | 'l' Show count of loop()'s, Bytes Read,Written\n\ 49 | 'B, or b': Make Big file half of free space, or remove all Big files\n\ 50 | 'S, or s': Make 2MB file , or remove all 2MB files\n\ 51 | \t>> Media Format : 'q' and 'F' remove ALL FS data\n\ 52 | 'q' Quick Format Disk \n\ 53 | 'F' Low Level Format Disk \n\ 54 | 'w' WIPE ALL Directories and Files\n\ 55 | 'm' Make ROOT dirs (needed after q/F format or 'w')\n\ 56 | 'f' LittleFS::formatUnused( ALL ) : DATA PRESERVED, empty blocks preformatted \n\ 57 | 'a' Auto formatUnused() during Loop - TOGGLE\n\ 58 | 'y' reclaim 1 block :: myfs.formatUnused( 1 )\n\ 59 | 'Y' reclaim 15 blocks :: myfs.formatUnused( 15 )\n\ 60 | \t>> Test Features \n\ 61 | 'g' run SpeedBench() Media speed benchmark\n\ 62 | 'x' Directory filecount verify - TOGGLE\n\ 63 | 'v' Verbose All Dir Prints - TOGGLE\n\ 64 | 'p' Pause after all Dir prints - TOGGLE\n\ 65 | 'n' No verify on Write- TOGGLE\n\ 66 | 'u' Update Filecount\n\ 67 | 'R' Restart Teensy - Except 'RAM' - data persists\n\ 68 | '+, or -': more, or less add .vs. delete in Loop\n\ 69 | '?' Help list : A Loop will Create, Extend, or Delete files to verify Integrity" ); 70 | break; 71 | case 'R': 72 | Serial.print(" RESTART Teensy ..."); 73 | delay(100); 74 | SCB_AIRCR = 0x05FA0004; 75 | break; 76 | case '0': 77 | case '1': 78 | case '2': 79 | case '3': 80 | case '4': 81 | case '5': 82 | case '6': 83 | case '7': 84 | case '8': 85 | case '9': 86 | loopLimit = chIn - '0'; 87 | if ( chIn != '0' ) // Preserve elapsed time on Error or STOP command 88 | lastTime = micros(); 89 | break; 90 | case 'b': 91 | bigFile( 0 ); // delete 92 | chIn = 0; 93 | break; 94 | case 'B': 95 | lastTime = micros(); 96 | bigFile( 1 ); // CREATE 97 | chIn = 0; 98 | break; 99 | case 's': 100 | bigFile2MB( 0 ); // CREATE 101 | chIn = 0; 102 | break; 103 | case 'S': 104 | lastTime = micros(); 105 | bigFile2MB( 1 ); // CREATE 106 | chIn = 0; 107 | break; 108 | case 'c': 109 | loopLimit = -1; 110 | break; 111 | case 'g': 112 | lastTime = micros(); 113 | speedBench(); 114 | chIn = 0; 115 | break; 116 | case 'd': 117 | Serial.print( " d\n" ); 118 | lastTime = micros(); 119 | printDirectory(); 120 | Serial.print( '\n' ); 121 | parseCmd( 'l' ); 122 | checkInput( 1 ); 123 | chIn = 0; 124 | break; 125 | case 'D': 126 | Serial.println("\nWalk all Files verify Read Size:\t"); 127 | lastTime = micros(); 128 | Serial.printf( "\t%u Errors found\n" , DirectoryVerify( myfs.open("/") ) ); 129 | Serial.printf("\nBytes Used: %llu, Bytes Total:%llu\n", myfs.usedSize(), myfs.totalSize()); 130 | parseCmd( 'l' ); 131 | checkInput( 1 ); 132 | chIn = 0; 133 | break; 134 | case 'w': 135 | Serial.println("\nWipe All Files and DIRS:"); 136 | deleteAllDirectory(myfs.open("/"), szNone ); 137 | errsLFS = 0; // No Errors on new Format 138 | warnLFS = 0; // No warning on new Format 139 | chIn = 0; 140 | parseCmd( 'u' ); 141 | break; 142 | case 'h': 143 | loopLimit = 100; 144 | lastTime = micros(); 145 | break; 146 | case 'k': 147 | loopLimit = 1000; 148 | lastTime = micros(); 149 | break; 150 | case 'F': // Low Level format 151 | Serial.print( "\nFormatting Low Level:\n\t" ); 152 | lastTime = micros(); 153 | timeMe = micros(); 154 | myfs.lowLevelFormat('.'); 155 | timeMe = micros() - timeMe; 156 | Serial.printf( "\n Done Formatting Low Level in %lu us.\n", timeMe ); 157 | errsLFS = 0; // No Errors on new Format 158 | warnLFS = 0; // No warning on new Format 159 | bCheckFormat = false; 160 | parseCmd( 'u' ); 161 | break; 162 | case 'q': // quick format 163 | lastTime = micros(); 164 | myfs.quickFormat(); 165 | errsLFS = 0; // No Errors on new Format 166 | parseCmd( 'u' ); 167 | break; 168 | case 'v': // verbose dir 169 | showDir = !showDir; 170 | showDir ? Serial.print(" Verbose on: ") : Serial.print(" Verbose off: "); 171 | chIn = 0; 172 | break; 173 | case 'p': // pause on dirs 174 | pauseDir = !pauseDir; 175 | pauseDir ? Serial.print(" Pause on: ") : Serial.print(" Pause off: "); 176 | chIn = 0; 177 | break; 178 | case 'x': // dir filecount Verify 179 | bDirVerify = !bDirVerify; 180 | bDirVerify ? Serial.print(" FileCnt on: ") : Serial.print(" FileCnt off: "); 181 | lastTime = micros(); 182 | dirVerify(); 183 | chIn = 0; 184 | break; 185 | case 'n': // No Verify on write 186 | bWriteVerify = !bWriteVerify; 187 | bWriteVerify ? Serial.print(" Write Verify on: ") : Serial.print(" Write Verify off: "); 188 | chIn = 0; 189 | break; 190 | case 'a': // Auto myfs.formatUnused() during iterations 191 | bAutoFormat = !bAutoFormat; 192 | bAutoFormat ? Serial.print(" \nAuto formatUnused() On: ") : Serial.print(" \nAuto formatUnused() Off: "); 193 | chIn = 0; 194 | break; 195 | case 'y': // Reclaim 1 unused format 196 | lastTime = micros(); 197 | Serial.printf( "\n myfs.formatUnused( 1 ) ...\n" ); 198 | timeMe = micros(); 199 | res = myfs.formatUnused( 1, res ); 200 | timeMe = micros() - timeMe; 201 | Serial.printf( "\n\t formatUnused :: Done Formatting Low Level in %lu us (last %lu).\n", timeMe, res ); 202 | chIn = 0; 203 | break; 204 | case 'Y': // Reclaim 15 unused format 205 | lastTime = micros(); 206 | Serial.printf( "\n myfs.formatUnused( 15 ) ...\n" ); 207 | timeMe = micros(); 208 | res = myfs.formatUnused( 15, res ); 209 | timeMe = micros() - timeMe; 210 | Serial.printf( "\n\t formatUnused :: Done Formatting Low Level in %lu us (last %lu).\n", timeMe, res ); 211 | chIn = 0; 212 | break; 213 | case 'f': // Reclaim all unused format 214 | lastTime = micros(); 215 | Serial.printf( "\n myfs.formatUnused( 0 ) ...\n" ); 216 | timeMe = micros(); 217 | myfs.formatUnused( 0, 0 ); 218 | timeMe = micros() - timeMe; 219 | Serial.printf( "\n\t formatUnused :: Done Formatting Low Level in %lu us.\n", timeMe ); 220 | chIn = 0; 221 | break; 222 | case 'l': // Show Loop Count 223 | Serial.printf("\n\t Loop Count: %u (#fileCycle=%u), Bytes read %llu, written %llu, #Files=%u\n", LoopCnt, lCnt, rdCnt, wrCnt, filecount ); 224 | if ( 0 != errsLFS ) 225 | Serial.printf("\t ERROR COUNT =%u\n", errsLFS ); 226 | if ( 0 != warnLFS ) 227 | Serial.printf("\t Free Space Warn COUNT =%u\n", warnLFS ); 228 | dirVerify(); 229 | chIn = 0; 230 | break; 231 | case 'm': 232 | Serial.printf("m \n\t Making Root Dirs\n" ); 233 | makeRootDirs(); 234 | parseCmd( 'd' ); 235 | chIn = 0; 236 | break; 237 | case 'u': // Show Loop Count 238 | filecount = printDirectoryFilecount( myfs.open("/") ); 239 | Serial.printf("\n\t Updated filecount %u\n", filecount ); 240 | chIn = 0; 241 | break; 242 | case '+': // increase add cycles 243 | DELSTART++; 244 | Serial.printf("+\n Deletes start after %u cycles ", DELSTART); 245 | chIn = 0; 246 | break; 247 | case '-': // decrease add cycles 248 | DELSTART--; 249 | if ( DELSTART < 1 ) DELSTART = 1; 250 | Serial.printf("-\n Deletes start after %u cycles ", DELSTART); 251 | chIn = 0; 252 | break; 253 | default: 254 | Serial.println( chIn ); // never see without unhandled char in szInputs[] 255 | break; 256 | } 257 | if ( 0 != chIn ) Serial.print( chIn ); 258 | } 259 | 260 | uint32_t loopAutoFormat( uint32_t cnt, uint32_t myres ) { 261 | uint32_t retres; 262 | retres = myfs.formatUnused( cnt, myres ); 263 | Serial.printf("\t fmtU @ %lu - %lu \n", myres, retres ); 264 | return retres; 265 | } 266 | 267 | uint32_t fTot, totSize; 268 | void printDirectory() { 269 | fTot = 0, totSize = 0; 270 | Serial.printf("printDirectory %s\n--------------\n", szDiskMem); 271 | printDirectory(myfs.open("/"), 0); 272 | Serial.printf(" %Total %u files of Size %u Bytes\n", fTot, totSize); 273 | Serial.printf("Bytes Used: %llu, Bytes Total:%llu\n", myfs.usedSize(), myfs.totalSize()); 274 | if ( myfs.usedSize() == myfs.totalSize() ) { 275 | Serial.printf("\n\n\tWARNING: DISK FULL >>>>> Bytes Used: %llu, Bytes Total:%llu\n\n", myfs.usedSize(), myfs.totalSize()); 276 | warnLFS++; 277 | } 278 | } 279 | 280 | void deleteAllDirectory(File dir, char *fullPath ) { 281 | char myPath[ 256 ] = ""; 282 | while (true) { 283 | File entry = dir.openNextFile(); 284 | if (! entry) { 285 | // no more files 286 | break; 287 | } 288 | if (entry.isDirectory()) { 289 | strcpy( myPath, fullPath); 290 | strcat( myPath, entry.name()); 291 | strcat( myPath, "/"); 292 | deleteAllDirectory(entry, myPath); 293 | entry.close(); 294 | if ( !myfs.remove( myPath ) ) 295 | Serial.print( " Fail remove DIR>\t"); 296 | else 297 | Serial.print( " Removed DIR>\t"); 298 | Serial.println( myPath ); 299 | 300 | } else { 301 | strcpy( myPath, fullPath); 302 | strcat( myPath, entry.name()); 303 | entry.close(); 304 | if ( !myfs.remove( myPath ) ) 305 | Serial.print( "\tFail remove>\t"); 306 | else 307 | Serial.print( "\tRemoved>\t"); 308 | Serial.println( myPath ); 309 | } 310 | } 311 | } 312 | 313 | int printDirectoryFilecount(File dir) { 314 | unsigned int filecnt = 0; 315 | while (true) { 316 | File entry = dir.openNextFile(); 317 | if (! entry) { 318 | // no more files 319 | break; 320 | } 321 | if (entry.isDirectory()) { 322 | filecnt += printDirectoryFilecount(entry); 323 | } else { 324 | filecnt++; 325 | } 326 | entry.close(); 327 | } 328 | return filecnt; 329 | } 330 | 331 | void printDirectory(File dir, int numTabs) { 332 | uint64_t fSize = 0; 333 | uint32_t dCnt = 0, fCnt = 0; 334 | if ( 0 == dir ) { 335 | Serial.printf( "\t>>>\t>>>>> No Dir\n" ); 336 | return; 337 | } 338 | while (true) { 339 | File entry = dir.openNextFile(); 340 | if (! entry) { 341 | // no more files 342 | Serial.printf("\n %u dirs with %u files of Size %llu Bytes\n", dCnt, fCnt, fSize); 343 | fTot += fCnt; 344 | totSize += fSize; 345 | break; 346 | } 347 | for (uint8_t i = 0; i < numTabs; i++) { 348 | Serial.print('\t'); 349 | } 350 | 351 | if (entry.isDirectory()) { 352 | Serial.print("DIR\t"); 353 | dCnt++; 354 | } else { 355 | Serial.print("FILE\t"); 356 | fCnt++; 357 | fSize += entry.size(); 358 | } 359 | Serial.print(entry.name()); 360 | if (entry.isDirectory()) { 361 | Serial.println(" / "); 362 | printDirectory(entry, numTabs + 1); 363 | } else { 364 | // files have sizes, directories do not 365 | Serial.print("\t\t"); 366 | Serial.println(entry.size(), DEC); 367 | } 368 | entry.close(); 369 | //Serial.flush(); 370 | } 371 | } 372 | 373 | int DirectoryVerify(File dir) { 374 | int errCnt = 0; 375 | while (true) { 376 | File entry = dir.openNextFile(); 377 | if (! entry) { 378 | break; 379 | } 380 | if (entry.isDirectory()) { 381 | Serial.print("\tD"); 382 | Serial.print(entry.name()[0]); 383 | Serial.print(" "); 384 | errCnt += DirectoryVerify(entry); 385 | Serial.print("\n"); 386 | } else { 387 | uint64_t fileSize, sizeCnt = 0; 388 | char mm; 389 | Serial.print(entry.name()[0]); 390 | fileSize = entry.size(); 391 | while ( entry.available() ) { 392 | if (fileSize < sizeCnt ) break; 393 | entry.read( &mm , 1 ); 394 | sizeCnt++; 395 | } 396 | if (fileSize != sizeCnt ) { 397 | Serial.printf("\n File Size Error:: %s found %llu Bytes for Size %llu \n", entry.name(), sizeCnt, fileSize); 398 | errCnt++; 399 | errsLFS++; 400 | } 401 | } 402 | entry.close(); 403 | } 404 | return errCnt; 405 | } 406 | 407 | uint32_t cCnt = 0; 408 | uint32_t fileCycle(const char *dir) { 409 | static char szFile[] = "_file.txt"; 410 | char szPath[150]; 411 | int ii; 412 | lCnt++; 413 | byte nNum = lCnt % MAXNUM; 414 | char chNow = 'A' + lCnt % MAXNUM; 415 | lfs_ssize_t resW = 1; 416 | uint32_t timeMeAll = micros(); 417 | 418 | if ( dir[1] == 0 ) // catch root 419 | sprintf( szPath, "/%c%s", chNow, szFile ); 420 | else 421 | sprintf( szPath, "%s/%c%s", dir, chNow, szFile ); 422 | if ( cCnt >= DELSTART && myfs.exists(szPath) ) { // DELETE ALL KNOWN FILES 423 | if ( nNum == 1 ) { 424 | Serial.print( "\n == == == DELETE PASS START == == == = \n"); 425 | if ( showDir ) { 426 | printDirectory(); 427 | Serial.print( " == == == DELETE PASS START == == == = \n"); 428 | } 429 | delayMicroseconds(DELDELAY); 430 | } 431 | } 432 | Serial.printf( ":: %s ", szPath ); 433 | if ( cCnt >= DELSTART && myfs.exists(szPath) ) { // DELETE ALL KNOWN FILES 434 | readVerify( szPath, chNow ); 435 | myfs.remove(szPath); 436 | filecount--; 437 | Serial.printf(" ----DEL----"); 438 | Serial.printf(" -- %c", chNow); 439 | if ( showDir ) { 440 | Serial.print("\n"); 441 | printDirectory(myfs.open(dir), 1); 442 | } 443 | if ( pauseDir ) checkInput( 1 ); 444 | Serial.println(); 445 | } 446 | else { 447 | if ( bWriteVerify && ( myfs.totalSize() - myfs.usedSize() ) < MAXFILL ) { 448 | warnLFS++; 449 | Serial.printf( "\tXXX\tXXX\tXXX\tXXX\tSIZE WARNING { MAXFILL } \n" ); 450 | cCnt = DELSTART; 451 | return cCnt; // EARLY EXIT 452 | } 453 | if ( nNum == 0 ) { 454 | nNum = 10; 455 | cCnt++; 456 | if ( cCnt >= DELSTART + 2 ) cCnt = 0; 457 | } 458 | file3 = myfs.open(szPath, FILE_WRITE); 459 | if ( 0 == file3 ) { 460 | Serial.printf( "\tXXX\tXXX\tXXX\tXXX\tFail File open {mkdir?}\n" ); 461 | delayMicroseconds(300000); 462 | checkInput( 1 ); // PAUSE on CmdLine 463 | } 464 | else { 465 | delayMicroseconds(ADDDELAY); 466 | char mm = chNow + lowOffset; 467 | uint64_t jj = file3.size() + 1; 468 | uint32_t timeMe = micros(); 469 | int32_t nn = nNum * SUBADD + BIGADD; 470 | for ( ii = 0; ii < nn && resW >= 0; ii++ ) { 471 | if ( 0 == ((ii + jj) / lowShift) % 2 ) 472 | resW = file3.write( &mm , 1 ); 473 | else 474 | resW = file3.write( &chNow , 1 ); 475 | wrCnt++; 476 | // if ( lCnt%100 == 50 ) mm='x'; // GENERATE ERROR to detect on DELETE read verify 477 | } 478 | file3.close(); 479 | timeMe = micros() - timeMe; 480 | timeMeAll = micros() - timeMeAll; 481 | Serial.printf(" +++ size %llu: Add %d @KB/sec %5.2f ", jj - 1, nn, ii / (timeMeAll / 1000.0) ); 482 | if (resW < 0) { 483 | Serial.printf( "\n\twrite fail ERR# %i 0x%X \n", resW, resW ); 484 | parseCmd( '0' ); 485 | errsLFS++; 486 | checkInput( 1 ); // PAUSE on CmdLine 487 | } 488 | else if ( jj == 1 ) filecount++; // File Added 489 | Serial.print("\t"); 490 | if ( bWriteVerify ) 491 | readVerify( szPath, chNow ); 492 | Serial.print('\n'); 493 | if ( showDir ) { 494 | Serial.print('\n'); 495 | printDirectory(myfs.open(dir), 1); 496 | } 497 | } 498 | if ( pauseDir ) checkInput( 1 ); 499 | //Serial.print("\n"); 500 | delayMicroseconds(ADDDELAY); 501 | } 502 | checkInput( 0 ); // user stop request? 503 | if ( bDirVerify ) dirVerify(); 504 | return cCnt; 505 | } 506 | 507 | void dirVerify() { 508 | uint32_t timeMe = micros(); 509 | Serial.printf("\tdirV..."); 510 | if ( filecount != printDirectoryFilecount( myfs.open("/") ) ) { 511 | Serial.printf( "\tFilecount mismatch %u != %u\n", filecount, printDirectoryFilecount( myfs.open("/") ) ); 512 | parseCmd( '0' ); 513 | errsLFS++; 514 | checkInput( 1 ); // PAUSE on CmdLine 515 | } 516 | timeMe = micros() - timeMe; 517 | Serial.printf("%lu_us\t", timeMe); 518 | } 519 | 520 | void readVerify( char szPath[], char chNow ) { 521 | uint32_t timeMe = micros(); 522 | file3 = myfs.open(szPath); 523 | if ( 0 == file3 ) { 524 | Serial.printf( "\tV\t Fail File open %s\n", szPath ); 525 | parseCmd( '0' ); 526 | errsLFS++; 527 | checkInput( 1 ); 528 | } 529 | char mm; 530 | char chNow2 = chNow + lowOffset; 531 | uint32_t ii = 0; 532 | while ( file3.available() ) { 533 | file3.read( &mm , 1 ); 534 | rdCnt++; 535 | //Serial.print( mm ); // show chars as read 536 | ii++; 537 | if ( 0 == (ii / lowShift) % 2 ) { 538 | if ( chNow2 != mm ) { 539 | Serial.printf( " fSize ) { // catch over length return makes bad loop !!! 593 | Serial.printf( "\n\tFile LEN Corrupt! FS returning over %u bytes\n", fSize ); 594 | parseCmd( '0' ); 595 | errsLFS++; 596 | checkInput( 1 ); 597 | break; 598 | } 599 | } 600 | if (ii != file3.size()) { 601 | Serial.printf( "\n\tRead Count fail! :: read %u != f.size %llu\n", ii, file3.size() ); 602 | parseCmd( '0' ); 603 | errsLFS++; 604 | checkInput( 1 ); // PAUSE on CmdLine 605 | } 606 | else 607 | Serial.printf( "\tGOOD! >> bytes %lu", ii ); 608 | file3.close(); 609 | timeMe = micros() - timeMe; 610 | Serial.printf( "\n\tBig read&compare KBytes per second %5.2f \n", ii / (timeMe / 1000.0) ); 611 | if ( 0 == ii ) return false; 612 | return true; 613 | } 614 | 615 | 616 | void bigFile( int doThis ) { 617 | char myFile[] = "/0_bigfile.txt"; 618 | char fileID = '0' - 1; 619 | 620 | if ( 0 == doThis ) { // delete File 621 | Serial.printf( "\nDelete with read verify all #bigfile's\n"); 622 | do { 623 | fileID++; 624 | myFile[1] = fileID; 625 | if ( myfs.exists(myFile) && bigVerify( myFile, fileID) ) { 626 | filecount--; 627 | myfs.remove(myFile); 628 | } 629 | else break; // no more of these 630 | } while ( 1 ); 631 | } 632 | else { // FILL DISK 633 | lfs_ssize_t resW = 1; 634 | char someData[2048]; 635 | uint32_t xx, toWrite; 636 | toWrite = (myfs.totalSize()) - myfs.usedSize(); 637 | if ( toWrite < 65535 ) { 638 | Serial.print( "Disk too full! DO :: b or q or F"); 639 | return; 640 | } 641 | toWrite -= SLACK_SPACE; 642 | toWrite /= 2; 643 | xx = toWrite; 644 | Serial.printf( "\nStart Big write of %u Bytes", xx); 645 | uint32_t timeMe = micros(); 646 | file3 = nullptr; 647 | do { 648 | if ( file3 ) file3.close(); 649 | fileID++; 650 | myFile[1] = fileID; 651 | file3 = myfs.open(myFile, FILE_WRITE); 652 | } while ( fileID < '9' && file3.size() > 0); 653 | if ( fileID == '9' ) { 654 | Serial.print( "Disk has 9 halves 0-8! DO :: b or q or F"); 655 | return; 656 | } 657 | memset( someData, fileID, 2048 ); 658 | int hh = 0; 659 | while ( toWrite > 2048 && resW > 0 ) { 660 | resW = file3.write( someData , 2048 ); 661 | hh++; 662 | if ( !(hh % 40) ) Serial.print('.'); 663 | toWrite -= 2048; 664 | } 665 | xx -= toWrite; 666 | file3.close(); 667 | timeMe = micros() - timeMe; 668 | file3 = myfs.open(myFile, FILE_WRITE); 669 | if ( file3.size() > 0 ) { 670 | filecount++; 671 | Serial.printf( "\nBig write %s took %5.2f Sec for %lu Bytes : file3.size()=%llu", myFile , timeMe / 1000000.0, xx, file3.size() ); 672 | } 673 | if ( file3 != 0 ) file3.close(); 674 | Serial.printf( "\n\tBig write KBytes per second %5.2f \n", xx / (timeMe / 1000.0) ); 675 | Serial.printf("\nBytes Used: %llu, Bytes Total:%llu\n", myfs.usedSize(), myfs.totalSize()); 676 | if ( myfs.usedSize() == myfs.totalSize() ) { 677 | Serial.printf("\n\n\tWARNING: DISK FULL >>>>> Bytes Used: %llu, Bytes Total:%llu\n\n", myfs.usedSize(), myfs.totalSize()); 678 | warnLFS++; 679 | } 680 | if ( resW < 0 ) { 681 | Serial.printf( "\nBig write ERR# %i 0x%X \n", resW, resW ); 682 | errsLFS++; 683 | myfs.remove(myFile); 684 | } 685 | } 686 | } 687 | 688 | void bigFile2MB( int doThis ) { 689 | char myFile[] = "/0_2MBfile.txt"; 690 | char fileID = '0' - 1; 691 | 692 | if ( 0 == doThis ) { // delete File 693 | Serial.printf( "\nDelete with read verify all #bigfile's\n"); 694 | do { 695 | fileID++; 696 | myFile[1] = fileID; 697 | if ( myfs.exists(myFile) && bigVerify( myFile, fileID) ) { 698 | filecount--; 699 | myfs.remove(myFile); 700 | } 701 | else break; // no more of these 702 | } while ( 1 ); 703 | } 704 | else { // FILL DISK 705 | lfs_ssize_t resW = 1; 706 | char someData[2048]; 707 | uint32_t xx, toWrite; 708 | toWrite = 2048 * 1000; 709 | if ( toWrite > (65535 + (myfs.totalSize() - myfs.usedSize()) ) ) { 710 | Serial.print( "Disk too full! DO :: q or F"); 711 | return; 712 | } 713 | xx = toWrite; 714 | Serial.printf( "\nStart Big write of %u Bytes", xx); 715 | uint32_t timeMe = micros(); 716 | file3 = nullptr; 717 | do { 718 | if ( file3 ) file3.close(); 719 | fileID++; 720 | myFile[1] = fileID; 721 | file3 = myfs.open(myFile, FILE_WRITE); 722 | } while ( fileID < '9' && file3.size() > 0); 723 | if ( fileID == '9' ) { 724 | Serial.print( "Disk has 9 files 0-8! DO :: b or q or F"); 725 | return; 726 | } 727 | memset( someData, fileID, 2048 ); 728 | int hh = 0; 729 | while ( toWrite >= 2048 && resW > 0 ) { 730 | resW = file3.write( someData , 2048 ); 731 | hh++; 732 | if ( !(hh % 40) ) Serial.print('.'); 733 | toWrite -= 2048; 734 | } 735 | xx -= toWrite; 736 | file3.close(); 737 | timeMe = micros() - timeMe; 738 | file3 = myfs.open(myFile, FILE_WRITE); 739 | if ( file3.size() > 0 ) { 740 | filecount++; 741 | Serial.printf( "\nBig write %s took %5.2f Sec for %lu Bytes : file3.size()=%llu", myFile , timeMe / 1000000.0, xx, file3.size() ); 742 | } 743 | if ( file3 != 0 ) file3.close(); 744 | Serial.printf( "\n\tBig write KBytes per second %5.2f \n", xx / (timeMe / 1000.0) ); 745 | Serial.printf("\nBytes Used: %llu, Bytes Total:%llu\n", myfs.usedSize(), myfs.totalSize()); 746 | if ( myfs.usedSize() == myfs.totalSize() ) { 747 | Serial.printf("\n\n\tWARNING: DISK FULL >>>>> Bytes Used: %llu, Bytes Total:%llu\n\n", myfs.usedSize(), myfs.totalSize()); 748 | warnLFS++; 749 | } 750 | if ( resW < 0 ) { 751 | Serial.printf( "\nBig write ERR# %i 0x%X \n", resW, resW ); 752 | errsLFS++; 753 | myfs.remove(myFile); 754 | } 755 | } 756 | } 757 | 758 | void makeRootDirs() { 759 | char szDir[16]; 760 | for ( uint32_t ii = 1; ii <= NUMDIRS; ii++ ) { 761 | sprintf( szDir, "/%lu_dir", ii ); 762 | myfs.mkdir( szDir ); 763 | } 764 | } 765 | 766 | // for void speedBench() 767 | #include 768 | ArduinoOutStream cout(Serial); 769 | File file; 770 | 771 | // File size in bytes. 772 | const uint32_t FILE_SIZE = 16 * 1024; 773 | 774 | // Set SKIP_FIRST_LATENCY true if the first read/write to the SD can 775 | // be avoid by writing a file header or reading the first record. 776 | const bool SKIP_FIRST_LATENCY = true; 777 | 778 | // Size of read/write. 779 | const size_t BUF_SIZE = 2048; 780 | 781 | // Write pass count. 782 | const uint8_t WRITE_COUNT = 5; 783 | 784 | // Read pass count. 785 | const uint8_t READ_COUNT = 5; 786 | 787 | //Block size for qspi 788 | #define MYBLKSIZE 2048 // 2048 789 | 790 | // Insure 4-byte alignment. 791 | uint32_t buf32[(BUF_SIZE + 3) / 4]; 792 | uint8_t* bufA32 = (uint8_t*)buf32; 793 | 794 | //Number of random reads 795 | #define randomReads 1 796 | 797 | void speedBench() { 798 | File file; 799 | float s; 800 | uint32_t t; 801 | uint32_t maxLatency; 802 | uint32_t minLatency; 803 | uint32_t totalLatency; 804 | bool skipLatency; 805 | 806 | myfs.remove("bench.dat"); 807 | //for(uint8_t cnt=0; cnt < 10; cnt++) { 808 | 809 | // fill buf with known data 810 | if (BUF_SIZE > 1) { 811 | for (size_t i = 0; i < (BUF_SIZE - 2); i++) { 812 | bufA32[i] = 'A' + (i % 26); 813 | } 814 | bufA32[BUF_SIZE - 2] = '\r'; 815 | } 816 | bufA32[BUF_SIZE - 1] = '\n'; 817 | 818 | Serial.printf("%s Disk Stats:", szDiskMem ); 819 | Serial.printf("Bytes Used: %llu, Bytes Total:%llu\n", myfs.usedSize(), myfs.totalSize()); 820 | Serial.printf("%s Benchmark:\n", szDiskMem ); 821 | cout << F("FILE_SIZE = ") << FILE_SIZE << endl; 822 | cout << F("BUF_SIZE = ") << BUF_SIZE << F(" bytes\n"); 823 | cout << F("Starting write test, please wait.") << endl << endl; 824 | 825 | // do write test 826 | uint32_t n = FILE_SIZE / BUF_SIZE; 827 | cout << F("write speed and latency") << endl; 828 | cout << F("speed,max,min,avg") << endl; 829 | cout << F("KB/Sec,usec,usec,usec") << endl; 830 | 831 | // open or create file - truncate existing file. 832 | file = myfs.open("bench.dat", FILE_WRITE); 833 | 834 | for (uint8_t nTest = 0; nTest < WRITE_COUNT; nTest++) { 835 | file.seek(0); 836 | 837 | maxLatency = 0; 838 | minLatency = 9999999; 839 | totalLatency = 0; 840 | skipLatency = SKIP_FIRST_LATENCY; 841 | t = millis(); 842 | 843 | for (uint32_t i = 0; i < n; i++) { 844 | uint32_t m = micros(); 845 | if (file.write(bufA32, BUF_SIZE) != BUF_SIZE) { 846 | Serial.println("write failed"); 847 | } 848 | m = micros() - m; 849 | totalLatency += m; 850 | if (skipLatency) { 851 | // Wait until first write to SD, not just a copy to the cache. 852 | skipLatency = file.position() < 512; 853 | } else { 854 | if (maxLatency < m) { 855 | maxLatency = m; 856 | } 857 | if (minLatency > m) { 858 | minLatency = m; 859 | } 860 | } 861 | } 862 | 863 | t = millis() - t; 864 | s = file.size(); 865 | cout << s / t << ',' << maxLatency << ',' << minLatency; 866 | cout << ',' << totalLatency / n << endl; 867 | } 868 | cout << endl << F("Starting sequential read test, please wait.") << endl; 869 | cout << endl << F("read speed and latency") << endl; 870 | cout << F("speed,max,min,avg") << endl; 871 | cout << F("KB/Sec,usec,usec,usec") << endl; 872 | 873 | // do read test 874 | for (uint8_t nTest = 0; nTest < READ_COUNT; nTest++) { 875 | file.seek(0); 876 | maxLatency = 0; 877 | minLatency = 9999999; 878 | totalLatency = 0; 879 | skipLatency = SKIP_FIRST_LATENCY; 880 | t = micros(); 881 | for (uint32_t i = 0; i < n; i++) { 882 | bufA32[BUF_SIZE - 1] = 0; 883 | uint32_t m = micros(); 884 | int32_t nr = file.read(bufA32, BUF_SIZE); 885 | if (nr != BUF_SIZE) { 886 | Serial.println("read failed"); 887 | } 888 | m = micros() - m; 889 | totalLatency += m; 890 | if (bufA32[BUF_SIZE - 1] != '\n') { 891 | Serial.println("data check error"); 892 | } 893 | if (skipLatency) { 894 | skipLatency = false; 895 | } else { 896 | if (maxLatency < m) { 897 | maxLatency = m; 898 | } 899 | if (minLatency > m) { 900 | minLatency = m; 901 | } 902 | } 903 | } 904 | 905 | s = file.size(); 906 | 907 | t = micros() - t; 908 | cout << s * 1000 / t << ',' << maxLatency << ',' << minLatency; 909 | cout << ',' << totalLatency / n << endl; 910 | } 911 | 912 | cout << endl << F("Done") << endl; 913 | 914 | cout << endl << F("Starting random read test, please wait.") << endl; 915 | 916 | Serial.printf("Number of random reads: %d\n", randomReads); 917 | Serial.printf("Number of blocks: %d\n", n); 918 | 919 | cout << endl << F("read speed and latency") << endl; 920 | cout << F("speed,max,min,avg") << endl; 921 | 922 | // do read test 923 | for (uint8_t nTest = 0; nTest < READ_COUNT; nTest++) { 924 | file.seek(0); 925 | maxLatency = 0; 926 | minLatency = 0; 927 | totalLatency = 0; 928 | skipLatency = SKIP_FIRST_LATENCY; 929 | t = micros(); 930 | for (uint32_t i = 0; i < randomReads; i++) { 931 | bufA32[BUF_SIZE - 1] = 0; 932 | uint32_t m = micros(); 933 | uint32_t block_pos = random(0, (n - 1)); 934 | uint32_t random_pos = block_pos * MYBLKSIZE; 935 | cout << "Position (bytes), Block: " << random_pos << ", "; 936 | cout << block_pos << endl; 937 | uint32_t startCNT = ARM_DWT_CYCCNT; 938 | file.seek(random_pos); 939 | int32_t nr = file.read(bufA32, BUF_SIZE); 940 | uint32_t endCNT = ARM_DWT_CYCCNT; 941 | cout << F("Read Time (ARM Cycle Delta): ") << endCNT - startCNT << endl; 942 | if (nr != BUF_SIZE) { 943 | Serial.println("read failed"); 944 | } 945 | m = micros() - m; 946 | totalLatency += m; 947 | if (bufA32[BUF_SIZE - 1] != '\n') { 948 | Serial.println("data check error"); 949 | } 950 | if (skipLatency) { 951 | skipLatency = false; 952 | } else { 953 | if (maxLatency < m) { 954 | maxLatency = m; 955 | } 956 | if (minLatency > m) { 957 | minLatency = m; 958 | } 959 | } 960 | } 961 | 962 | s = file.size(); 963 | 964 | 965 | t = micros() - t; 966 | cout << F("KB/Sec,usec,usec,usec") << endl; 967 | cout << s * 1000 / t << ',' << maxLatency << ',' << minLatency; 968 | cout << ',' << totalLatency / n << endl; 969 | } 970 | cout << endl << F("Done") << endl; 971 | 972 | file.close(); 973 | myfs.remove("bench.dat"); 974 | 975 | } 976 | -------------------------------------------------------------------------------- /examples/Test_Integrity/PROG/PROG.ino: -------------------------------------------------------------------------------- 1 | #include "LittleFS.h" 2 | 3 | /* NOTES on LittleFS and Integrity testa 4 | - Sketches are a Media specific subset of tests done during development 5 | - When the Media is properly connected, it allows repeated FS file access to fill and verify function 6 | - The goal then was to repeatedly Create files and then Add to the file, or Delete the file 7 | - This allowed verifying File and Directory data integrtty as the Media was filled and reused as the data moved 8 | - Other than for Media and device testing - the code provides examples of support for the commands shown. 9 | - It will also provide a rough estimate of data I/O speed for the Media at hand 10 | - There is a Case Sensitive command set active when run displayed on start or when '?' is entered 11 | - For the primary looping enter number 1-9, h, or k and 'c' for continuous. A running count will stop with '0' 12 | - 'd' for directory will walk the directory and verify against the expected number of files added/deleted 13 | - Many commands were added as LittleFS developed, early work has lots of verbose settings now TOGGLED off for speed 14 | - 15 | - Each named sketch works on a specific Media type as presented 16 | - there are various commands provided to demonstrate usage 17 | - All .size() functions return a 64 bit uint64_t - take care when printing 18 | */ 19 | 20 | // NOTE: This option is only available on the Teensy 4.0, Teensy 4.1 and Teensy Micromod boards. 21 | 22 | #define PROG_FLASH_SIZE 1024 * 1024 * 1 // Specify size to use of onboard Teensy Program Flash chip 23 | // This creates a LittleFS drive in Teensy PCB FLash. 24 | // This will persist until a new UPLOAD - Bootloader may change after TD 1.54 to prevent reformat. 25 | LittleFS_Program myfs; 26 | char szDiskMem[] = "PRO_DISK"; 27 | 28 | // Adjust these for amount of disk space used in iterations 29 | #define MAXNUM 5 // Number of files : ALPHA A-Z is MAX of 26, less for fewer files 30 | #define NUMDIRS 2 // Number of Directories to use 0 is Rootonly 31 | #define BIGADD 2024 // bytes added each pass - bigger will quickly consume more space 32 | #define SUBADD 512 // bytes added each pass (*times file number) 33 | #define MAXFILL 2048 // 66000 // ZERO to disable :: Prevent iterations from over filling - require this much free 34 | 35 | // These can likely be left unchanged 36 | #define MYBLKSIZE 2048 // 2048 37 | #define SLACK_SPACE 40960 // allow for overhead slack space :: WORKS on FLASH {some need more with small alloc units} 38 | uint32_t DELSTART = 3; // originally was 3 + higher bias more to writes and larger files - lower odd 39 | #define DELDELAY 0 // delay before DEL files : delayMicroseconds 40 | #define ADDDELAY 0 // delay on ADD FILE : delayMicroseconds 41 | 42 | // Various Globals 43 | const uint32_t lowOffset = 'a' - 'A'; 44 | const uint32_t lowShift = 13; 45 | uint32_t errsLFS = 0, warnLFS = 0; // Track warnings or Errors 46 | uint32_t lCnt = 0, LoopCnt = 0; // loop counters 47 | uint64_t rdCnt = 0, wrCnt = 0; // Track Bytes Read and Written 48 | int filecount = 0; 49 | int loopLimit = 0; // -1 continuous, otherwise # to count down to 0 50 | bool pauseDir = false; // Start Pause on each off 51 | bool showDir = false; // false Start Dir on each off 52 | bool bDirVerify = false; // false Start Dir on each off 53 | bool bWriteVerify = true; // Verify on Write Toggle 54 | bool bAutoFormat = false; // false Auto formatUnused() off 55 | bool bCheckFormat = false; // false CheckFormat 56 | bool bCheckUsed = false; // false CheckUsed 57 | uint32_t res = 0; // for formatUnused 58 | File file3; // Single FILE used for all functions 59 | 60 | void setup() { 61 | while (!Serial) ; // wait 62 | Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__); 63 | Serial.println("LittleFS Test : File Integrity"); delay(5); 64 | 65 | if (!myfs.begin(PROG_FLASH_SIZE)) { 66 | Serial.printf("Error starting %s\n", szDiskMem); 67 | while( 1 ); 68 | } 69 | filecount = printDirectoryFilecount( myfs.open("/") ); // Set base value of filecount for disk 70 | printDirectory(); 71 | parseCmd( '?' ); 72 | makeRootDirs(); 73 | checkInput( 1 ); 74 | filecount = printDirectoryFilecount( myfs.open("/") ); // Set base value of filecount for disk 75 | printDirectory(); 76 | } 77 | 78 | 79 | void loop() { 80 | char szDir[16]; 81 | LoopCnt++; 82 | uint32_t chStep; 83 | if ( loopLimit != 0 ) { 84 | for ( uint32_t ii = 0; ii <= NUMDIRS && ( loopLimit != 0 ); ii++ ) 85 | { 86 | if ( ii == 0 ) 87 | sprintf( szDir, "/" ); 88 | else 89 | sprintf( szDir, "/%lu_dir", ii ); 90 | chStep = fileCycle(szDir); 91 | if ( bAutoFormat && !(lCnt % 5) ) res = loopAutoFormat( 20, res ); 92 | while ( chStep != fileCycle(szDir) && ( loopLimit != 0 ) ) { 93 | if ( bAutoFormat && !(lCnt % 5) ) res = loopAutoFormat( 12, res ); // how often and how many depends on media and sizes 94 | checkInput( 0 ); // user input can 0 loopLimit 95 | } 96 | } 97 | checkInput( 0 ); 98 | if ( loopLimit > 0 ) // -1 means continuous 99 | loopLimit--; 100 | } 101 | else 102 | checkInput( 1 ); 103 | } 104 | -------------------------------------------------------------------------------- /examples/Test_Integrity/PSRAM/PSRAM.ino: -------------------------------------------------------------------------------- 1 | #include "LittleFS.h" 2 | 3 | /* NOTES on LittleFS and Integrity testa 4 | - Sketches are a Media specific subset of tests done during development 5 | - When the Media is properly connected, it allows repeated FS file access to fill and verify function 6 | - The goal then was to repeatedly Create files and then Add to the file, or Delete the file 7 | - This allowed verifying File and Directory data integrtty as the Media was filled and reused as the data moved 8 | - Other than for Media and device testing - the code provides examples of support for the commands shown. 9 | - It will also provide a rough estimate of data I/O speed for the Media at hand 10 | - There is a Case Sensitive command set active when run displayed on start or when '?' is entered 11 | - For the primary looping enter number 1-9, h, or k and 'c' for continuous. A running count will stop with '0' 12 | - 'd' for directory will walk the directory and verify against the expected number of files added/deleted 13 | - Many commands were added as LittleFS developed, early work has lots of verbose settings now TOGGLED off for speed 14 | - 15 | - Each named sketch works on a specific Media type as presented 16 | - there are various commands provided to demonstrate usage 17 | - All .size() functions return a 64 bit uint64_t - take care when printing 18 | */ 19 | 20 | // NOTE: This option is only available on the Teensy 4.1 board with added bottomside PSRAM chip(s) in place. 21 | 22 | // This declares the LittleFS Media type and gives a text name to Identify in use 23 | LittleFS_RAM myfs; 24 | #define MYPSRAM 8 // compile time PSRAM size and is T_4.1 specific either 8 or 16, or smaller portion 25 | EXTMEM char buf[MYPSRAM * 1024 * 1024]; 26 | char szDiskMem[] = "RAM_DISK"; 27 | 28 | // Adjust these for amount of disk space used in iterations 29 | #define MAXNUM 5 // Number of files : ALPHA A-Z is MAX of 26, less for fewer files 30 | #define NUMDIRS 5 // Number of Directories to use 0 is Rootonly 31 | #define BIGADD 2024 // bytes added each pass - bigger will quickly consume more space 32 | #define SUBADD 512 // bytes added each pass (*times file number) 33 | #define MAXFILL 2048 // 66000 // ZERO to disable :: Prevent iterations from over filling - require this much free 34 | 35 | // These can likely be left unchanged 36 | #define MYBLKSIZE 2048 // 2048 37 | #define SLACK_SPACE 40960 // allow for overhead slack space :: WORKS on FLASH {some need more with small alloc units} 38 | uint32_t DELSTART = 3; // originally was 3 + higher bias more to writes and larger files - lower odd 39 | #define DELDELAY 0 // delay before DEL files : delayMicroseconds 40 | #define ADDDELAY 0 // delay on ADD FILE : delayMicroseconds 41 | 42 | // Various Globals 43 | const uint32_t lowOffset = 'a' - 'A'; 44 | const uint32_t lowShift = 13; 45 | uint32_t errsLFS = 0, warnLFS = 0; // Track warnings or Errors 46 | uint32_t lCnt = 0, LoopCnt = 0; // loop counters 47 | uint64_t rdCnt = 0, wrCnt = 0; // Track Bytes Read and Written 48 | int filecount = 0; 49 | int loopLimit = 0; // -1 continuous, otherwise # to count down to 0 50 | bool pauseDir = false; // Start Pause on each off 51 | bool showDir = false; // false Start Dir on each off 52 | bool bDirVerify = false; // false Start Dir on each off 53 | bool bWriteVerify = true; // Verify on Write Toggle 54 | bool bAutoFormat = false; // false Auto formatUnused() off 55 | bool bCheckFormat = false; // false CheckFormat 56 | bool bCheckUsed = false; // false CheckUsed 57 | uint32_t res = 0; // for formatUnused 58 | File file3; // Single FILE used for all functions 59 | 60 | void setup() { 61 | while (!Serial) ; // wait 62 | Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__); 63 | Serial.println("LittleFS Test : File Integrity"); delay(5); 64 | 65 | if (!myfs.begin(buf, sizeof(buf))) { 66 | Serial.printf("Error starting %s\n", szDiskMem); 67 | while( 1 ); 68 | } 69 | // LittleFS_RAM is volatile (does not retain files) directory should always start empty 70 | filecount = printDirectoryFilecount( myfs.open("/") ); // Set base value of filecount for disk 71 | printDirectory(); 72 | parseCmd( '?' ); 73 | makeRootDirs(); 74 | checkInput( 1 ); 75 | filecount = printDirectoryFilecount( myfs.open("/") ); // Set base value of filecount for disk 76 | printDirectory(); 77 | } 78 | 79 | void loop() { 80 | char szDir[16]; 81 | LoopCnt++; 82 | uint32_t chStep; 83 | if ( loopLimit != 0 ) { 84 | for ( uint32_t ii = 0; ii <= NUMDIRS && ( loopLimit != 0 ); ii++ ) 85 | { 86 | if ( ii == 0 ) 87 | sprintf( szDir, "/" ); 88 | else 89 | sprintf( szDir, "/%lu_dir", ii ); 90 | chStep = fileCycle(szDir); 91 | if ( bAutoFormat && !(lCnt % 5) ) res = loopAutoFormat( 20, res ); 92 | while ( chStep != fileCycle(szDir) && ( loopLimit != 0 ) ) { 93 | if ( bAutoFormat && !(lCnt % 5) ) res = loopAutoFormat( 12, res ); // how often and how many depends on media and sizes 94 | checkInput( 0 ); // user input can 0 loopLimit 95 | } 96 | } 97 | checkInput( 0 ); 98 | if ( loopLimit > 0 ) // -1 means continuous 99 | loopLimit--; 100 | } 101 | else 102 | checkInput( 1 ); 103 | } 104 | -------------------------------------------------------------------------------- /examples/Test_Integrity/QSPI/QSPI.ino: -------------------------------------------------------------------------------- 1 | #include "LittleFS.h" 2 | 3 | /* NOTES on LittleFS and Integrity testa 4 | - Sketches are a Media specific subset of tests done during development 5 | - When the Media is properly connected, it allows repeated FS file access to fill and verify function 6 | - The goal then was to repeatedly Create files and then Add to the file, or Delete the file 7 | - This allowed verifying File and Directory data integrtty as the Media was filled and reused as the data moved 8 | - Other than for Media and device testing - the code provides examples of support for the commands shown. 9 | - It will also provide a rough estimate of data I/O speed for the Media at hand 10 | - There is a Case Sensitive command set active when run displayed on start or when '?' is entered 11 | - For the primary looping enter number 1-9, h, or k and 'c' for continuous. A running count will stop with '0' 12 | - 'd' for directory will walk the directory and verify against the expected number of files added/deleted 13 | - Many commands were added as LittleFS developed, early work has lots of verbose settings now TOGGLED off for speed 14 | - 15 | - Each named sketch works on a specific Media type as presented 16 | - there are various commands provided to demonstrate usage 17 | - All .size() functions return a 64 bit uint64_t - take care when printing 18 | */ 19 | 20 | // NOTE: This option is only available on the Teensy 4.1 board with added bottomside chip in place. 21 | 22 | /* \/ == Uncomment ONE of the TWO lines to select SPI FLASH type: NOR or NAND == \/ */ 23 | 24 | #define TEST_QSPI // Typical NOR FLASH 25 | //#define TEST_QSPI_NAND // NAND Flash 26 | 27 | /* /\ == Uncomment ONE of the TWO lines to select SPI FLASH type: NOR or NAND == /\ */ 28 | 29 | // This declares the LittleFS Media type and gives a text name to Identify in use 30 | #if defined TEST_QSPI 31 | LittleFS_QSPIFlash myfs; 32 | char szDiskMem[] = "QSPI_DISK"; 33 | #elif defined(TEST_QSPI_NAND) 34 | char szDiskMem[] = "QSPI_NAND"; 35 | LittleFS_QPINAND myfs; 36 | #endif 37 | 38 | // Adjust these for amount of disk space used in iterations 39 | #define MAXNUM 5 // Number of files : ALPHA A-Z is MAX of 26, less for fewer files 40 | #define NUMDIRS 5 // Number of Directories to use 0 is Rootonly 41 | #define BIGADD 2024 // bytes added each pass - bigger will quickly consume more space 42 | #define SUBADD 512 // bytes added each pass (*times file number) 43 | #define MAXFILL 2048 // 66000 // ZERO to disable :: Prevent iterations from over filling - require this much free 44 | 45 | // These can likely be left unchanged 46 | #define MYBLKSIZE 2048 // 2048 47 | #define SLACK_SPACE 40960 // allow for overhead slack space :: WORKS on FLASH {some need more with small alloc units} 48 | uint32_t DELSTART = 3; // originally was 3 + higher bias more to writes and larger files - lower odd 49 | #define DELDELAY 0 // delay before DEL files : delayMicroseconds 50 | #define ADDDELAY 0 // delay on ADD FILE : delayMicroseconds 51 | 52 | // Various Globals 53 | const uint32_t lowOffset = 'a' - 'A'; 54 | const uint32_t lowShift = 13; 55 | uint32_t errsLFS = 0, warnLFS = 0; // Track warnings or Errors 56 | uint32_t lCnt = 0, LoopCnt = 0; // loop counters 57 | uint64_t rdCnt = 0, wrCnt = 0; // Track Bytes Read and Written 58 | int filecount = 0; 59 | int loopLimit = 0; // -1 continuous, otherwise # to count down to 0 60 | bool pauseDir = false; // Start Pause on each off 61 | bool showDir = false; // false Start Dir on each off 62 | bool bDirVerify = false; // false Start Dir on each off 63 | bool bWriteVerify = true; // Verify on Write Toggle 64 | bool bAutoFormat = false; // false Auto formatUnused() off 65 | bool bCheckFormat = false; // false CheckFormat 66 | bool bCheckUsed = false; // false CheckUsed 67 | uint32_t res = 0; // for formatUnused 68 | File file3; // Single FILE used for all functions 69 | 70 | void setup() { 71 | while (!Serial) ; // wait 72 | Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__); 73 | Serial.println("LittleFS Test : File Integrity"); delay(5); 74 | 75 | if (!myfs.begin()) { 76 | Serial.printf("Error starting %s\n", szDiskMem); 77 | while( 1 ); 78 | } 79 | filecount = printDirectoryFilecount( myfs.open("/") ); // Set base value of filecount for disk 80 | printDirectory(); 81 | parseCmd( '?' ); 82 | makeRootDirs(); 83 | checkInput( 1 ); 84 | filecount = printDirectoryFilecount( myfs.open("/") ); // Set base value of filecount for disk 85 | printDirectory(); 86 | } 87 | 88 | void loop() { 89 | char szDir[16]; 90 | LoopCnt++; 91 | uint32_t chStep; 92 | if ( loopLimit != 0 ) { 93 | for ( uint32_t ii = 0; ii <= NUMDIRS && ( loopLimit != 0 ); ii++ ) 94 | { 95 | if ( ii == 0 ) 96 | sprintf( szDir, "/" ); 97 | else 98 | sprintf( szDir, "/%lu_dir", ii ); 99 | chStep = fileCycle(szDir); 100 | if ( bAutoFormat && !(lCnt % 5) ) res = loopAutoFormat( 20, res ); 101 | while ( chStep != fileCycle(szDir) && ( loopLimit != 0 ) ) { 102 | if ( bAutoFormat && !(lCnt % 5) ) res = loopAutoFormat( 12, res ); // how often and how many depends on media and sizes 103 | checkInput( 0 ); // user input can 0 loopLimit 104 | } 105 | } 106 | checkInput( 0 ); 107 | if ( loopLimit > 0 ) // -1 means continuous 108 | loopLimit--; 109 | } 110 | else 111 | checkInput( 1 ); 112 | } 113 | -------------------------------------------------------------------------------- /examples/Test_Integrity/RAM/RAM.ino: -------------------------------------------------------------------------------- 1 | #include "LittleFS.h" 2 | 3 | /* NOTES on LittleFS and Integrity testa 4 | - Sketches are a Media specific subset of tests done during development 5 | - When the Media is properly connected, it allows repeated FS file access to fill and verify function 6 | - The goal then was to repeatedly Create files and then Add to the file, or Delete the file 7 | - This allowed verifying File and Directory data integrtty as the Media was filled and reused as the data moved 8 | - Other than for Media and device testing - the code provides examples of support for the commands shown. 9 | - It will also provide a rough estimate of data I/O speed for the Media at hand 10 | - There is a Case Sensitive command set active when run displayed on start or when '?' is entered 11 | - For the primary looping enter number 1-9, h, or k and 'c' for continuous. A running count will stop with '0' 12 | - 'd' for directory will walk the directory and verify against the expected number of files added/deleted 13 | - Many commands were added as LittleFS developed, early work has lots of verbose settings now TOGGLED off for speed 14 | - 15 | - Each named sketch works on a specific Media type as presented 16 | - there are various commands provided to demonstrate usage 17 | - All .size() functions return a 64 bit uint64_t - take care when printing 18 | */ 19 | 20 | // This declares the LittleFS Media type and gives a text name to Identify in use 21 | LittleFS_RAM myfs; // CAN use either RAM1 or RAM2 as available 22 | #ifdef __IMXRT1062__ // Teensy 4.0, 4.1 and T_MicroMod 23 | char buf[ 390 * 1024 ]; // BUFFER in RAM1 24 | //DMAMEM char buf[ 390 * 1024 ]; // DMAMEM Uses RAM2 25 | #define NUMDIRS 4 // Number of Directories to use 0 is Rootonly 26 | #define BIGADD 640 // bytes added each pass - bigger will quickly consume more space 27 | #define SUBADD 512 // bytes added each pass (*times file number) 28 | #else // Teensy 3.5 and 3.6 have 256KB of RAM shared with user data 29 | char buf[ 120 * 1024 ]; // BUFFER in RAM 30 | #define NUMDIRS 2 // Number of Directories to use 0 is Rootonly 31 | #define BIGADD 320 // bytes added each pass - bigger will quickly consume more space 32 | #define SUBADD 256 // bytes added each pass (*times file number) 33 | #endif 34 | 35 | char szDiskMem[] = "RAM_DISK"; 36 | 37 | // Adjust these for amount of disk space used in iterations 38 | #define MAXNUM 5 // Number of files : ALPHA A-Z is MAX of 26, less for fewer files 39 | #define MAXFILL 10000 // 66000 // ZERO to disable :: Prevent iterations from over filling - require this much free 40 | 41 | // These can likely be left unchanged 42 | #define MYBLKSIZE 2048 // 2048 43 | #define SLACK_SPACE 40960 // allow for overhead slack space :: WORKS on FLASH {some need more with small alloc units} 44 | uint32_t DELSTART = 3; // originally was 3 + higher bias more to writes and larger files - lower odd 45 | #define DELDELAY 0 // delay before DEL files : delayMicroseconds 46 | #define ADDDELAY 0 // delay on ADD FILE : delayMicroseconds 47 | 48 | // Various Globals 49 | const uint32_t lowOffset = 'a' - 'A'; 50 | const uint32_t lowShift = 13; 51 | uint32_t errsLFS = 0, warnLFS = 0; // Track warnings or Errors 52 | uint32_t lCnt = 0, LoopCnt = 0; // loop counters 53 | uint64_t rdCnt = 0, wrCnt = 0; // Track Bytes Read and Written 54 | int filecount = 0; 55 | int loopLimit = 0; // -1 continuous, otherwise # to count down to 0 56 | bool pauseDir = false; // Start Pause on each off 57 | bool showDir = false; // false Start Dir on each off 58 | bool bDirVerify = false; // false Start Dir on each off 59 | bool bWriteVerify = true; // Verify on Write Toggle 60 | bool bAutoFormat = false; // false Auto formatUnused() off 61 | bool bCheckFormat = false; // false CheckFormat 62 | bool bCheckUsed = false; // false CheckUsed 63 | uint32_t res = 0; // for formatUnused 64 | File file3; // Single FILE used for all functions 65 | 66 | void setup() { 67 | while (!Serial) ; // wait 68 | Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__); 69 | Serial.println("LittleFS Test : File Integrity"); delay(5); 70 | 71 | if (!myfs.begin(buf, sizeof(buf))) { 72 | Serial.printf("Error starting %s\n", szDiskMem); 73 | while( 1 ); 74 | } 75 | // LittleFS_RAM is volatile (does not retain files) directory should always start empty 76 | filecount = printDirectoryFilecount( myfs.open("/") ); // Set base value of filecount for disk 77 | printDirectory(); 78 | parseCmd( '?' ); 79 | makeRootDirs(); 80 | checkInput( 1 ); 81 | filecount = printDirectoryFilecount( myfs.open("/") ); // Set base value of filecount for disk 82 | printDirectory(); 83 | } 84 | 85 | void loop() { 86 | char szDir[16]; 87 | LoopCnt++; 88 | uint32_t chStep; 89 | if ( loopLimit != 0 ) { 90 | for ( uint32_t ii = 0; ii <= NUMDIRS && ( loopLimit != 0 ); ii++ ) 91 | { 92 | if ( ii == 0 ) 93 | sprintf( szDir, "/" ); 94 | else 95 | sprintf( szDir, "/%lu_dir", ii ); 96 | chStep = fileCycle(szDir); 97 | if ( bAutoFormat && !(lCnt % 5) ) res = loopAutoFormat( 20, res ); 98 | while ( chStep != fileCycle(szDir) && ( loopLimit != 0 ) ) { 99 | if ( bAutoFormat && !(lCnt % 5) ) res = loopAutoFormat( 12, res ); // how often and how many depends on media and sizes 100 | checkInput( 0 ); // user input can 0 loopLimit 101 | } 102 | } 103 | checkInput( 0 ); 104 | if ( loopLimit > 0 ) // -1 means continuous 105 | loopLimit--; 106 | } 107 | else 108 | checkInput( 1 ); 109 | } 110 | -------------------------------------------------------------------------------- /examples/Test_Integrity/RAM/functions.ino: -------------------------------------------------------------------------------- 1 | 2 | char szInputs[] = "0123456789RdDwcghkFqvplmusSBbyYxfan+-?"; 3 | uint32_t lastTime; 4 | void checkInput( int step ) { // prompt for input without user input with step != 0 5 | uint32_t nowTime = micros(); 6 | 7 | char retVal = 0, temp; 8 | char *pTemp; 9 | if ( step != 0 ) { 10 | nowTime -= lastTime; 11 | Serial.printf( "[%6.2f M](%6.5f M elap) Awaiting input %s loops left %d >", millis() / 60000.0, nowTime / 60000000.0, szInputs, loopLimit ); 12 | } 13 | else { 14 | if ( !Serial.available() ) return; 15 | nowTime -= lastTime; 16 | Serial.printf( "[%6.2f M](%6.2f elap) Awaiting input %s loops left %d >", millis() / 60000.0, nowTime / 60000000.0, szInputs, loopLimit ); 17 | //Serial.printf( "[%6.2f] Awaiting input %s loops left %d >", millis() / 60000.0, szInputs, loopLimit ); 18 | while ( Serial.available() ) { 19 | temp = Serial.read( ); 20 | if ( (pTemp = strchr(szInputs, temp)) ) { 21 | retVal = pTemp[0]; 22 | parseCmd( retVal ); 23 | } 24 | } 25 | } 26 | while ( !Serial.available() ); 27 | while ( Serial.available() ) { 28 | temp = Serial.read(); 29 | if ( (pTemp = strchr(szInputs, temp)) ) { 30 | retVal = pTemp[0]; 31 | parseCmd( retVal ); 32 | } 33 | } 34 | Serial.print( '\n' ); 35 | if ( '?' == retVal ) checkInput( 1 ); // recurse on '?' to allow command show and response 36 | return; 37 | } 38 | void parseCmd( char chIn ) { // pass chIn == '?' for help 39 | uint32_t timeMe; 40 | char szNone[] = ""; 41 | switch (chIn ) { 42 | case '?': 43 | Serial.printf( "\n%s\n", " 1-9 '#' passes continue Loop before Pause\n\ 44 | 'c, or 0': Continuous Loop, or stop Loop in progress\n\ 45 | 'h, or k': start Hundred or Thousand Loops\n\ 46 | 'd' Directory of LittleFS Media\n\ 47 | 'D' Walk all Dir Files verify Read Size\n\ 48 | 'l' Show count of loop()'s, Bytes Read,Written\n\ 49 | 'B, or b': Make Big file half of free space, or remove all Big files\n\ 50 | 'S, or s': Make 2MB file , or remove all 2MB files\n\ 51 | \t>> Media Format : 'q' and 'F' remove ALL FS data\n\ 52 | 'q' Quick Format Disk \n\ 53 | 'F' Low Level Format Disk \n\ 54 | 'w' WIPE ALL Directories and Files\n\ 55 | 'm' Make ROOT dirs (needed after q/F format or 'w')\n\ 56 | 'f' LittleFS::formatUnused( ALL ) : DATA PRESERVED, empty blocks preformatted \n\ 57 | 'a' Auto formatUnused() during Loop - TOGGLE\n\ 58 | 'y' reclaim 1 block :: myfs.formatUnused( 1 )\n\ 59 | 'Y' reclaim 15 blocks :: myfs.formatUnused( 15 )\n\ 60 | \t>> Test Features \n\ 61 | 'g' run SpeedBench() Media speed benchmark\n\ 62 | 'x' Directory filecount verify - TOGGLE\n\ 63 | 'v' Verbose All Dir Prints - TOGGLE\n\ 64 | 'p' Pause after all Dir prints - TOGGLE\n\ 65 | 'n' No verify on Write- TOGGLE\n\ 66 | 'u' Update Filecount\n\ 67 | 'R' Restart Teensy - Except 'RAM' - data persists\n\ 68 | '+, or -': more, or less add .vs. delete in Loop\n\ 69 | '?' Help list : A Loop will Create, Extend, or Delete files to verify Integrity" ); 70 | break; 71 | case 'R': 72 | Serial.print(" RESTART Teensy ..."); 73 | delay(100); 74 | SCB_AIRCR = 0x05FA0004; 75 | break; 76 | case '0': 77 | case '1': 78 | case '2': 79 | case '3': 80 | case '4': 81 | case '5': 82 | case '6': 83 | case '7': 84 | case '8': 85 | case '9': 86 | loopLimit = chIn - '0'; 87 | if ( chIn != '0' ) // Preserve elapsed time on Error or STOP command 88 | lastTime = micros(); 89 | break; 90 | case 'b': 91 | bigFile( 0 ); // delete 92 | chIn = 0; 93 | break; 94 | case 'B': 95 | lastTime = micros(); 96 | bigFile( 1 ); // CREATE 97 | chIn = 0; 98 | break; 99 | case 's': 100 | bigFile2MB( 0 ); // CREATE 101 | chIn = 0; 102 | break; 103 | case 'S': 104 | lastTime = micros(); 105 | bigFile2MB( 1 ); // CREATE 106 | chIn = 0; 107 | break; 108 | case 'c': 109 | loopLimit = -1; 110 | break; 111 | case 'g': 112 | lastTime = micros(); 113 | speedBench(); 114 | chIn = 0; 115 | break; 116 | case 'd': 117 | Serial.print( " d\n" ); 118 | lastTime = micros(); 119 | printDirectory(); 120 | Serial.print( '\n' ); 121 | parseCmd( 'l' ); 122 | checkInput( 1 ); 123 | chIn = 0; 124 | break; 125 | case 'D': 126 | Serial.println("\nWalk all Files verify Read Size:\t"); 127 | lastTime = micros(); 128 | Serial.printf( "\t%u Errors found\n" , DirectoryVerify( myfs.open("/") ) ); 129 | Serial.printf("\nBytes Used: %llu, Bytes Total:%llu\n", myfs.usedSize(), myfs.totalSize()); 130 | parseCmd( 'l' ); 131 | checkInput( 1 ); 132 | chIn = 0; 133 | break; 134 | case 'w': 135 | Serial.println("\nWipe All Files and DIRS:"); 136 | deleteAllDirectory(myfs.open("/"), szNone ); 137 | errsLFS = 0; // No Errors on new Format 138 | warnLFS = 0; // No warning on new Format 139 | chIn = 0; 140 | parseCmd( 'u' ); 141 | break; 142 | case 'h': 143 | loopLimit = 100; 144 | lastTime = micros(); 145 | break; 146 | case 'k': 147 | loopLimit = 1000; 148 | lastTime = micros(); 149 | break; 150 | case 'F': // Low Level format 151 | Serial.print( "\nFormatting Low Level:\n\t" ); 152 | lastTime = micros(); 153 | timeMe = micros(); 154 | myfs.lowLevelFormat('.'); 155 | timeMe = micros() - timeMe; 156 | Serial.printf( "\n Done Formatting Low Level in %lu us.\n", timeMe ); 157 | errsLFS = 0; // No Errors on new Format 158 | warnLFS = 0; // No warning on new Format 159 | bCheckFormat = false; 160 | parseCmd( 'u' ); 161 | break; 162 | case 'q': // quick format 163 | lastTime = micros(); 164 | myfs.quickFormat(); 165 | errsLFS = 0; // No Errors on new Format 166 | parseCmd( 'u' ); 167 | break; 168 | case 'v': // verbose dir 169 | showDir = !showDir; 170 | showDir ? Serial.print(" Verbose on: ") : Serial.print(" Verbose off: "); 171 | chIn = 0; 172 | break; 173 | case 'p': // pause on dirs 174 | pauseDir = !pauseDir; 175 | pauseDir ? Serial.print(" Pause on: ") : Serial.print(" Pause off: "); 176 | chIn = 0; 177 | break; 178 | case 'x': // dir filecount Verify 179 | bDirVerify = !bDirVerify; 180 | bDirVerify ? Serial.print(" FileCnt on: ") : Serial.print(" FileCnt off: "); 181 | lastTime = micros(); 182 | dirVerify(); 183 | chIn = 0; 184 | break; 185 | case 'n': // No Verify on write 186 | bWriteVerify = !bWriteVerify; 187 | bWriteVerify ? Serial.print(" Write Verify on: ") : Serial.print(" Write Verify off: "); 188 | chIn = 0; 189 | break; 190 | case 'a': // Auto myfs.formatUnused() during iterations 191 | bAutoFormat = !bAutoFormat; 192 | bAutoFormat ? Serial.print(" \nAuto formatUnused() On: ") : Serial.print(" \nAuto formatUnused() Off: "); 193 | chIn = 0; 194 | break; 195 | case 'y': // Reclaim 1 unused format 196 | lastTime = micros(); 197 | Serial.printf( "\n myfs.formatUnused( 1 ) ...\n" ); 198 | timeMe = micros(); 199 | res = myfs.formatUnused( 1, res ); 200 | timeMe = micros() - timeMe; 201 | Serial.printf( "\n\t formatUnused :: Done Formatting Low Level in %lu us (last %lu).\n", timeMe, res ); 202 | chIn = 0; 203 | break; 204 | case 'Y': // Reclaim 15 unused format 205 | lastTime = micros(); 206 | Serial.printf( "\n myfs.formatUnused( 15 ) ...\n" ); 207 | timeMe = micros(); 208 | res = myfs.formatUnused( 15, res ); 209 | timeMe = micros() - timeMe; 210 | Serial.printf( "\n\t formatUnused :: Done Formatting Low Level in %lu us (last %lu).\n", timeMe, res ); 211 | chIn = 0; 212 | break; 213 | case 'f': // Reclaim all unused format 214 | lastTime = micros(); 215 | Serial.printf( "\n myfs.formatUnused( 0 ) ...\n" ); 216 | timeMe = micros(); 217 | myfs.formatUnused( 0, 0 ); 218 | timeMe = micros() - timeMe; 219 | Serial.printf( "\n\t formatUnused :: Done Formatting Low Level in %lu us.\n", timeMe ); 220 | chIn = 0; 221 | break; 222 | case 'l': // Show Loop Count 223 | Serial.printf("\n\t Loop Count: %u (#fileCycle=%u), Bytes read %llu, written %llu, #Files=%u\n", LoopCnt, lCnt, rdCnt, wrCnt, filecount ); 224 | if ( 0 != errsLFS ) 225 | Serial.printf("\t ERROR COUNT =%u\n", errsLFS ); 226 | if ( 0 != warnLFS ) 227 | Serial.printf("\t Free Space Warn COUNT =%u\n", warnLFS ); 228 | dirVerify(); 229 | chIn = 0; 230 | break; 231 | case 'm': 232 | Serial.printf("m \n\t Making Root Dirs\n" ); 233 | makeRootDirs(); 234 | parseCmd( 'd' ); 235 | chIn = 0; 236 | break; 237 | case 'u': // Show Loop Count 238 | filecount = printDirectoryFilecount( myfs.open("/") ); 239 | Serial.printf("\n\t Updated filecount %u\n", filecount ); 240 | chIn = 0; 241 | break; 242 | case '+': // increase add cycles 243 | DELSTART++; 244 | Serial.printf("+\n Deletes start after %u cycles ", DELSTART); 245 | chIn = 0; 246 | break; 247 | case '-': // decrease add cycles 248 | DELSTART--; 249 | if ( DELSTART < 1 ) DELSTART = 1; 250 | Serial.printf("-\n Deletes start after %u cycles ", DELSTART); 251 | chIn = 0; 252 | break; 253 | default: 254 | Serial.println( chIn ); // never see without unhandled char in szInputs[] 255 | break; 256 | } 257 | if ( 0 != chIn ) Serial.print( chIn ); 258 | } 259 | 260 | uint32_t loopAutoFormat( uint32_t cnt, uint32_t myres ) { 261 | uint32_t retres; 262 | retres = myfs.formatUnused( cnt, myres ); 263 | Serial.printf("\t fmtU @ %lu - %lu \n", myres, retres ); 264 | return retres; 265 | } 266 | 267 | uint32_t fTot, totSize; 268 | void printDirectory() { 269 | fTot = 0, totSize = 0; 270 | Serial.printf("printDirectory %s\n--------------\n", szDiskMem); 271 | printDirectory(myfs.open("/"), 0); 272 | Serial.printf(" %Total %u files of Size %u Bytes\n", fTot, totSize); 273 | Serial.printf("Bytes Used: %llu, Bytes Total:%llu\n", myfs.usedSize(), myfs.totalSize()); 274 | if ( myfs.usedSize() == myfs.totalSize() ) { 275 | Serial.printf("\n\n\tWARNING: DISK FULL >>>>> Bytes Used: %llu, Bytes Total:%llu\n\n", myfs.usedSize(), myfs.totalSize()); 276 | warnLFS++; 277 | } 278 | } 279 | 280 | void deleteAllDirectory(File dir, char *fullPath ) { 281 | char myPath[ 256 ] = ""; 282 | while (true) { 283 | File entry = dir.openNextFile(); 284 | if (! entry) { 285 | // no more files 286 | break; 287 | } 288 | if (entry.isDirectory()) { 289 | strcpy( myPath, fullPath); 290 | strcat( myPath, entry.name()); 291 | strcat( myPath, "/"); 292 | deleteAllDirectory(entry, myPath); 293 | entry.close(); 294 | if ( !myfs.remove( myPath ) ) 295 | Serial.print( " Fail remove DIR>\t"); 296 | else 297 | Serial.print( " Removed DIR>\t"); 298 | Serial.println( myPath ); 299 | 300 | } else { 301 | strcpy( myPath, fullPath); 302 | strcat( myPath, entry.name()); 303 | entry.close(); 304 | if ( !myfs.remove( myPath ) ) 305 | Serial.print( "\tFail remove>\t"); 306 | else 307 | Serial.print( "\tRemoved>\t"); 308 | Serial.println( myPath ); 309 | } 310 | } 311 | } 312 | 313 | int printDirectoryFilecount(File dir) { 314 | unsigned int filecnt = 0; 315 | while (true) { 316 | File entry = dir.openNextFile(); 317 | if (! entry) { 318 | // no more files 319 | break; 320 | } 321 | if (entry.isDirectory()) { 322 | filecnt += printDirectoryFilecount(entry); 323 | } else { 324 | filecnt++; 325 | } 326 | entry.close(); 327 | } 328 | return filecnt; 329 | } 330 | 331 | void printDirectory(File dir, int numTabs) { 332 | uint64_t fSize = 0; 333 | uint32_t dCnt = 0, fCnt = 0; 334 | if ( 0 == dir ) { 335 | Serial.printf( "\t>>>\t>>>>> No Dir\n" ); 336 | return; 337 | } 338 | while (true) { 339 | File entry = dir.openNextFile(); 340 | if (! entry) { 341 | // no more files 342 | Serial.printf("\n %u dirs with %u files of Size %llu Bytes\n", dCnt, fCnt, fSize); 343 | fTot += fCnt; 344 | totSize += fSize; 345 | break; 346 | } 347 | for (uint8_t i = 0; i < numTabs; i++) { 348 | Serial.print('\t'); 349 | } 350 | 351 | if (entry.isDirectory()) { 352 | Serial.print("DIR\t"); 353 | dCnt++; 354 | } else { 355 | Serial.print("FILE\t"); 356 | fCnt++; 357 | fSize += entry.size(); 358 | } 359 | Serial.print(entry.name()); 360 | if (entry.isDirectory()) { 361 | Serial.println(" / "); 362 | printDirectory(entry, numTabs + 1); 363 | } else { 364 | // files have sizes, directories do not 365 | Serial.print("\t\t"); 366 | Serial.println(entry.size(), DEC); 367 | } 368 | entry.close(); 369 | //Serial.flush(); 370 | } 371 | } 372 | 373 | int DirectoryVerify(File dir) { 374 | int errCnt = 0; 375 | while (true) { 376 | File entry = dir.openNextFile(); 377 | if (! entry) { 378 | break; 379 | } 380 | if (entry.isDirectory()) { 381 | Serial.print("\tD"); 382 | Serial.print(entry.name()[0]); 383 | Serial.print(" "); 384 | errCnt += DirectoryVerify(entry); 385 | Serial.print("\n"); 386 | } else { 387 | uint64_t fileSize, sizeCnt = 0; 388 | char mm; 389 | Serial.print(entry.name()[0]); 390 | fileSize = entry.size(); 391 | while ( entry.available() ) { 392 | if (fileSize < sizeCnt ) break; 393 | entry.read( &mm , 1 ); 394 | sizeCnt++; 395 | } 396 | if (fileSize != sizeCnt ) { 397 | Serial.printf("\n File Size Error:: %s found %llu Bytes for Size %llu \n", entry.name(), sizeCnt, fileSize); 398 | errCnt++; 399 | errsLFS++; 400 | } 401 | } 402 | entry.close(); 403 | } 404 | return errCnt; 405 | } 406 | 407 | uint32_t cCnt = 0; 408 | uint32_t fileCycle(const char *dir) { 409 | static char szFile[] = "_file.txt"; 410 | char szPath[150]; 411 | int ii; 412 | lCnt++; 413 | byte nNum = lCnt % MAXNUM; 414 | char chNow = 'A' + lCnt % MAXNUM; 415 | lfs_ssize_t resW = 1; 416 | uint32_t timeMeAll = micros(); 417 | 418 | if ( dir[1] == 0 ) // catch root 419 | sprintf( szPath, "/%c%s", chNow, szFile ); 420 | else 421 | sprintf( szPath, "%s/%c%s", dir, chNow, szFile ); 422 | if ( cCnt >= DELSTART && myfs.exists(szPath) ) { // DELETE ALL KNOWN FILES 423 | if ( nNum == 1 ) { 424 | Serial.print( "\n == == == DELETE PASS START == == == = \n"); 425 | if ( showDir ) { 426 | printDirectory(); 427 | Serial.print( " == == == DELETE PASS START == == == = \n"); 428 | } 429 | delayMicroseconds(DELDELAY); 430 | } 431 | } 432 | Serial.printf( ":: %s ", szPath ); 433 | if ( cCnt >= DELSTART && myfs.exists(szPath) ) { // DELETE ALL KNOWN FILES 434 | readVerify( szPath, chNow ); 435 | myfs.remove(szPath); 436 | filecount--; 437 | Serial.printf(" ----DEL----"); 438 | Serial.printf(" -- %c", chNow); 439 | if ( showDir ) { 440 | Serial.print("\n"); 441 | printDirectory(myfs.open(dir), 1); 442 | } 443 | if ( pauseDir ) checkInput( 1 ); 444 | Serial.println(); 445 | } 446 | else { 447 | if ( bWriteVerify && ( myfs.totalSize() - myfs.usedSize() ) < MAXFILL ) { 448 | warnLFS++; 449 | Serial.printf( "\tXXX\tXXX\tXXX\tXXX\tSIZE WARNING { MAXFILL } \n" ); 450 | cCnt = DELSTART; 451 | return cCnt; // EARLY EXIT 452 | } 453 | if ( nNum == 0 ) { 454 | nNum = 10; 455 | cCnt++; 456 | if ( cCnt >= DELSTART + 2 ) cCnt = 0; 457 | } 458 | file3 = myfs.open(szPath, FILE_WRITE); 459 | if ( 0 == file3 ) { 460 | Serial.printf( "\tXXX\tXXX\tXXX\tXXX\tFail File open {mkdir?}\n" ); 461 | delayMicroseconds(300000); 462 | checkInput( 1 ); // PAUSE on CmdLine 463 | } 464 | else { 465 | delayMicroseconds(ADDDELAY); 466 | char mm = chNow + lowOffset; 467 | uint64_t jj = file3.size() + 1; 468 | uint32_t timeMe = micros(); 469 | int32_t nn = nNum * SUBADD + BIGADD; 470 | for ( ii = 0; ii < nn && resW >= 0; ii++ ) { 471 | if ( 0 == ((ii + jj) / lowShift) % 2 ) 472 | resW = file3.write( &mm , 1 ); 473 | else 474 | resW = file3.write( &chNow , 1 ); 475 | wrCnt++; 476 | // if ( lCnt%100 == 50 ) mm='x'; // GENERATE ERROR to detect on DELETE read verify 477 | } 478 | file3.close(); 479 | timeMe = micros() - timeMe; 480 | timeMeAll = micros() - timeMeAll; 481 | Serial.printf(" +++ size %llu: Add %d @KB/sec %5.2f ", jj - 1, nn, ii / (timeMeAll / 1000.0) ); 482 | if (resW < 0) { 483 | Serial.printf( "\n\twrite fail ERR# %i 0x%X \n", resW, resW ); 484 | parseCmd( '0' ); 485 | errsLFS++; 486 | checkInput( 1 ); // PAUSE on CmdLine 487 | } 488 | else if ( jj == 1 ) filecount++; // File Added 489 | Serial.print("\t"); 490 | if ( bWriteVerify ) 491 | readVerify( szPath, chNow ); 492 | Serial.print('\n'); 493 | if ( showDir ) { 494 | Serial.print('\n'); 495 | printDirectory(myfs.open(dir), 1); 496 | } 497 | } 498 | if ( pauseDir ) checkInput( 1 ); 499 | //Serial.print("\n"); 500 | delayMicroseconds(ADDDELAY); 501 | } 502 | checkInput( 0 ); // user stop request? 503 | if ( bDirVerify ) dirVerify(); 504 | return cCnt; 505 | } 506 | 507 | void dirVerify() { 508 | uint32_t timeMe = micros(); 509 | Serial.printf("\tdirV..."); 510 | if ( filecount != printDirectoryFilecount( myfs.open("/") ) ) { 511 | Serial.printf( "\tFilecount mismatch %u != %u\n", filecount, printDirectoryFilecount( myfs.open("/") ) ); 512 | parseCmd( '0' ); 513 | errsLFS++; 514 | checkInput( 1 ); // PAUSE on CmdLine 515 | } 516 | timeMe = micros() - timeMe; 517 | Serial.printf("%lu_us\t", timeMe); 518 | } 519 | 520 | void readVerify( char szPath[], char chNow ) { 521 | uint32_t timeMe = micros(); 522 | file3 = myfs.open(szPath); 523 | if ( 0 == file3 ) { 524 | Serial.printf( "\tV\t Fail File open %s\n", szPath ); 525 | parseCmd( '0' ); 526 | errsLFS++; 527 | checkInput( 1 ); 528 | } 529 | char mm; 530 | char chNow2 = chNow + lowOffset; 531 | uint32_t ii = 0; 532 | while ( file3.available() ) { 533 | file3.read( &mm , 1 ); 534 | rdCnt++; 535 | //Serial.print( mm ); // show chars as read 536 | ii++; 537 | if ( 0 == (ii / lowShift) % 2 ) { 538 | if ( chNow2 != mm ) { 539 | Serial.printf( " fSize ) { // catch over length return makes bad loop !!! 593 | Serial.printf( "\n\tFile LEN Corrupt! FS returning over %u bytes\n", fSize ); 594 | parseCmd( '0' ); 595 | errsLFS++; 596 | checkInput( 1 ); 597 | break; 598 | } 599 | } 600 | if (ii != file3.size()) { 601 | Serial.printf( "\n\tRead Count fail! :: read %u != f.size %llu\n", ii, file3.size() ); 602 | parseCmd( '0' ); 603 | errsLFS++; 604 | checkInput( 1 ); // PAUSE on CmdLine 605 | } 606 | else 607 | Serial.printf( "\tGOOD! >> bytes %lu", ii ); 608 | file3.close(); 609 | timeMe = micros() - timeMe; 610 | Serial.printf( "\n\tBig read&compare KBytes per second %5.2f \n", ii / (timeMe / 1000.0) ); 611 | if ( 0 == ii ) return false; 612 | return true; 613 | } 614 | 615 | 616 | void bigFile( int doThis ) { 617 | char myFile[] = "/0_bigfile.txt"; 618 | char fileID = '0' - 1; 619 | 620 | if ( 0 == doThis ) { // delete File 621 | Serial.printf( "\nDelete with read verify all #bigfile's\n"); 622 | do { 623 | fileID++; 624 | myFile[1] = fileID; 625 | if ( myfs.exists(myFile) && bigVerify( myFile, fileID) ) { 626 | filecount--; 627 | myfs.remove(myFile); 628 | } 629 | else break; // no more of these 630 | } while ( 1 ); 631 | } 632 | else { // FILL DISK 633 | lfs_ssize_t resW = 1; 634 | char someData[2048]; 635 | uint32_t xx, toWrite; 636 | toWrite = (myfs.totalSize()) - myfs.usedSize(); 637 | if ( toWrite < 65535 ) { 638 | Serial.print( "Disk too full! DO :: b or q or F"); 639 | return; 640 | } 641 | toWrite -= SLACK_SPACE; 642 | toWrite /= 2; 643 | xx = toWrite; 644 | Serial.printf( "\nStart Big write of %u Bytes", xx); 645 | uint32_t timeMe = micros(); 646 | file3 = nullptr; 647 | do { 648 | if ( file3 ) file3.close(); 649 | fileID++; 650 | myFile[1] = fileID; 651 | file3 = myfs.open(myFile, FILE_WRITE); 652 | } while ( fileID < '9' && file3.size() > 0); 653 | if ( fileID == '9' ) { 654 | Serial.print( "Disk has 9 halves 0-8! DO :: b or q or F"); 655 | return; 656 | } 657 | memset( someData, fileID, 2048 ); 658 | int hh = 0; 659 | while ( toWrite > 2048 && resW > 0 ) { 660 | resW = file3.write( someData , 2048 ); 661 | hh++; 662 | if ( !(hh % 40) ) Serial.print('.'); 663 | toWrite -= 2048; 664 | } 665 | xx -= toWrite; 666 | file3.close(); 667 | timeMe = micros() - timeMe; 668 | file3 = myfs.open(myFile, FILE_WRITE); 669 | if ( file3.size() > 0 ) { 670 | filecount++; 671 | Serial.printf( "\nBig write %s took %5.2f Sec for %lu Bytes : file3.size()=%llu", myFile , timeMe / 1000000.0, xx, file3.size() ); 672 | } 673 | if ( file3 != 0 ) file3.close(); 674 | Serial.printf( "\n\tBig write KBytes per second %5.2f \n", xx / (timeMe / 1000.0) ); 675 | Serial.printf("\nBytes Used: %llu, Bytes Total:%llu\n", myfs.usedSize(), myfs.totalSize()); 676 | if ( myfs.usedSize() == myfs.totalSize() ) { 677 | Serial.printf("\n\n\tWARNING: DISK FULL >>>>> Bytes Used: %llu, Bytes Total:%llu\n\n", myfs.usedSize(), myfs.totalSize()); 678 | warnLFS++; 679 | } 680 | if ( resW < 0 ) { 681 | Serial.printf( "\nBig write ERR# %i 0x%X \n", resW, resW ); 682 | errsLFS++; 683 | myfs.remove(myFile); 684 | } 685 | } 686 | } 687 | 688 | void bigFile2MB( int doThis ) { 689 | char myFile[] = "/0_2MBfile.txt"; 690 | char fileID = '0' - 1; 691 | 692 | if ( 0 == doThis ) { // delete File 693 | Serial.printf( "\nDelete with read verify all #bigfile's\n"); 694 | do { 695 | fileID++; 696 | myFile[1] = fileID; 697 | if ( myfs.exists(myFile) && bigVerify( myFile, fileID) ) { 698 | filecount--; 699 | myfs.remove(myFile); 700 | } 701 | else break; // no more of these 702 | } while ( 1 ); 703 | } 704 | else { // FILL DISK 705 | lfs_ssize_t resW = 1; 706 | char someData[2048]; 707 | uint32_t xx, toWrite; 708 | toWrite = 2048 * 1000; 709 | if ( toWrite > (65535 + (myfs.totalSize() - myfs.usedSize()) ) ) { 710 | Serial.print( "Disk too full! DO :: q or F"); 711 | return; 712 | } 713 | xx = toWrite; 714 | Serial.printf( "\nStart Big write of %u Bytes", xx); 715 | uint32_t timeMe = micros(); 716 | file3 = nullptr; 717 | do { 718 | if ( file3 ) file3.close(); 719 | fileID++; 720 | myFile[1] = fileID; 721 | file3 = myfs.open(myFile, FILE_WRITE); 722 | } while ( fileID < '9' && file3.size() > 0); 723 | if ( fileID == '9' ) { 724 | Serial.print( "Disk has 9 files 0-8! DO :: b or q or F"); 725 | return; 726 | } 727 | memset( someData, fileID, 2048 ); 728 | int hh = 0; 729 | while ( toWrite >= 2048 && resW > 0 ) { 730 | resW = file3.write( someData , 2048 ); 731 | hh++; 732 | if ( !(hh % 40) ) Serial.print('.'); 733 | toWrite -= 2048; 734 | } 735 | xx -= toWrite; 736 | file3.close(); 737 | timeMe = micros() - timeMe; 738 | file3 = myfs.open(myFile, FILE_WRITE); 739 | if ( file3.size() > 0 ) { 740 | filecount++; 741 | Serial.printf( "\nBig write %s took %5.2f Sec for %lu Bytes : file3.size()=%llu", myFile , timeMe / 1000000.0, xx, file3.size() ); 742 | } 743 | if ( file3 != 0 ) file3.close(); 744 | Serial.printf( "\n\tBig write KBytes per second %5.2f \n", xx / (timeMe / 1000.0) ); 745 | Serial.printf("\nBytes Used: %llu, Bytes Total:%llu\n", myfs.usedSize(), myfs.totalSize()); 746 | if ( myfs.usedSize() == myfs.totalSize() ) { 747 | Serial.printf("\n\n\tWARNING: DISK FULL >>>>> Bytes Used: %llu, Bytes Total:%llu\n\n", myfs.usedSize(), myfs.totalSize()); 748 | warnLFS++; 749 | } 750 | if ( resW < 0 ) { 751 | Serial.printf( "\nBig write ERR# %i 0x%X \n", resW, resW ); 752 | errsLFS++; 753 | myfs.remove(myFile); 754 | } 755 | } 756 | } 757 | 758 | void makeRootDirs() { 759 | char szDir[16]; 760 | for ( uint32_t ii = 1; ii <= NUMDIRS; ii++ ) { 761 | sprintf( szDir, "/%lu_dir", ii ); 762 | myfs.mkdir( szDir ); 763 | } 764 | } 765 | 766 | // for void speedBench() 767 | #include 768 | ArduinoOutStream cout(Serial); 769 | File file; 770 | 771 | // File size in bytes. 772 | const uint32_t FILE_SIZE = 16 * 1024; 773 | 774 | // Set SKIP_FIRST_LATENCY true if the first read/write to the SD can 775 | // be avoid by writing a file header or reading the first record. 776 | const bool SKIP_FIRST_LATENCY = true; 777 | 778 | // Size of read/write. 779 | const size_t BUF_SIZE = 2048; 780 | 781 | // Write pass count. 782 | const uint8_t WRITE_COUNT = 5; 783 | 784 | // Read pass count. 785 | const uint8_t READ_COUNT = 5; 786 | 787 | //Block size for qspi 788 | #define MYBLKSIZE 2048 // 2048 789 | 790 | // Insure 4-byte alignment. 791 | uint32_t buf32[(BUF_SIZE + 3) / 4]; 792 | uint8_t* bufA32 = (uint8_t*)buf32; 793 | 794 | //Number of random reads 795 | #define randomReads 1 796 | 797 | void speedBench() { 798 | File file; 799 | float s; 800 | uint32_t t; 801 | uint32_t maxLatency; 802 | uint32_t minLatency; 803 | uint32_t totalLatency; 804 | bool skipLatency; 805 | 806 | myfs.remove("bench.dat"); 807 | //for(uint8_t cnt=0; cnt < 10; cnt++) { 808 | 809 | // fill buf with known data 810 | if (BUF_SIZE > 1) { 811 | for (size_t i = 0; i < (BUF_SIZE - 2); i++) { 812 | bufA32[i] = 'A' + (i % 26); 813 | } 814 | bufA32[BUF_SIZE - 2] = '\r'; 815 | } 816 | bufA32[BUF_SIZE - 1] = '\n'; 817 | 818 | Serial.printf("%s Disk Stats:", szDiskMem ); 819 | Serial.printf("Bytes Used: %llu, Bytes Total:%llu\n", myfs.usedSize(), myfs.totalSize()); 820 | Serial.printf("%s Benchmark:\n", szDiskMem ); 821 | cout << F("FILE_SIZE = ") << FILE_SIZE << endl; 822 | cout << F("BUF_SIZE = ") << BUF_SIZE << F(" bytes\n"); 823 | cout << F("Starting write test, please wait.") << endl << endl; 824 | 825 | // do write test 826 | uint32_t n = FILE_SIZE / BUF_SIZE; 827 | cout << F("write speed and latency") << endl; 828 | cout << F("speed,max,min,avg") << endl; 829 | cout << F("KB/Sec,usec,usec,usec") << endl; 830 | 831 | // open or create file - truncate existing file. 832 | file = myfs.open("bench.dat", FILE_WRITE); 833 | 834 | for (uint8_t nTest = 0; nTest < WRITE_COUNT; nTest++) { 835 | file.seek(0); 836 | 837 | maxLatency = 0; 838 | minLatency = 9999999; 839 | totalLatency = 0; 840 | skipLatency = SKIP_FIRST_LATENCY; 841 | t = millis(); 842 | 843 | for (uint32_t i = 0; i < n; i++) { 844 | uint32_t m = micros(); 845 | if (file.write(bufA32, BUF_SIZE) != BUF_SIZE) { 846 | Serial.println("write failed"); 847 | } 848 | m = micros() - m; 849 | totalLatency += m; 850 | if (skipLatency) { 851 | // Wait until first write to SD, not just a copy to the cache. 852 | skipLatency = file.position() < 512; 853 | } else { 854 | if (maxLatency < m) { 855 | maxLatency = m; 856 | } 857 | if (minLatency > m) { 858 | minLatency = m; 859 | } 860 | } 861 | } 862 | 863 | t = millis() - t; 864 | s = file.size(); 865 | cout << s / t << ',' << maxLatency << ',' << minLatency; 866 | cout << ',' << totalLatency / n << endl; 867 | } 868 | cout << endl << F("Starting sequential read test, please wait.") << endl; 869 | cout << endl << F("read speed and latency") << endl; 870 | cout << F("speed,max,min,avg") << endl; 871 | cout << F("KB/Sec,usec,usec,usec") << endl; 872 | 873 | // do read test 874 | for (uint8_t nTest = 0; nTest < READ_COUNT; nTest++) { 875 | file.seek(0); 876 | maxLatency = 0; 877 | minLatency = 9999999; 878 | totalLatency = 0; 879 | skipLatency = SKIP_FIRST_LATENCY; 880 | t = micros(); 881 | for (uint32_t i = 0; i < n; i++) { 882 | bufA32[BUF_SIZE - 1] = 0; 883 | uint32_t m = micros(); 884 | int32_t nr = file.read(bufA32, BUF_SIZE); 885 | if (nr != BUF_SIZE) { 886 | Serial.println("read failed"); 887 | } 888 | m = micros() - m; 889 | totalLatency += m; 890 | if (bufA32[BUF_SIZE - 1] != '\n') { 891 | Serial.println("data check error"); 892 | } 893 | if (skipLatency) { 894 | skipLatency = false; 895 | } else { 896 | if (maxLatency < m) { 897 | maxLatency = m; 898 | } 899 | if (minLatency > m) { 900 | minLatency = m; 901 | } 902 | } 903 | } 904 | 905 | s = file.size(); 906 | 907 | t = micros() - t; 908 | cout << s * 1000 / t << ',' << maxLatency << ',' << minLatency; 909 | cout << ',' << totalLatency / n << endl; 910 | } 911 | 912 | cout << endl << F("Done") << endl; 913 | 914 | cout << endl << F("Starting random read test, please wait.") << endl; 915 | 916 | Serial.printf("Number of random reads: %d\n", randomReads); 917 | Serial.printf("Number of blocks: %d\n", n); 918 | 919 | cout << endl << F("read speed and latency") << endl; 920 | cout << F("speed,max,min,avg") << endl; 921 | 922 | // do read test 923 | for (uint8_t nTest = 0; nTest < READ_COUNT; nTest++) { 924 | file.seek(0); 925 | maxLatency = 0; 926 | minLatency = 0; 927 | totalLatency = 0; 928 | skipLatency = SKIP_FIRST_LATENCY; 929 | t = micros(); 930 | for (uint32_t i = 0; i < randomReads; i++) { 931 | bufA32[BUF_SIZE - 1] = 0; 932 | uint32_t m = micros(); 933 | uint32_t block_pos = random(0, (n - 1)); 934 | uint32_t random_pos = block_pos * MYBLKSIZE; 935 | cout << "Position (bytes), Block: " << random_pos << ", "; 936 | cout << block_pos << endl; 937 | uint32_t startCNT = ARM_DWT_CYCCNT; 938 | file.seek(random_pos); 939 | int32_t nr = file.read(bufA32, BUF_SIZE); 940 | uint32_t endCNT = ARM_DWT_CYCCNT; 941 | cout << F("Read Time (ARM Cycle Delta): ") << endCNT - startCNT << endl; 942 | if (nr != BUF_SIZE) { 943 | Serial.println("read failed"); 944 | } 945 | m = micros() - m; 946 | totalLatency += m; 947 | if (bufA32[BUF_SIZE - 1] != '\n') { 948 | Serial.println("data check error"); 949 | } 950 | if (skipLatency) { 951 | skipLatency = false; 952 | } else { 953 | if (maxLatency < m) { 954 | maxLatency = m; 955 | } 956 | if (minLatency > m) { 957 | minLatency = m; 958 | } 959 | } 960 | } 961 | 962 | s = file.size(); 963 | 964 | 965 | t = micros() - t; 966 | cout << F("KB/Sec,usec,usec,usec") << endl; 967 | cout << s * 1000 / t << ',' << maxLatency << ',' << minLatency; 968 | cout << ',' << totalLatency / n << endl; 969 | } 970 | cout << endl << F("Done") << endl; 971 | 972 | file.close(); 973 | myfs.remove("bench.dat"); 974 | 975 | } 976 | -------------------------------------------------------------------------------- /examples/Test_Integrity/SPI/SPI.ino: -------------------------------------------------------------------------------- 1 | #include "LittleFS.h" 2 | 3 | /* NOTES on LittleFS and Integrity testa 4 | - Sketches are a Media specific subset of tests done during development 5 | - When the Media is properly connected, it allows repeated FS file access to fill and verify function 6 | - The goal then was to repeatedly Create files and then Add to the file, or Delete the file 7 | - This allowed verifying File and Directory data integrtty as the Media was filled and reused as the data moved 8 | - Other than for Media and device testing - the code provides examples of support for the commands shown. 9 | - It will also provide a rough estimate of data I/O speed for the Media at hand 10 | - There is a Case Sensitive command set active when run displayed on start or when '?' is entered 11 | - For the primary looping enter number 1-9, h, or k and 'c' for continuous. A running count will stop with '0' 12 | - 'd' for directory will walk the directory and verify against the expected number of files added/deleted 13 | - Many commands were added as LittleFS developed, early work has lots of verbose settings now TOGGLED off for speed 14 | - 15 | - Each named sketch works on a specific Media type as presented 16 | - there are various commands provided to demonstrate usage 17 | - All .size() functions return a 64 bit uint64_t - take care when printing 18 | */ 19 | 20 | 21 | /* \/ == Uncomment ONE of the TWO lines to select SPI FLASH type: NOR or NAND == \/ */ 22 | 23 | #define TEST_SPI // Typical NOR FLASH 24 | //#define TEST_SPI_NAND // NAND Flash 25 | 26 | /* /\ == Uncomment ONE of the TWO lines to select SPI FLASH type: NOR or NAND == /\ */ 27 | 28 | // Set which Chip Select pin for SPI usage 29 | const int FlashChipSelect = 6; // PJRC AUDIO BOARD is 10 // Tested NOR 64MB on #5, #6 : NAND 1Gb on #3, 2GB on #4 30 | 31 | #ifdef TEST_SPI 32 | LittleFS_SPIFlash myfs; 33 | char szDiskMem[] = "SPI_DISK"; 34 | #elif defined(TEST_SPI_NAND) 35 | LittleFS_SPINAND myfs; 36 | char szDiskMem[] = "SPI_NAND"; 37 | #endif 38 | 39 | // Adjust these for amount of disk space used in iterations 40 | #define MAXNUM 5 // Number of files : ALPHA A-Z is MAX of 26, less for fewer files 41 | #define NUMDIRS 5 // Number of Directories to use 0 is Rootonly 42 | #define BIGADD 2024 // bytes added each pass - bigger will quickly consume more space 43 | #define SUBADD 512 // bytes added each pass (*times file number) 44 | #define MAXFILL 2048 // 66000 // ZERO to disable :: Prevent iterations from over filling - require this much free 45 | 46 | // These can likely be left unchanged 47 | #define MYBLKSIZE 2048 // 2048 48 | #define SLACK_SPACE 40960 // allow for overhead slack space :: WORKS on FLASH {some need more with small alloc units} 49 | uint32_t DELSTART = 3; // originally was 3 + higher bias more to writes and larger files - lower odd 50 | #define DELDELAY 0 // delay before DEL files : delayMicroseconds 51 | #define ADDDELAY 0 // delay on ADD FILE : delayMicroseconds 52 | 53 | // Various Globals 54 | const uint32_t lowOffset = 'a' - 'A'; 55 | const uint32_t lowShift = 13; 56 | uint32_t errsLFS = 0, warnLFS = 0; // Track warnings or Errors 57 | uint32_t lCnt = 0, LoopCnt = 0; // loop counters 58 | uint64_t rdCnt = 0, wrCnt = 0; // Track Bytes Read and Written 59 | int filecount = 0; 60 | int loopLimit = 0; // -1 continuous, otherwise # to count down to 0 61 | bool pauseDir = false; // Start Pause on each off 62 | bool showDir = false; // false Start Dir on each off 63 | bool bDirVerify = false; // false Start Dir on each off 64 | bool bWriteVerify = true; // Verify on Write Toggle 65 | bool bAutoFormat = false; // false Auto formatUnused() off 66 | bool bCheckFormat = false; // false CheckFormat 67 | bool bCheckUsed = false; // false CheckUsed 68 | uint32_t res = 0; // for formatUnused 69 | File file3; // Single FILE used for all functions 70 | 71 | void setup() { 72 | while (!Serial) ; // wait 73 | Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__); 74 | Serial.println("LittleFS Test : File Integrity"); delay(5); 75 | 76 | if (!myfs.begin( FlashChipSelect )) { 77 | Serial.printf("Error starting %s\n", szDiskMem); 78 | while( 1 ); 79 | } 80 | filecount = printDirectoryFilecount( myfs.open("/") ); // Set base value of filecount for disk 81 | printDirectory(); 82 | parseCmd( '?' ); 83 | makeRootDirs(); 84 | checkInput( 1 ); 85 | filecount = printDirectoryFilecount( myfs.open("/") ); // Set base value of filecount for disk 86 | printDirectory(); 87 | } 88 | 89 | void loop() { 90 | char szDir[16]; 91 | LoopCnt++; 92 | uint32_t chStep; 93 | if ( loopLimit != 0 ) { 94 | for ( uint32_t ii = 0; ii <= NUMDIRS && ( loopLimit != 0 ); ii++ ) 95 | { 96 | if ( ii == 0 ) 97 | sprintf( szDir, "/" ); 98 | else 99 | sprintf( szDir, "/%lu_dir", ii ); 100 | chStep = fileCycle(szDir); 101 | if ( bAutoFormat && !(lCnt % 5) ) res = loopAutoFormat( 20, res ); 102 | while ( chStep != fileCycle(szDir) && ( loopLimit != 0 ) ) { 103 | if ( bAutoFormat && !(lCnt % 5) ) res = loopAutoFormat( 12, res ); // how often and how many depends on media and sizes 104 | checkInput( 0 ); // user input can 0 loopLimit 105 | } 106 | } 107 | checkInput( 0 ); 108 | if ( loopLimit > 0 ) // -1 means continuous 109 | loopLimit--; 110 | } 111 | else 112 | checkInput( 1 ); 113 | } 114 | -------------------------------------------------------------------------------- /examples/Test_Integrity/SPI/functions.ino: -------------------------------------------------------------------------------- 1 | 2 | char szInputs[] = "0123456789RdDwcghkFqvplmusSBbyYxfan+-?"; 3 | uint32_t lastTime; 4 | void checkInput( int step ) { // prompt for input without user input with step != 0 5 | uint32_t nowTime = micros(); 6 | 7 | char retVal = 0, temp; 8 | char *pTemp; 9 | if ( step != 0 ) { 10 | nowTime -= lastTime; 11 | Serial.printf( "[%6.2f M](%6.5f M elap) Awaiting input %s loops left %d >", millis() / 60000.0, nowTime / 60000000.0, szInputs, loopLimit ); 12 | } 13 | else { 14 | if ( !Serial.available() ) return; 15 | nowTime -= lastTime; 16 | Serial.printf( "[%6.2f M](%6.2f elap) Awaiting input %s loops left %d >", millis() / 60000.0, nowTime / 60000000.0, szInputs, loopLimit ); 17 | //Serial.printf( "[%6.2f] Awaiting input %s loops left %d >", millis() / 60000.0, szInputs, loopLimit ); 18 | while ( Serial.available() ) { 19 | temp = Serial.read( ); 20 | if ( (pTemp = strchr(szInputs, temp)) ) { 21 | retVal = pTemp[0]; 22 | parseCmd( retVal ); 23 | } 24 | } 25 | } 26 | while ( !Serial.available() ); 27 | while ( Serial.available() ) { 28 | temp = Serial.read(); 29 | if ( (pTemp = strchr(szInputs, temp)) ) { 30 | retVal = pTemp[0]; 31 | parseCmd( retVal ); 32 | } 33 | } 34 | Serial.print( '\n' ); 35 | if ( '?' == retVal ) checkInput( 1 ); // recurse on '?' to allow command show and response 36 | return; 37 | } 38 | void parseCmd( char chIn ) { // pass chIn == '?' for help 39 | uint32_t timeMe; 40 | char szNone[] = ""; 41 | switch (chIn ) { 42 | case '?': 43 | Serial.printf( "\n%s\n", " 1-9 '#' passes continue Loop before Pause\n\ 44 | 'c, or 0': Continuous Loop, or stop Loop in progress\n\ 45 | 'h, or k': start Hundred or Thousand Loops\n\ 46 | 'd' Directory of LittleFS Media\n\ 47 | 'D' Walk all Dir Files verify Read Size\n\ 48 | 'l' Show count of loop()'s, Bytes Read,Written\n\ 49 | 'B, or b': Make Big file half of free space, or remove all Big files\n\ 50 | 'S, or s': Make 2MB file , or remove all 2MB files\n\ 51 | \t>> Media Format : 'q' and 'F' remove ALL FS data\n\ 52 | 'q' Quick Format Disk \n\ 53 | 'F' Low Level Format Disk \n\ 54 | 'w' WIPE ALL Directories and Files\n\ 55 | 'm' Make ROOT dirs (needed after q/F format or 'w')\n\ 56 | 'f' LittleFS::formatUnused( ALL ) : DATA PRESERVED, empty blocks preformatted \n\ 57 | 'a' Auto formatUnused() during Loop - TOGGLE\n\ 58 | 'y' reclaim 1 block :: myfs.formatUnused( 1 )\n\ 59 | 'Y' reclaim 15 blocks :: myfs.formatUnused( 15 )\n\ 60 | \t>> Test Features \n\ 61 | 'g' run SpeedBench() Media speed benchmark\n\ 62 | 'x' Directory filecount verify - TOGGLE\n\ 63 | 'v' Verbose All Dir Prints - TOGGLE\n\ 64 | 'p' Pause after all Dir prints - TOGGLE\n\ 65 | 'n' No verify on Write- TOGGLE\n\ 66 | 'u' Update Filecount\n\ 67 | 'R' Restart Teensy - Except 'RAM' - data persists\n\ 68 | '+, or -': more, or less add .vs. delete in Loop\n\ 69 | '?' Help list : A Loop will Create, Extend, or Delete files to verify Integrity" ); 70 | break; 71 | case 'R': 72 | Serial.print(" RESTART Teensy ..."); 73 | delay(100); 74 | SCB_AIRCR = 0x05FA0004; 75 | break; 76 | case '0': 77 | case '1': 78 | case '2': 79 | case '3': 80 | case '4': 81 | case '5': 82 | case '6': 83 | case '7': 84 | case '8': 85 | case '9': 86 | loopLimit = chIn - '0'; 87 | if ( chIn != '0' ) // Preserve elapsed time on Error or STOP command 88 | lastTime = micros(); 89 | break; 90 | case 'b': 91 | bigFile( 0 ); // delete 92 | chIn = 0; 93 | break; 94 | case 'B': 95 | lastTime = micros(); 96 | bigFile( 1 ); // CREATE 97 | chIn = 0; 98 | break; 99 | case 's': 100 | bigFile2MB( 0 ); // CREATE 101 | chIn = 0; 102 | break; 103 | case 'S': 104 | lastTime = micros(); 105 | bigFile2MB( 1 ); // CREATE 106 | chIn = 0; 107 | break; 108 | case 'c': 109 | loopLimit = -1; 110 | break; 111 | case 'g': 112 | lastTime = micros(); 113 | speedBench(); 114 | chIn = 0; 115 | break; 116 | case 'd': 117 | Serial.print( " d\n" ); 118 | lastTime = micros(); 119 | printDirectory(); 120 | Serial.print( '\n' ); 121 | parseCmd( 'l' ); 122 | checkInput( 1 ); 123 | chIn = 0; 124 | break; 125 | case 'D': 126 | Serial.println("\nWalk all Files verify Read Size:\t"); 127 | lastTime = micros(); 128 | Serial.printf( "\t%u Errors found\n" , DirectoryVerify( myfs.open("/") ) ); 129 | Serial.printf("\nBytes Used: %llu, Bytes Total:%llu\n", myfs.usedSize(), myfs.totalSize()); 130 | parseCmd( 'l' ); 131 | checkInput( 1 ); 132 | chIn = 0; 133 | break; 134 | case 'w': 135 | Serial.println("\nWipe All Files and DIRS:"); 136 | deleteAllDirectory(myfs.open("/"), szNone ); 137 | errsLFS = 0; // No Errors on new Format 138 | warnLFS = 0; // No warning on new Format 139 | chIn = 0; 140 | parseCmd( 'u' ); 141 | break; 142 | case 'h': 143 | loopLimit = 100; 144 | lastTime = micros(); 145 | break; 146 | case 'k': 147 | loopLimit = 1000; 148 | lastTime = micros(); 149 | break; 150 | case 'F': // Low Level format 151 | Serial.print( "\nFormatting Low Level:\n\t" ); 152 | lastTime = micros(); 153 | timeMe = micros(); 154 | myfs.lowLevelFormat('.'); 155 | timeMe = micros() - timeMe; 156 | Serial.printf( "\n Done Formatting Low Level in %lu us.\n", timeMe ); 157 | errsLFS = 0; // No Errors on new Format 158 | warnLFS = 0; // No warning on new Format 159 | bCheckFormat = false; 160 | parseCmd( 'u' ); 161 | break; 162 | case 'q': // quick format 163 | lastTime = micros(); 164 | myfs.quickFormat(); 165 | errsLFS = 0; // No Errors on new Format 166 | parseCmd( 'u' ); 167 | break; 168 | case 'v': // verbose dir 169 | showDir = !showDir; 170 | showDir ? Serial.print(" Verbose on: ") : Serial.print(" Verbose off: "); 171 | chIn = 0; 172 | break; 173 | case 'p': // pause on dirs 174 | pauseDir = !pauseDir; 175 | pauseDir ? Serial.print(" Pause on: ") : Serial.print(" Pause off: "); 176 | chIn = 0; 177 | break; 178 | case 'x': // dir filecount Verify 179 | bDirVerify = !bDirVerify; 180 | bDirVerify ? Serial.print(" FileCnt on: ") : Serial.print(" FileCnt off: "); 181 | lastTime = micros(); 182 | dirVerify(); 183 | chIn = 0; 184 | break; 185 | case 'n': // No Verify on write 186 | bWriteVerify = !bWriteVerify; 187 | bWriteVerify ? Serial.print(" Write Verify on: ") : Serial.print(" Write Verify off: "); 188 | chIn = 0; 189 | break; 190 | case 'a': // Auto myfs.formatUnused() during iterations 191 | bAutoFormat = !bAutoFormat; 192 | bAutoFormat ? Serial.print(" \nAuto formatUnused() On: ") : Serial.print(" \nAuto formatUnused() Off: "); 193 | chIn = 0; 194 | break; 195 | case 'y': // Reclaim 1 unused format 196 | lastTime = micros(); 197 | Serial.printf( "\n myfs.formatUnused( 1 ) ...\n" ); 198 | timeMe = micros(); 199 | res = myfs.formatUnused( 1, res ); 200 | timeMe = micros() - timeMe; 201 | Serial.printf( "\n\t formatUnused :: Done Formatting Low Level in %lu us (last %lu).\n", timeMe, res ); 202 | chIn = 0; 203 | break; 204 | case 'Y': // Reclaim 15 unused format 205 | lastTime = micros(); 206 | Serial.printf( "\n myfs.formatUnused( 15 ) ...\n" ); 207 | timeMe = micros(); 208 | res = myfs.formatUnused( 15, res ); 209 | timeMe = micros() - timeMe; 210 | Serial.printf( "\n\t formatUnused :: Done Formatting Low Level in %lu us (last %lu).\n", timeMe, res ); 211 | chIn = 0; 212 | break; 213 | case 'f': // Reclaim all unused format 214 | lastTime = micros(); 215 | Serial.printf( "\n myfs.formatUnused( 0 ) ...\n" ); 216 | timeMe = micros(); 217 | myfs.formatUnused( 0, 0 ); 218 | timeMe = micros() - timeMe; 219 | Serial.printf( "\n\t formatUnused :: Done Formatting Low Level in %lu us.\n", timeMe ); 220 | chIn = 0; 221 | break; 222 | case 'l': // Show Loop Count 223 | Serial.printf("\n\t Loop Count: %u (#fileCycle=%u), Bytes read %llu, written %llu, #Files=%u\n", LoopCnt, lCnt, rdCnt, wrCnt, filecount ); 224 | if ( 0 != errsLFS ) 225 | Serial.printf("\t ERROR COUNT =%u\n", errsLFS ); 226 | if ( 0 != warnLFS ) 227 | Serial.printf("\t Free Space Warn COUNT =%u\n", warnLFS ); 228 | dirVerify(); 229 | chIn = 0; 230 | break; 231 | case 'm': 232 | Serial.printf("m \n\t Making Root Dirs\n" ); 233 | makeRootDirs(); 234 | parseCmd( 'd' ); 235 | chIn = 0; 236 | break; 237 | case 'u': // Show Loop Count 238 | filecount = printDirectoryFilecount( myfs.open("/") ); 239 | Serial.printf("\n\t Updated filecount %u\n", filecount ); 240 | chIn = 0; 241 | break; 242 | case '+': // increase add cycles 243 | DELSTART++; 244 | Serial.printf("+\n Deletes start after %u cycles ", DELSTART); 245 | chIn = 0; 246 | break; 247 | case '-': // decrease add cycles 248 | DELSTART--; 249 | if ( DELSTART < 1 ) DELSTART = 1; 250 | Serial.printf("-\n Deletes start after %u cycles ", DELSTART); 251 | chIn = 0; 252 | break; 253 | default: 254 | Serial.println( chIn ); // never see without unhandled char in szInputs[] 255 | break; 256 | } 257 | if ( 0 != chIn ) Serial.print( chIn ); 258 | } 259 | 260 | uint32_t loopAutoFormat( uint32_t cnt, uint32_t myres ) { 261 | uint32_t retres; 262 | retres = myfs.formatUnused( cnt, myres ); 263 | Serial.printf("\t fmtU @ %lu - %lu \n", myres, retres ); 264 | return retres; 265 | } 266 | 267 | uint32_t fTot, totSize; 268 | void printDirectory() { 269 | fTot = 0, totSize = 0; 270 | Serial.printf("printDirectory %s\n--------------\n", szDiskMem); 271 | printDirectory(myfs.open("/"), 0); 272 | Serial.printf(" %Total %u files of Size %u Bytes\n", fTot, totSize); 273 | Serial.printf("Bytes Used: %llu, Bytes Total:%llu\n", myfs.usedSize(), myfs.totalSize()); 274 | if ( myfs.usedSize() == myfs.totalSize() ) { 275 | Serial.printf("\n\n\tWARNING: DISK FULL >>>>> Bytes Used: %llu, Bytes Total:%llu\n\n", myfs.usedSize(), myfs.totalSize()); 276 | warnLFS++; 277 | } 278 | } 279 | 280 | void deleteAllDirectory(File dir, char *fullPath ) { 281 | char myPath[ 256 ] = ""; 282 | while (true) { 283 | File entry = dir.openNextFile(); 284 | if (! entry) { 285 | // no more files 286 | break; 287 | } 288 | if (entry.isDirectory()) { 289 | strcpy( myPath, fullPath); 290 | strcat( myPath, entry.name()); 291 | strcat( myPath, "/"); 292 | deleteAllDirectory(entry, myPath); 293 | entry.close(); 294 | if ( !myfs.remove( myPath ) ) 295 | Serial.print( " Fail remove DIR>\t"); 296 | else 297 | Serial.print( " Removed DIR>\t"); 298 | Serial.println( myPath ); 299 | 300 | } else { 301 | strcpy( myPath, fullPath); 302 | strcat( myPath, entry.name()); 303 | entry.close(); 304 | if ( !myfs.remove( myPath ) ) 305 | Serial.print( "\tFail remove>\t"); 306 | else 307 | Serial.print( "\tRemoved>\t"); 308 | Serial.println( myPath ); 309 | } 310 | } 311 | } 312 | 313 | int printDirectoryFilecount(File dir) { 314 | unsigned int filecnt = 0; 315 | while (true) { 316 | File entry = dir.openNextFile(); 317 | if (! entry) { 318 | // no more files 319 | break; 320 | } 321 | if (entry.isDirectory()) { 322 | filecnt += printDirectoryFilecount(entry); 323 | } else { 324 | filecnt++; 325 | } 326 | entry.close(); 327 | } 328 | return filecnt; 329 | } 330 | 331 | void printDirectory(File dir, int numTabs) { 332 | uint64_t fSize = 0; 333 | uint32_t dCnt = 0, fCnt = 0; 334 | if ( 0 == dir ) { 335 | Serial.printf( "\t>>>\t>>>>> No Dir\n" ); 336 | return; 337 | } 338 | while (true) { 339 | File entry = dir.openNextFile(); 340 | if (! entry) { 341 | // no more files 342 | Serial.printf("\n %u dirs with %u files of Size %llu Bytes\n", dCnt, fCnt, fSize); 343 | fTot += fCnt; 344 | totSize += fSize; 345 | break; 346 | } 347 | for (uint8_t i = 0; i < numTabs; i++) { 348 | Serial.print('\t'); 349 | } 350 | 351 | if (entry.isDirectory()) { 352 | Serial.print("DIR\t"); 353 | dCnt++; 354 | } else { 355 | Serial.print("FILE\t"); 356 | fCnt++; 357 | fSize += entry.size(); 358 | } 359 | Serial.print(entry.name()); 360 | if (entry.isDirectory()) { 361 | Serial.println(" / "); 362 | printDirectory(entry, numTabs + 1); 363 | } else { 364 | // files have sizes, directories do not 365 | Serial.print("\t\t"); 366 | Serial.println(entry.size(), DEC); 367 | } 368 | entry.close(); 369 | //Serial.flush(); 370 | } 371 | } 372 | 373 | int DirectoryVerify(File dir) { 374 | int errCnt = 0; 375 | while (true) { 376 | File entry = dir.openNextFile(); 377 | if (! entry) { 378 | break; 379 | } 380 | if (entry.isDirectory()) { 381 | Serial.print("\tD"); 382 | Serial.print(entry.name()[0]); 383 | Serial.print(" "); 384 | errCnt += DirectoryVerify(entry); 385 | Serial.print("\n"); 386 | } else { 387 | uint64_t fileSize, sizeCnt = 0; 388 | char mm; 389 | Serial.print(entry.name()[0]); 390 | fileSize = entry.size(); 391 | while ( entry.available() ) { 392 | if (fileSize < sizeCnt ) break; 393 | entry.read( &mm , 1 ); 394 | sizeCnt++; 395 | } 396 | if (fileSize != sizeCnt ) { 397 | Serial.printf("\n File Size Error:: %s found %llu Bytes for Size %llu \n", entry.name(), sizeCnt, fileSize); 398 | errCnt++; 399 | errsLFS++; 400 | } 401 | } 402 | entry.close(); 403 | } 404 | return errCnt; 405 | } 406 | 407 | uint32_t cCnt = 0; 408 | uint32_t fileCycle(const char *dir) { 409 | static char szFile[] = "_file.txt"; 410 | char szPath[150]; 411 | int ii; 412 | lCnt++; 413 | byte nNum = lCnt % MAXNUM; 414 | char chNow = 'A' + lCnt % MAXNUM; 415 | lfs_ssize_t resW = 1; 416 | uint32_t timeMeAll = micros(); 417 | 418 | if ( dir[1] == 0 ) // catch root 419 | sprintf( szPath, "/%c%s", chNow, szFile ); 420 | else 421 | sprintf( szPath, "%s/%c%s", dir, chNow, szFile ); 422 | if ( cCnt >= DELSTART && myfs.exists(szPath) ) { // DELETE ALL KNOWN FILES 423 | if ( nNum == 1 ) { 424 | Serial.print( "\n == == == DELETE PASS START == == == = \n"); 425 | if ( showDir ) { 426 | printDirectory(); 427 | Serial.print( " == == == DELETE PASS START == == == = \n"); 428 | } 429 | delayMicroseconds(DELDELAY); 430 | } 431 | } 432 | Serial.printf( ":: %s ", szPath ); 433 | if ( cCnt >= DELSTART && myfs.exists(szPath) ) { // DELETE ALL KNOWN FILES 434 | readVerify( szPath, chNow ); 435 | myfs.remove(szPath); 436 | filecount--; 437 | Serial.printf(" ----DEL----"); 438 | Serial.printf(" -- %c", chNow); 439 | if ( showDir ) { 440 | Serial.print("\n"); 441 | printDirectory(myfs.open(dir), 1); 442 | } 443 | if ( pauseDir ) checkInput( 1 ); 444 | Serial.println(); 445 | } 446 | else { 447 | if ( bWriteVerify && ( myfs.totalSize() - myfs.usedSize() ) < MAXFILL ) { 448 | warnLFS++; 449 | Serial.printf( "\tXXX\tXXX\tXXX\tXXX\tSIZE WARNING { MAXFILL } \n" ); 450 | cCnt = DELSTART; 451 | return cCnt; // EARLY EXIT 452 | } 453 | if ( nNum == 0 ) { 454 | nNum = 10; 455 | cCnt++; 456 | if ( cCnt >= DELSTART + 2 ) cCnt = 0; 457 | } 458 | file3 = myfs.open(szPath, FILE_WRITE); 459 | if ( 0 == file3 ) { 460 | Serial.printf( "\tXXX\tXXX\tXXX\tXXX\tFail File open {mkdir?}\n" ); 461 | delayMicroseconds(300000); 462 | checkInput( 1 ); // PAUSE on CmdLine 463 | } 464 | else { 465 | delayMicroseconds(ADDDELAY); 466 | char mm = chNow + lowOffset; 467 | uint64_t jj = file3.size() + 1; 468 | uint32_t timeMe = micros(); 469 | int32_t nn = nNum * SUBADD + BIGADD; 470 | for ( ii = 0; ii < nn && resW >= 0; ii++ ) { 471 | if ( 0 == ((ii + jj) / lowShift) % 2 ) 472 | resW = file3.write( &mm , 1 ); 473 | else 474 | resW = file3.write( &chNow , 1 ); 475 | wrCnt++; 476 | // if ( lCnt%100 == 50 ) mm='x'; // GENERATE ERROR to detect on DELETE read verify 477 | } 478 | file3.close(); 479 | timeMe = micros() - timeMe; 480 | timeMeAll = micros() - timeMeAll; 481 | Serial.printf(" +++ size %llu: Add %d @KB/sec %5.2f ", jj - 1, nn, ii / (timeMeAll / 1000.0) ); 482 | if (resW < 0) { 483 | Serial.printf( "\n\twrite fail ERR# %i 0x%X \n", resW, resW ); 484 | parseCmd( '0' ); 485 | errsLFS++; 486 | checkInput( 1 ); // PAUSE on CmdLine 487 | } 488 | else if ( jj == 1 ) filecount++; // File Added 489 | Serial.print("\t"); 490 | if ( bWriteVerify ) 491 | readVerify( szPath, chNow ); 492 | Serial.print('\n'); 493 | if ( showDir ) { 494 | Serial.print('\n'); 495 | printDirectory(myfs.open(dir), 1); 496 | } 497 | } 498 | if ( pauseDir ) checkInput( 1 ); 499 | //Serial.print("\n"); 500 | delayMicroseconds(ADDDELAY); 501 | } 502 | checkInput( 0 ); // user stop request? 503 | if ( bDirVerify ) dirVerify(); 504 | return cCnt; 505 | } 506 | 507 | void dirVerify() { 508 | uint32_t timeMe = micros(); 509 | Serial.printf("\tdirV..."); 510 | if ( filecount != printDirectoryFilecount( myfs.open("/") ) ) { 511 | Serial.printf( "\tFilecount mismatch %u != %u\n", filecount, printDirectoryFilecount( myfs.open("/") ) ); 512 | parseCmd( '0' ); 513 | errsLFS++; 514 | checkInput( 1 ); // PAUSE on CmdLine 515 | } 516 | timeMe = micros() - timeMe; 517 | Serial.printf("%lu_us\t", timeMe); 518 | } 519 | 520 | void readVerify( char szPath[], char chNow ) { 521 | uint32_t timeMe = micros(); 522 | file3 = myfs.open(szPath); 523 | if ( 0 == file3 ) { 524 | Serial.printf( "\tV\t Fail File open %s\n", szPath ); 525 | parseCmd( '0' ); 526 | errsLFS++; 527 | checkInput( 1 ); 528 | } 529 | char mm; 530 | char chNow2 = chNow + lowOffset; 531 | uint32_t ii = 0; 532 | while ( file3.available() ) { 533 | file3.read( &mm , 1 ); 534 | rdCnt++; 535 | //Serial.print( mm ); // show chars as read 536 | ii++; 537 | if ( 0 == (ii / lowShift) % 2 ) { 538 | if ( chNow2 != mm ) { 539 | Serial.printf( " fSize ) { // catch over length return makes bad loop !!! 593 | Serial.printf( "\n\tFile LEN Corrupt! FS returning over %u bytes\n", fSize ); 594 | parseCmd( '0' ); 595 | errsLFS++; 596 | checkInput( 1 ); 597 | break; 598 | } 599 | } 600 | if (ii != file3.size()) { 601 | Serial.printf( "\n\tRead Count fail! :: read %u != f.size %llu\n", ii, file3.size() ); 602 | parseCmd( '0' ); 603 | errsLFS++; 604 | checkInput( 1 ); // PAUSE on CmdLine 605 | } 606 | else 607 | Serial.printf( "\tGOOD! >> bytes %lu", ii ); 608 | file3.close(); 609 | timeMe = micros() - timeMe; 610 | Serial.printf( "\n\tBig read&compare KBytes per second %5.2f \n", ii / (timeMe / 1000.0) ); 611 | if ( 0 == ii ) return false; 612 | return true; 613 | } 614 | 615 | 616 | void bigFile( int doThis ) { 617 | char myFile[] = "/0_bigfile.txt"; 618 | char fileID = '0' - 1; 619 | 620 | if ( 0 == doThis ) { // delete File 621 | Serial.printf( "\nDelete with read verify all #bigfile's\n"); 622 | do { 623 | fileID++; 624 | myFile[1] = fileID; 625 | if ( myfs.exists(myFile) && bigVerify( myFile, fileID) ) { 626 | filecount--; 627 | myfs.remove(myFile); 628 | } 629 | else break; // no more of these 630 | } while ( 1 ); 631 | } 632 | else { // FILL DISK 633 | lfs_ssize_t resW = 1; 634 | char someData[2048]; 635 | uint32_t xx, toWrite; 636 | toWrite = (myfs.totalSize()) - myfs.usedSize(); 637 | if ( toWrite < 65535 ) { 638 | Serial.print( "Disk too full! DO :: b or q or F"); 639 | return; 640 | } 641 | toWrite -= SLACK_SPACE; 642 | toWrite /= 2; 643 | xx = toWrite; 644 | Serial.printf( "\nStart Big write of %u Bytes", xx); 645 | uint32_t timeMe = micros(); 646 | file3 = nullptr; 647 | do { 648 | if ( file3 ) file3.close(); 649 | fileID++; 650 | myFile[1] = fileID; 651 | file3 = myfs.open(myFile, FILE_WRITE); 652 | } while ( fileID < '9' && file3.size() > 0); 653 | if ( fileID == '9' ) { 654 | Serial.print( "Disk has 9 halves 0-8! DO :: b or q or F"); 655 | return; 656 | } 657 | memset( someData, fileID, 2048 ); 658 | int hh = 0; 659 | while ( toWrite > 2048 && resW > 0 ) { 660 | resW = file3.write( someData , 2048 ); 661 | hh++; 662 | if ( !(hh % 40) ) Serial.print('.'); 663 | toWrite -= 2048; 664 | } 665 | xx -= toWrite; 666 | file3.close(); 667 | timeMe = micros() - timeMe; 668 | file3 = myfs.open(myFile, FILE_WRITE); 669 | if ( file3.size() > 0 ) { 670 | filecount++; 671 | Serial.printf( "\nBig write %s took %5.2f Sec for %lu Bytes : file3.size()=%llu", myFile , timeMe / 1000000.0, xx, file3.size() ); 672 | } 673 | if ( file3 != 0 ) file3.close(); 674 | Serial.printf( "\n\tBig write KBytes per second %5.2f \n", xx / (timeMe / 1000.0) ); 675 | Serial.printf("\nBytes Used: %llu, Bytes Total:%llu\n", myfs.usedSize(), myfs.totalSize()); 676 | if ( myfs.usedSize() == myfs.totalSize() ) { 677 | Serial.printf("\n\n\tWARNING: DISK FULL >>>>> Bytes Used: %llu, Bytes Total:%llu\n\n", myfs.usedSize(), myfs.totalSize()); 678 | warnLFS++; 679 | } 680 | if ( resW < 0 ) { 681 | Serial.printf( "\nBig write ERR# %i 0x%X \n", resW, resW ); 682 | errsLFS++; 683 | myfs.remove(myFile); 684 | } 685 | } 686 | } 687 | 688 | void bigFile2MB( int doThis ) { 689 | char myFile[] = "/0_2MBfile.txt"; 690 | char fileID = '0' - 1; 691 | 692 | if ( 0 == doThis ) { // delete File 693 | Serial.printf( "\nDelete with read verify all #bigfile's\n"); 694 | do { 695 | fileID++; 696 | myFile[1] = fileID; 697 | if ( myfs.exists(myFile) && bigVerify( myFile, fileID) ) { 698 | filecount--; 699 | myfs.remove(myFile); 700 | } 701 | else break; // no more of these 702 | } while ( 1 ); 703 | } 704 | else { // FILL DISK 705 | lfs_ssize_t resW = 1; 706 | char someData[2048]; 707 | uint32_t xx, toWrite; 708 | toWrite = 2048 * 1000; 709 | if ( toWrite > (65535 + (myfs.totalSize() - myfs.usedSize()) ) ) { 710 | Serial.print( "Disk too full! DO :: q or F"); 711 | return; 712 | } 713 | xx = toWrite; 714 | Serial.printf( "\nStart Big write of %u Bytes", xx); 715 | uint32_t timeMe = micros(); 716 | file3 = nullptr; 717 | do { 718 | if ( file3 ) file3.close(); 719 | fileID++; 720 | myFile[1] = fileID; 721 | file3 = myfs.open(myFile, FILE_WRITE); 722 | } while ( fileID < '9' && file3.size() > 0); 723 | if ( fileID == '9' ) { 724 | Serial.print( "Disk has 9 files 0-8! DO :: b or q or F"); 725 | return; 726 | } 727 | memset( someData, fileID, 2048 ); 728 | int hh = 0; 729 | while ( toWrite >= 2048 && resW > 0 ) { 730 | resW = file3.write( someData , 2048 ); 731 | hh++; 732 | if ( !(hh % 40) ) Serial.print('.'); 733 | toWrite -= 2048; 734 | } 735 | xx -= toWrite; 736 | file3.close(); 737 | timeMe = micros() - timeMe; 738 | file3 = myfs.open(myFile, FILE_WRITE); 739 | if ( file3.size() > 0 ) { 740 | filecount++; 741 | Serial.printf( "\nBig write %s took %5.2f Sec for %lu Bytes : file3.size()=%llu", myFile , timeMe / 1000000.0, xx, file3.size() ); 742 | } 743 | if ( file3 != 0 ) file3.close(); 744 | Serial.printf( "\n\tBig write KBytes per second %5.2f \n", xx / (timeMe / 1000.0) ); 745 | Serial.printf("\nBytes Used: %llu, Bytes Total:%llu\n", myfs.usedSize(), myfs.totalSize()); 746 | if ( myfs.usedSize() == myfs.totalSize() ) { 747 | Serial.printf("\n\n\tWARNING: DISK FULL >>>>> Bytes Used: %llu, Bytes Total:%llu\n\n", myfs.usedSize(), myfs.totalSize()); 748 | warnLFS++; 749 | } 750 | if ( resW < 0 ) { 751 | Serial.printf( "\nBig write ERR# %i 0x%X \n", resW, resW ); 752 | errsLFS++; 753 | myfs.remove(myFile); 754 | } 755 | } 756 | } 757 | 758 | void makeRootDirs() { 759 | char szDir[16]; 760 | for ( uint32_t ii = 1; ii <= NUMDIRS; ii++ ) { 761 | sprintf( szDir, "/%lu_dir", ii ); 762 | myfs.mkdir( szDir ); 763 | } 764 | } 765 | 766 | // for void speedBench() 767 | #include 768 | ArduinoOutStream cout(Serial); 769 | File file; 770 | 771 | // File size in bytes. 772 | const uint32_t FILE_SIZE = 16 * 1024; 773 | 774 | // Set SKIP_FIRST_LATENCY true if the first read/write to the SD can 775 | // be avoid by writing a file header or reading the first record. 776 | const bool SKIP_FIRST_LATENCY = true; 777 | 778 | // Size of read/write. 779 | const size_t BUF_SIZE = 2048; 780 | 781 | // Write pass count. 782 | const uint8_t WRITE_COUNT = 5; 783 | 784 | // Read pass count. 785 | const uint8_t READ_COUNT = 5; 786 | 787 | //Block size for qspi 788 | #define MYBLKSIZE 2048 // 2048 789 | 790 | // Insure 4-byte alignment. 791 | uint32_t buf32[(BUF_SIZE + 3) / 4]; 792 | uint8_t* bufA32 = (uint8_t*)buf32; 793 | 794 | //Number of random reads 795 | #define randomReads 1 796 | 797 | void speedBench() { 798 | File file; 799 | float s; 800 | uint32_t t; 801 | uint32_t maxLatency; 802 | uint32_t minLatency; 803 | uint32_t totalLatency; 804 | bool skipLatency; 805 | 806 | myfs.remove("bench.dat"); 807 | //for(uint8_t cnt=0; cnt < 10; cnt++) { 808 | 809 | // fill buf with known data 810 | if (BUF_SIZE > 1) { 811 | for (size_t i = 0; i < (BUF_SIZE - 2); i++) { 812 | bufA32[i] = 'A' + (i % 26); 813 | } 814 | bufA32[BUF_SIZE - 2] = '\r'; 815 | } 816 | bufA32[BUF_SIZE - 1] = '\n'; 817 | 818 | Serial.printf("%s Disk Stats:", szDiskMem ); 819 | Serial.printf("Bytes Used: %llu, Bytes Total:%llu\n", myfs.usedSize(), myfs.totalSize()); 820 | Serial.printf("%s Benchmark:\n", szDiskMem ); 821 | cout << F("FILE_SIZE = ") << FILE_SIZE << endl; 822 | cout << F("BUF_SIZE = ") << BUF_SIZE << F(" bytes\n"); 823 | cout << F("Starting write test, please wait.") << endl << endl; 824 | 825 | // do write test 826 | uint32_t n = FILE_SIZE / BUF_SIZE; 827 | cout << F("write speed and latency") << endl; 828 | cout << F("speed,max,min,avg") << endl; 829 | cout << F("KB/Sec,usec,usec,usec") << endl; 830 | 831 | // open or create file - truncate existing file. 832 | file = myfs.open("bench.dat", FILE_WRITE); 833 | 834 | for (uint8_t nTest = 0; nTest < WRITE_COUNT; nTest++) { 835 | file.seek(0); 836 | 837 | maxLatency = 0; 838 | minLatency = 9999999; 839 | totalLatency = 0; 840 | skipLatency = SKIP_FIRST_LATENCY; 841 | t = millis(); 842 | 843 | for (uint32_t i = 0; i < n; i++) { 844 | uint32_t m = micros(); 845 | if (file.write(bufA32, BUF_SIZE) != BUF_SIZE) { 846 | Serial.println("write failed"); 847 | } 848 | m = micros() - m; 849 | totalLatency += m; 850 | if (skipLatency) { 851 | // Wait until first write to SD, not just a copy to the cache. 852 | skipLatency = file.position() < 512; 853 | } else { 854 | if (maxLatency < m) { 855 | maxLatency = m; 856 | } 857 | if (minLatency > m) { 858 | minLatency = m; 859 | } 860 | } 861 | } 862 | 863 | t = millis() - t; 864 | s = file.size(); 865 | cout << s / t << ',' << maxLatency << ',' << minLatency; 866 | cout << ',' << totalLatency / n << endl; 867 | } 868 | cout << endl << F("Starting sequential read test, please wait.") << endl; 869 | cout << endl << F("read speed and latency") << endl; 870 | cout << F("speed,max,min,avg") << endl; 871 | cout << F("KB/Sec,usec,usec,usec") << endl; 872 | 873 | // do read test 874 | for (uint8_t nTest = 0; nTest < READ_COUNT; nTest++) { 875 | file.seek(0); 876 | maxLatency = 0; 877 | minLatency = 9999999; 878 | totalLatency = 0; 879 | skipLatency = SKIP_FIRST_LATENCY; 880 | t = micros(); 881 | for (uint32_t i = 0; i < n; i++) { 882 | bufA32[BUF_SIZE - 1] = 0; 883 | uint32_t m = micros(); 884 | int32_t nr = file.read(bufA32, BUF_SIZE); 885 | if (nr != BUF_SIZE) { 886 | Serial.println("read failed"); 887 | } 888 | m = micros() - m; 889 | totalLatency += m; 890 | if (bufA32[BUF_SIZE - 1] != '\n') { 891 | Serial.println("data check error"); 892 | } 893 | if (skipLatency) { 894 | skipLatency = false; 895 | } else { 896 | if (maxLatency < m) { 897 | maxLatency = m; 898 | } 899 | if (minLatency > m) { 900 | minLatency = m; 901 | } 902 | } 903 | } 904 | 905 | s = file.size(); 906 | 907 | t = micros() - t; 908 | cout << s * 1000 / t << ',' << maxLatency << ',' << minLatency; 909 | cout << ',' << totalLatency / n << endl; 910 | } 911 | 912 | cout << endl << F("Done") << endl; 913 | 914 | cout << endl << F("Starting random read test, please wait.") << endl; 915 | 916 | Serial.printf("Number of random reads: %d\n", randomReads); 917 | Serial.printf("Number of blocks: %d\n", n); 918 | 919 | cout << endl << F("read speed and latency") << endl; 920 | cout << F("speed,max,min,avg") << endl; 921 | 922 | // do read test 923 | for (uint8_t nTest = 0; nTest < READ_COUNT; nTest++) { 924 | file.seek(0); 925 | maxLatency = 0; 926 | minLatency = 0; 927 | totalLatency = 0; 928 | skipLatency = SKIP_FIRST_LATENCY; 929 | t = micros(); 930 | for (uint32_t i = 0; i < randomReads; i++) { 931 | bufA32[BUF_SIZE - 1] = 0; 932 | uint32_t m = micros(); 933 | uint32_t block_pos = random(0, (n - 1)); 934 | uint32_t random_pos = block_pos * MYBLKSIZE; 935 | cout << "Position (bytes), Block: " << random_pos << ", "; 936 | cout << block_pos << endl; 937 | uint32_t startCNT = ARM_DWT_CYCCNT; 938 | file.seek(random_pos); 939 | int32_t nr = file.read(bufA32, BUF_SIZE); 940 | uint32_t endCNT = ARM_DWT_CYCCNT; 941 | cout << F("Read Time (ARM Cycle Delta): ") << endCNT - startCNT << endl; 942 | if (nr != BUF_SIZE) { 943 | Serial.println("read failed"); 944 | } 945 | m = micros() - m; 946 | totalLatency += m; 947 | if (bufA32[BUF_SIZE - 1] != '\n') { 948 | Serial.println("data check error"); 949 | } 950 | if (skipLatency) { 951 | skipLatency = false; 952 | } else { 953 | if (maxLatency < m) { 954 | maxLatency = m; 955 | } 956 | if (minLatency > m) { 957 | minLatency = m; 958 | } 959 | } 960 | } 961 | 962 | s = file.size(); 963 | 964 | 965 | t = micros() - t; 966 | cout << F("KB/Sec,usec,usec,usec") << endl; 967 | cout << s * 1000 / t << ',' << maxLatency << ',' << minLatency; 968 | cout << ',' << totalLatency / n << endl; 969 | } 970 | cout << endl << F("Done") << endl; 971 | 972 | file.close(); 973 | myfs.remove("bench.dat"); 974 | 975 | } 976 | -------------------------------------------------------------------------------- /examples/Write_Speed_Test/Write_Speed_Test.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Sustained write speed test 3 | 4 | This program repeated writes 512K of random data to a flash memory chip, 5 | while measuring the write speed. A blank chip will write faster than 6 | one filled with old data, where sectors must be erased. 7 | 8 | The main purpose of this example is to verify sustained write performance 9 | is fast enough for USB MTP. Microsoft Windows requires at least 10 | 87370 bytes/sec speed to avoid timeout and cancel of MTP SendObject. 11 | https://forum.pjrc.com/threads/68139?p=295294&viewfull=1#post295294 12 | 13 | This example code is in the public domain. 14 | */ 15 | 16 | #include 17 | #include 18 | 19 | LittleFS_SPIFlash myfs; 20 | 21 | #define chipSelect 6 // use pin 6 for access flash on audio or prop shield 22 | 23 | void setup() { 24 | Entropy.Initialize(); 25 | Serial.begin(9600); 26 | while (!Serial) ; // wait for Arduino Serial Monitor 27 | Serial.println("LittleFS Sustained Write Speed Test"); 28 | if (!myfs.begin(chipSelect, SPI)) { 29 | Serial.printf("Error starting %s\n", "SPI FLASH"); 30 | while (1) ; // stop here 31 | } 32 | Serial.printf("Volume size %d MByte\n", myfs.totalSize() / 1048576); 33 | } 34 | 35 | uint64_t total_bytes_written = 0; 36 | 37 | void loop() { 38 | unsigned long buf[1024]; 39 | File myfile = myfs.open("WriteSpeedTest.bin", FILE_WRITE_BEGIN); 40 | if (myfile) { 41 | const int num_write = 128; 42 | Serial.printf("Writing %d byte file... ", num_write * 4096); 43 | randomSeed(Entropy.random()); 44 | elapsedMillis t=0; 45 | for (int n=0; n < num_write; n++) { 46 | for (int i=0; i<1024; i++) buf[i] = random(); 47 | myfile.write(buf, 4096); 48 | } 49 | myfile.close(); 50 | int ms = t; 51 | total_bytes_written = total_bytes_written + num_write * 4096; 52 | Serial.printf(" %d ms, bandwidth = %d bytes/sec", ms, num_write * 4096 * 1000 / ms); 53 | myfs.remove("WriteSpeedTest.bin"); 54 | } 55 | Serial.println(); 56 | delay(2000); 57 | /* Do not write forever, possibly reducing the chip's lifespan */ 58 | if (total_bytes_written >= myfs.totalSize() * 100) { 59 | Serial.println("End test, entire flash has been written 100 times"); 60 | while (1) ; // stop here 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | LittleFS KEYWORD2 2 | LittleFS_RAM KEYWORD1 3 | LittleFS_SPIFlash KEYWORD1 4 | LittleFS_QSPIFlash KEYWORD1 5 | LittleFS_Program KEYWORD1 6 | LittleFS_SPINAND KEYWORD1 7 | LittleFS_QPINAND KEYWORD1 8 | LittleFS_QSPI KEYWORD1 9 | LittleFS_SPI KEYWORD1 10 | LittleFS_SPIFram 11 | quickFormat KEYWORD2 12 | lowLevelFormat KEYWORD2 13 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=LittleFS 2 | version=1.0.0 3 | author=Paul Stoffregen 4 | maintainer=Paul Stoffregen 5 | sentence=LittleFS for Teensy 6 | paragraph=Access flash memory chips as a filesystem, build in top of ARM's littlefs. 7 | category=Data Storage 8 | url=https://github.com/PaulStoffregen/LittleFS 9 | architectures=* 10 | -------------------------------------------------------------------------------- /src/littlefs/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, Arm Limited. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, 4 | are permitted provided that the following conditions are met: 5 | 6 | - Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | - Redistributions in binary form must reproduce the above copyright notice, this 9 | list of conditions and the following disclaimer in the documentation and/or 10 | other materials provided with the distribution. 11 | - Neither the name of ARM nor the names of its contributors may be used to 12 | endorse or promote products derived from this software without specific prior 13 | written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 22 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /src/littlefs/README.md: -------------------------------------------------------------------------------- 1 | ## littlefs 2 | 3 | A little fail-safe filesystem designed for microcontrollers. 4 | 5 | ``` 6 | | | | .---._____ 7 | .-----. | | 8 | --|o |---| littlefs | 9 | --| |---| | 10 | '-----' '----------' 11 | | | | 12 | ``` 13 | 14 | **Power-loss resilience** - littlefs is designed to handle random power 15 | failures. All file operations have strong copy-on-write guarantees and if 16 | power is lost the filesystem will fall back to the last known good state. 17 | 18 | **Dynamic wear leveling** - littlefs is designed with flash in mind, and 19 | provides wear leveling over dynamic blocks. Additionally, littlefs can 20 | detect bad blocks and work around them. 21 | 22 | **Bounded RAM/ROM** - littlefs is designed to work with a small amount of 23 | memory. RAM usage is strictly bounded, which means RAM consumption does not 24 | change as the filesystem grows. The filesystem contains no unbounded 25 | recursion and dynamic memory is limited to configurable buffers that can be 26 | provided statically. 27 | 28 | ## Example 29 | 30 | Here's a simple example that updates a file named `boot_count` every time 31 | main runs. The program can be interrupted at any time without losing track 32 | of how many times it has been booted and without corrupting the filesystem: 33 | 34 | ``` c 35 | #include "lfs.h" 36 | 37 | // variables used by the filesystem 38 | lfs_t lfs; 39 | lfs_file_t file; 40 | 41 | // configuration of the filesystem is provided by this struct 42 | const struct lfs_config cfg = { 43 | // block device operations 44 | .read = user_provided_block_device_read, 45 | .prog = user_provided_block_device_prog, 46 | .erase = user_provided_block_device_erase, 47 | .sync = user_provided_block_device_sync, 48 | 49 | // block device configuration 50 | .read_size = 16, 51 | .prog_size = 16, 52 | .block_size = 4096, 53 | .block_count = 128, 54 | .cache_size = 16, 55 | .lookahead_size = 16, 56 | .block_cycles = 500, 57 | }; 58 | 59 | // entry point 60 | int main(void) { 61 | // mount the filesystem 62 | int err = lfs_mount(&lfs, &cfg); 63 | 64 | // reformat if we can't mount the filesystem 65 | // this should only happen on the first boot 66 | if (err) { 67 | lfs_format(&lfs, &cfg); 68 | lfs_mount(&lfs, &cfg); 69 | } 70 | 71 | // read current count 72 | uint32_t boot_count = 0; 73 | lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT); 74 | lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count)); 75 | 76 | // update boot count 77 | boot_count += 1; 78 | lfs_file_rewind(&lfs, &file); 79 | lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count)); 80 | 81 | // remember the storage is not updated until the file is closed successfully 82 | lfs_file_close(&lfs, &file); 83 | 84 | // release any resources we were using 85 | lfs_unmount(&lfs); 86 | 87 | // print the boot count 88 | printf("boot_count: %d\n", boot_count); 89 | } 90 | ``` 91 | 92 | ## Usage 93 | 94 | Detailed documentation (or at least as much detail as is currently available) 95 | can be found in the comments in [lfs.h](lfs.h). 96 | 97 | littlefs takes in a configuration structure that defines how the filesystem 98 | operates. The configuration struct provides the filesystem with the block 99 | device operations and dimensions, tweakable parameters that tradeoff memory 100 | usage for performance, and optional static buffers if the user wants to avoid 101 | dynamic memory. 102 | 103 | The state of the littlefs is stored in the `lfs_t` type which is left up 104 | to the user to allocate, allowing multiple filesystems to be in use 105 | simultaneously. With the `lfs_t` and configuration struct, a user can 106 | format a block device or mount the filesystem. 107 | 108 | Once mounted, the littlefs provides a full set of POSIX-like file and 109 | directory functions, with the deviation that the allocation of filesystem 110 | structures must be provided by the user. 111 | 112 | All POSIX operations, such as remove and rename, are atomic, even in event 113 | of power-loss. Additionally, file updates are not actually committed to 114 | the filesystem until sync or close is called on the file. 115 | 116 | ## Other notes 117 | 118 | Littlefs is written in C, and specifically should compile with any compiler 119 | that conforms to the `C99` standard. 120 | 121 | All littlefs calls have the potential to return a negative error code. The 122 | errors can be either one of those found in the `enum lfs_error` in 123 | [lfs.h](lfs.h), or an error returned by the user's block device operations. 124 | 125 | In the configuration struct, the `prog` and `erase` function provided by the 126 | user may return a `LFS_ERR_CORRUPT` error if the implementation already can 127 | detect corrupt blocks. However, the wear leveling does not depend on the return 128 | code of these functions, instead all data is read back and checked for 129 | integrity. 130 | 131 | If your storage caches writes, make sure that the provided `sync` function 132 | flushes all the data to memory and ensures that the next read fetches the data 133 | from memory, otherwise data integrity can not be guaranteed. If the `write` 134 | function does not perform caching, and therefore each `read` or `write` call 135 | hits the memory, the `sync` function can simply return 0. 136 | 137 | ## Design 138 | 139 | At a high level, littlefs is a block based filesystem that uses small logs to 140 | store metadata and larger copy-on-write (COW) structures to store file data. 141 | 142 | In littlefs, these ingredients form a sort of two-layered cake, with the small 143 | logs (called metadata pairs) providing fast updates to metadata anywhere on 144 | storage, while the COW structures store file data compactly and without any 145 | wear amplification cost. 146 | 147 | Both of these data structures are built out of blocks, which are fed by a 148 | common block allocator. By limiting the number of erases allowed on a block 149 | per allocation, the allocator provides dynamic wear leveling over the entire 150 | filesystem. 151 | 152 | ``` 153 | root 154 | .--------.--------. 155 | | A'| B'| | 156 | | | |-> | 157 | | | | | 158 | '--------'--------' 159 | .----' '--------------. 160 | A v B v 161 | .--------.--------. .--------.--------. 162 | | C'| D'| | | E'|new| | 163 | | | |-> | | | E'|-> | 164 | | | | | | | | | 165 | '--------'--------' '--------'--------' 166 | .-' '--. | '------------------. 167 | v v .-' v 168 | .--------. .--------. v .--------. 169 | | C | | D | .--------. write | new E | 170 | | | | | | E | ==> | | 171 | | | | | | | | | 172 | '--------' '--------' | | '--------' 173 | '--------' .-' | 174 | .-' '-. .-------------|------' 175 | v v v v 176 | .--------. .--------. .--------. 177 | | F | | G | | new F | 178 | | | | | | | 179 | | | | | | | 180 | '--------' '--------' '--------' 181 | ``` 182 | 183 | More details on how littlefs works can be found in [DESIGN.md](DESIGN.md) and 184 | [SPEC.md](SPEC.md). 185 | 186 | - [DESIGN.md](DESIGN.md) - A fully detailed dive into how littlefs works. 187 | I would suggest reading it as the tradeoffs at work are quite interesting. 188 | 189 | - [SPEC.md](SPEC.md) - The on-disk specification of littlefs with all the 190 | nitty-gritty details. May be useful for tooling development. 191 | 192 | ## Testing 193 | 194 | The littlefs comes with a test suite designed to run on a PC using the 195 | [emulated block device](bd/lfs_testbd.h) found in the `bd` directory. 196 | The tests assume a Linux environment and can be started with make: 197 | 198 | ``` bash 199 | make test 200 | ``` 201 | 202 | ## License 203 | 204 | The littlefs is provided under the [BSD-3-Clause] license. See 205 | [LICENSE.md](LICENSE.md) for more information. Contributions to this project 206 | are accepted under the same license. 207 | 208 | Individual files contain the following tag instead of the full license text. 209 | 210 | SPDX-License-Identifier: BSD-3-Clause 211 | 212 | This enables machine processing of license information based on the SPDX 213 | License Identifiers that are here available: http://spdx.org/licenses/ 214 | 215 | ## Related projects 216 | 217 | - [littlefs-fuse] - A [FUSE] wrapper for littlefs. The project allows you to 218 | mount littlefs directly on a Linux machine. Can be useful for debugging 219 | littlefs if you have an SD card handy. 220 | 221 | - [littlefs-js] - A javascript wrapper for littlefs. I'm not sure why you would 222 | want this, but it is handy for demos. You can see it in action 223 | [here][littlefs-js-demo]. 224 | 225 | - [littlefs-python] - A Python wrapper for littlefs. The project allows you 226 | to create images of the filesystem on your PC. Check if littlefs will fit 227 | your needs, create images for a later download to the target memory or 228 | inspect the content of a binary image of the target memory. 229 | 230 | - [mklfs] - A command line tool built by the [Lua RTOS] guys for making 231 | littlefs images from a host PC. Supports Windows, Mac OS, and Linux. 232 | 233 | - [Mbed OS] - The easiest way to get started with littlefs is to jump into Mbed 234 | which already has block device drivers for most forms of embedded storage. 235 | littlefs is available in Mbed OS as the [LittleFileSystem] class. 236 | 237 | - [SPIFFS] - Another excellent embedded filesystem for NOR flash. As a more 238 | traditional logging filesystem with full static wear-leveling, SPIFFS will 239 | likely outperform littlefs on small memories such as the internal flash on 240 | microcontrollers. 241 | 242 | - [Dhara] - An interesting NAND flash translation layer designed for small 243 | MCUs. It offers static wear-leveling and power-resilience with only a fixed 244 | _O(|address|)_ pointer structure stored on each block and in RAM. 245 | 246 | 247 | [BSD-3-Clause]: https://spdx.org/licenses/BSD-3-Clause.html 248 | [littlefs-fuse]: https://github.com/geky/littlefs-fuse 249 | [FUSE]: https://github.com/libfuse/libfuse 250 | [littlefs-js]: https://github.com/geky/littlefs-js 251 | [littlefs-js-demo]:http://littlefs.geky.net/demo.html 252 | [mklfs]: https://github.com/whitecatboard/Lua-RTOS-ESP32/tree/master/components/mklfs/src 253 | [Lua RTOS]: https://github.com/whitecatboard/Lua-RTOS-ESP32 254 | [Mbed OS]: https://github.com/armmbed/mbed-os 255 | [LittleFileSystem]: https://os.mbed.com/docs/mbed-os/v5.12/apis/littlefilesystem.html 256 | [SPIFFS]: https://github.com/pellepl/spiffs 257 | [Dhara]: https://github.com/dlbeer/dhara 258 | [littlefs-python]: https://pypi.org/project/littlefs-python/ 259 | -------------------------------------------------------------------------------- /src/littlefs/lfs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The little filesystem 3 | * 4 | * Copyright (c) 2017, Arm Limited. All rights reserved. 5 | * SPDX-License-Identifier: BSD-3-Clause 6 | */ 7 | #ifndef LFS_H 8 | #define LFS_H 9 | 10 | #include 11 | #include 12 | #include "lfs_util.h" 13 | 14 | #ifdef __cplusplus 15 | extern "C" 16 | { 17 | #endif 18 | 19 | 20 | /// Version info /// 21 | 22 | // Software library version 23 | // Major (top-nibble), incremented on backwards incompatible changes 24 | // Minor (bottom-nibble), incremented on feature additions 25 | #define LFS_VERSION 0x00020004 26 | #define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16)) 27 | #define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0)) 28 | 29 | // Version of On-disk data structures 30 | // Major (top-nibble), incremented on backwards incompatible changes 31 | // Minor (bottom-nibble), incremented on feature additions 32 | #define LFS_DISK_VERSION 0x00020000 33 | #define LFS_DISK_VERSION_MAJOR (0xffff & (LFS_DISK_VERSION >> 16)) 34 | #define LFS_DISK_VERSION_MINOR (0xffff & (LFS_DISK_VERSION >> 0)) 35 | 36 | 37 | /// Definitions /// 38 | 39 | // Type definitions 40 | typedef uint32_t lfs_size_t; 41 | typedef uint32_t lfs_off_t; 42 | 43 | typedef int32_t lfs_ssize_t; 44 | typedef int32_t lfs_soff_t; 45 | 46 | typedef uint32_t lfs_block_t; 47 | 48 | // Maximum name size in bytes, may be redefined to reduce the size of the 49 | // info struct. Limited to <= 1022. Stored in superblock and must be 50 | // respected by other littlefs drivers. 51 | #ifndef LFS_NAME_MAX 52 | #define LFS_NAME_MAX 255 53 | //#define LFS_NAME_MAX 39 54 | #endif 55 | 56 | // Maximum size of a file in bytes, may be redefined to limit to support other 57 | // drivers. Limited on disk to <= 4294967296. However, above 2147483647 the 58 | // functions lfs_file_seek, lfs_file_size, and lfs_file_tell will return 59 | // incorrect values due to using signed integers. Stored in superblock and 60 | // must be respected by other littlefs drivers. 61 | #ifndef LFS_FILE_MAX 62 | #define LFS_FILE_MAX 2147483647 63 | #endif 64 | 65 | // Maximum size of custom attributes in bytes, may be redefined, but there is 66 | // no real benefit to using a smaller LFS_ATTR_MAX. Limited to <= 1022. 67 | #ifndef LFS_ATTR_MAX 68 | #define LFS_ATTR_MAX 1022 69 | #endif 70 | 71 | // Possible error codes, these are negative to allow 72 | // valid positive return values 73 | enum lfs_error { 74 | LFS_ERR_OK = 0, // No error 75 | LFS_ERR_IO = -5, // Error during device operation 76 | LFS_ERR_CORRUPT = -84, // Corrupted 77 | LFS_ERR_NOENT = -2, // No directory entry 78 | LFS_ERR_EXIST = -17, // Entry already exists 79 | LFS_ERR_NOTDIR = -20, // Entry is not a dir 80 | LFS_ERR_ISDIR = -21, // Entry is a dir 81 | LFS_ERR_NOTEMPTY = -39, // Dir is not empty 82 | LFS_ERR_BADF = -9, // Bad file number 83 | LFS_ERR_FBIG = -27, // File too large 84 | LFS_ERR_INVAL = -22, // Invalid parameter 85 | LFS_ERR_NOSPC = -28, // No space left on device 86 | LFS_ERR_NOMEM = -12, // No more memory available 87 | LFS_ERR_NOATTR = -61, // No data/attr available 88 | LFS_ERR_NAMETOOLONG = -36, // File name too long 89 | }; 90 | 91 | // File types 92 | enum lfs_type { 93 | // file types 94 | LFS_TYPE_REG = 0x001, 95 | LFS_TYPE_DIR = 0x002, 96 | 97 | // internally used types 98 | LFS_TYPE_SPLICE = 0x400, 99 | LFS_TYPE_NAME = 0x000, 100 | LFS_TYPE_STRUCT = 0x200, 101 | LFS_TYPE_USERATTR = 0x300, 102 | LFS_TYPE_FROM = 0x100, 103 | LFS_TYPE_TAIL = 0x600, 104 | LFS_TYPE_GLOBALS = 0x700, 105 | LFS_TYPE_CRC = 0x500, 106 | 107 | // internally used type specializations 108 | LFS_TYPE_CREATE = 0x401, 109 | LFS_TYPE_DELETE = 0x4ff, 110 | LFS_TYPE_SUPERBLOCK = 0x0ff, 111 | LFS_TYPE_DIRSTRUCT = 0x200, 112 | LFS_TYPE_CTZSTRUCT = 0x202, 113 | LFS_TYPE_INLINESTRUCT = 0x201, 114 | LFS_TYPE_SOFTTAIL = 0x600, 115 | LFS_TYPE_HARDTAIL = 0x601, 116 | LFS_TYPE_MOVESTATE = 0x7ff, 117 | 118 | // internal chip sources 119 | LFS_FROM_NOOP = 0x000, 120 | LFS_FROM_MOVE = 0x101, 121 | LFS_FROM_USERATTRS = 0x102, 122 | }; 123 | 124 | // File open flags 125 | enum lfs_open_flags { 126 | // open flags 127 | LFS_O_RDONLY = 1, // Open a file as read only 128 | #ifndef LFS_READONLY 129 | LFS_O_WRONLY = 2, // Open a file as write only 130 | LFS_O_RDWR = 3, // Open a file as read and write 131 | LFS_O_CREAT = 0x0100, // Create a file if it does not exist 132 | LFS_O_EXCL = 0x0200, // Fail if a file already exists 133 | LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size 134 | LFS_O_APPEND = 0x0800, // Move to end of file on every write 135 | #endif 136 | 137 | // internally used flags 138 | #ifndef LFS_READONLY 139 | LFS_F_DIRTY = 0x010000, // File does not match storage 140 | LFS_F_WRITING = 0x020000, // File has been written since last flush 141 | #endif 142 | LFS_F_READING = 0x040000, // File has been read since last flush 143 | #ifndef LFS_READONLY 144 | LFS_F_ERRED = 0x080000, // An error occurred during write 145 | #endif 146 | LFS_F_INLINE = 0x100000, // Currently inlined in directory entry 147 | }; 148 | 149 | // File seek flags 150 | enum lfs_whence_flags { 151 | LFS_SEEK_SET = 0, // Seek relative to an absolute position 152 | LFS_SEEK_CUR = 1, // Seek relative to the current file position 153 | LFS_SEEK_END = 2, // Seek relative to the end of the file 154 | }; 155 | 156 | 157 | // Configuration provided during initialization of the littlefs 158 | struct lfs_config { 159 | // Opaque user provided context that can be used to pass 160 | // information to the block device operations 161 | void *context; 162 | 163 | // Read a region in a block. Negative error codes are propogated 164 | // to the user. 165 | int (*read)(const struct lfs_config *c, lfs_block_t block, 166 | lfs_off_t off, void *buffer, lfs_size_t size); 167 | 168 | // Program a region in a block. The block must have previously 169 | // been erased. Negative error codes are propogated to the user. 170 | // May return LFS_ERR_CORRUPT if the block should be considered bad. 171 | int (*prog)(const struct lfs_config *c, lfs_block_t block, 172 | lfs_off_t off, const void *buffer, lfs_size_t size); 173 | 174 | // Erase a block. A block must be erased before being programmed. 175 | // The state of an erased block is undefined. Negative error codes 176 | // are propogated to the user. 177 | // May return LFS_ERR_CORRUPT if the block should be considered bad. 178 | int (*erase)(const struct lfs_config *c, lfs_block_t block); 179 | 180 | // Sync the state of the underlying block device. Negative error codes 181 | // are propogated to the user. 182 | int (*sync)(const struct lfs_config *c); 183 | 184 | #ifdef LFS_THREADSAFE 185 | // Lock the underlying block device. Negative error codes 186 | // are propogated to the user. 187 | int (*lock)(const struct lfs_config *c); 188 | 189 | // Unlock the underlying block device. Negative error codes 190 | // are propogated to the user. 191 | int (*unlock)(const struct lfs_config *c); 192 | #endif 193 | 194 | // Minimum size of a block read. All read operations will be a 195 | // multiple of this value. 196 | lfs_size_t read_size; 197 | 198 | // Minimum size of a block program. All program operations will be a 199 | // multiple of this value. 200 | lfs_size_t prog_size; 201 | 202 | // Size of an erasable block. This does not impact ram consumption and 203 | // may be larger than the physical erase size. However, non-inlined files 204 | // take up at minimum one block. Must be a multiple of the read 205 | // and program sizes. 206 | lfs_size_t block_size; 207 | 208 | // Number of erasable blocks on the device. 209 | lfs_size_t block_count; 210 | 211 | // Number of erase cycles before littlefs evicts metadata logs and moves 212 | // the metadata to another block. Suggested values are in the 213 | // range 100-1000, with large values having better performance at the cost 214 | // of less consistent wear distribution. 215 | // 216 | // Set to -1 to disable block-level wear-leveling. 217 | int32_t block_cycles; 218 | 219 | // Size of block caches. Each cache buffers a portion of a block in RAM. 220 | // The littlefs needs a read cache, a program cache, and one additional 221 | // cache per file. Larger caches can improve performance by storing more 222 | // data and reducing the number of disk accesses. Must be a multiple of 223 | // the read and program sizes, and a factor of the block size. 224 | lfs_size_t cache_size; 225 | 226 | // Size of the lookahead buffer in bytes. A larger lookahead buffer 227 | // increases the number of blocks found during an allocation pass. The 228 | // lookahead buffer is stored as a compact bitmap, so each byte of RAM 229 | // can track 8 blocks. Must be a multiple of 8. 230 | lfs_size_t lookahead_size; 231 | 232 | // Optional statically allocated read buffer. Must be cache_size. 233 | // By default lfs_malloc is used to allocate this buffer. 234 | void *read_buffer; 235 | 236 | // Optional statically allocated program buffer. Must be cache_size. 237 | // By default lfs_malloc is used to allocate this buffer. 238 | void *prog_buffer; 239 | 240 | // Optional statically allocated lookahead buffer. Must be lookahead_size 241 | // and aligned to a 32-bit boundary. By default lfs_malloc is used to 242 | // allocate this buffer. 243 | void *lookahead_buffer; 244 | 245 | // Optional upper limit on length of file names in bytes. No downside for 246 | // larger names except the size of the info struct which is controlled by 247 | // the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX when zero. Stored in 248 | // superblock and must be respected by other littlefs drivers. 249 | lfs_size_t name_max; 250 | 251 | // Optional upper limit on files in bytes. No downside for larger files 252 | // but must be <= LFS_FILE_MAX. Defaults to LFS_FILE_MAX when zero. Stored 253 | // in superblock and must be respected by other littlefs drivers. 254 | lfs_size_t file_max; 255 | 256 | // Optional upper limit on custom attributes in bytes. No downside for 257 | // larger attributes size but must be <= LFS_ATTR_MAX. Defaults to 258 | // LFS_ATTR_MAX when zero. 259 | lfs_size_t attr_max; 260 | 261 | // Optional upper limit on total space given to metadata pairs in bytes. On 262 | // devices with large blocks (e.g. 128kB) setting this to a low size (2-8kB) 263 | // can help bound the metadata compaction time. Must be <= block_size. 264 | // Defaults to block_size when zero. 265 | lfs_size_t metadata_max; 266 | }; 267 | 268 | // File info structure 269 | struct lfs_info { 270 | // Type of the file, either LFS_TYPE_REG or LFS_TYPE_DIR 271 | uint8_t type; 272 | 273 | // Size of the file, only valid for REG files. Limited to 32-bits. 274 | lfs_size_t size; 275 | 276 | // Name of the file stored as a null-terminated string. Limited to 277 | // LFS_NAME_MAX+1, which can be changed by redefining LFS_NAME_MAX to 278 | // reduce RAM. LFS_NAME_MAX is stored in superblock and must be 279 | // respected by other littlefs drivers. 280 | char name[LFS_NAME_MAX+1]; 281 | }; 282 | 283 | // Custom attribute structure, used to describe custom attributes 284 | // committed atomically during file writes. 285 | struct lfs_attr { 286 | // 8-bit type of attribute, provided by user and used to 287 | // identify the attribute 288 | uint8_t type; 289 | 290 | // Pointer to buffer containing the attribute 291 | void *buffer; 292 | 293 | // Size of attribute in bytes, limited to LFS_ATTR_MAX 294 | lfs_size_t size; 295 | }; 296 | 297 | // Optional configuration provided during lfs_file_opencfg 298 | struct lfs_file_config { 299 | // Optional statically allocated file buffer. Must be cache_size. 300 | // By default lfs_malloc is used to allocate this buffer. 301 | void *buffer; 302 | 303 | // Optional list of custom attributes related to the file. If the file 304 | // is opened with read access, these attributes will be read from disk 305 | // during the open call. If the file is opened with write access, the 306 | // attributes will be written to disk every file sync or close. This 307 | // write occurs atomically with update to the file's contents. 308 | // 309 | // Custom attributes are uniquely identified by an 8-bit type and limited 310 | // to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller 311 | // than the buffer, it will be padded with zeros. If the stored attribute 312 | // is larger, then it will be silently truncated. If the attribute is not 313 | // found, it will be created implicitly. 314 | struct lfs_attr *attrs; 315 | 316 | // Number of custom attributes in the list 317 | lfs_size_t attr_count; 318 | }; 319 | 320 | 321 | /// internal littlefs data structures /// 322 | typedef struct lfs_cache { 323 | lfs_block_t block; 324 | lfs_off_t off; 325 | lfs_size_t size; 326 | uint8_t *buffer; 327 | } lfs_cache_t; 328 | 329 | typedef struct lfs_mdir { 330 | lfs_block_t pair[2]; 331 | uint32_t rev; 332 | lfs_off_t off; 333 | uint32_t etag; 334 | uint16_t count; 335 | bool erased; 336 | bool split; 337 | lfs_block_t tail[2]; 338 | } lfs_mdir_t; 339 | 340 | // littlefs directory type 341 | typedef struct lfs_dir { 342 | struct lfs_dir *next; 343 | uint16_t id; 344 | uint8_t type; 345 | lfs_mdir_t m; 346 | 347 | lfs_off_t pos; 348 | lfs_block_t head[2]; 349 | } lfs_dir_t; 350 | 351 | // littlefs file type 352 | typedef struct lfs_file { 353 | struct lfs_file *next; 354 | uint16_t id; 355 | uint8_t type; 356 | lfs_mdir_t m; 357 | 358 | struct lfs_ctz { 359 | lfs_block_t head; 360 | lfs_size_t size; 361 | } ctz; 362 | 363 | uint32_t flags; 364 | lfs_off_t pos; 365 | lfs_block_t block; 366 | lfs_off_t off; 367 | lfs_cache_t cache; 368 | 369 | const struct lfs_file_config *cfg; 370 | } lfs_file_t; 371 | 372 | typedef struct lfs_superblock { 373 | uint32_t version; 374 | lfs_size_t block_size; 375 | lfs_size_t block_count; 376 | lfs_size_t name_max; 377 | lfs_size_t file_max; 378 | lfs_size_t attr_max; 379 | } lfs_superblock_t; 380 | 381 | typedef struct lfs_gstate { 382 | uint32_t tag; 383 | lfs_block_t pair[2]; 384 | } lfs_gstate_t; 385 | 386 | // The littlefs filesystem type 387 | typedef struct lfs { 388 | lfs_cache_t rcache; 389 | lfs_cache_t pcache; 390 | 391 | lfs_block_t root[2]; 392 | struct lfs_mlist { 393 | struct lfs_mlist *next; 394 | uint16_t id; 395 | uint8_t type; 396 | lfs_mdir_t m; 397 | } *mlist; 398 | uint32_t seed; 399 | 400 | lfs_gstate_t gstate; 401 | lfs_gstate_t gdisk; 402 | lfs_gstate_t gdelta; 403 | 404 | struct lfs_free { 405 | lfs_block_t off; 406 | lfs_block_t size; 407 | lfs_block_t i; 408 | lfs_block_t ack; 409 | uint32_t *buffer; 410 | } free; 411 | 412 | const struct lfs_config *cfg; 413 | lfs_size_t name_max; 414 | lfs_size_t file_max; 415 | lfs_size_t attr_max; 416 | 417 | #ifdef LFS_MIGRATE 418 | struct lfs1 *lfs1; 419 | #endif 420 | } lfs_t; 421 | 422 | 423 | /// Filesystem functions /// 424 | 425 | #ifndef LFS_READONLY 426 | // Format a block device with the littlefs 427 | // 428 | // Requires a littlefs object and config struct. This clobbers the littlefs 429 | // object, and does not leave the filesystem mounted. The config struct must 430 | // be zeroed for defaults and backwards compatibility. 431 | // 432 | // Returns a negative error code on failure. 433 | int lfs_format(lfs_t *lfs, const struct lfs_config *config); 434 | #endif 435 | 436 | // Mounts a littlefs 437 | // 438 | // Requires a littlefs object and config struct. Multiple filesystems 439 | // may be mounted simultaneously with multiple littlefs objects. Both 440 | // lfs and config must be allocated while mounted. The config struct must 441 | // be zeroed for defaults and backwards compatibility. 442 | // 443 | // Returns a negative error code on failure. 444 | int lfs_mount(lfs_t *lfs, const struct lfs_config *config); 445 | 446 | // Unmounts a littlefs 447 | // 448 | // Does nothing besides releasing any allocated resources. 449 | // Returns a negative error code on failure. 450 | int lfs_unmount(lfs_t *lfs); 451 | 452 | /// General operations /// 453 | 454 | #ifndef LFS_READONLY 455 | // Removes a file or directory 456 | // 457 | // If removing a directory, the directory must be empty. 458 | // Returns a negative error code on failure. 459 | int lfs_remove(lfs_t *lfs, const char *path); 460 | #endif 461 | 462 | #ifndef LFS_READONLY 463 | // Rename or move a file or directory 464 | // 465 | // If the destination exists, it must match the source in type. 466 | // If the destination is a directory, the directory must be empty. 467 | // 468 | // Returns a negative error code on failure. 469 | int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath); 470 | #endif 471 | 472 | // Find info about a file or directory 473 | // 474 | // Fills out the info structure, based on the specified file or directory. 475 | // Returns a negative error code on failure. 476 | int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info); 477 | 478 | // Get a custom attribute 479 | // 480 | // Custom attributes are uniquely identified by an 8-bit type and limited 481 | // to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller than 482 | // the buffer, it will be padded with zeros. If the stored attribute is larger, 483 | // then it will be silently truncated. If no attribute is found, the error 484 | // LFS_ERR_NOATTR is returned and the buffer is filled with zeros. 485 | // 486 | // Returns the size of the attribute, or a negative error code on failure. 487 | // Note, the returned size is the size of the attribute on disk, irrespective 488 | // of the size of the buffer. This can be used to dynamically allocate a buffer 489 | // or check for existance. 490 | lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path, 491 | uint8_t type, void *buffer, lfs_size_t size); 492 | 493 | #ifndef LFS_READONLY 494 | // Set custom attributes 495 | // 496 | // Custom attributes are uniquely identified by an 8-bit type and limited 497 | // to LFS_ATTR_MAX bytes. If an attribute is not found, it will be 498 | // implicitly created. 499 | // 500 | // Returns a negative error code on failure. 501 | int lfs_setattr(lfs_t *lfs, const char *path, 502 | uint8_t type, const void *buffer, lfs_size_t size); 503 | #endif 504 | 505 | #ifndef LFS_READONLY 506 | // Removes a custom attribute 507 | // 508 | // If an attribute is not found, nothing happens. 509 | // 510 | // Returns a negative error code on failure. 511 | int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type); 512 | #endif 513 | 514 | 515 | /// File operations /// 516 | 517 | // Open a file 518 | // 519 | // The mode that the file is opened in is determined by the flags, which 520 | // are values from the enum lfs_open_flags that are bitwise-ored together. 521 | // 522 | // Returns a negative error code on failure. 523 | int lfs_file_open(lfs_t *lfs, lfs_file_t *file, 524 | const char *path, int flags); 525 | 526 | // Open a file with extra configuration 527 | // 528 | // The mode that the file is opened in is determined by the flags, which 529 | // are values from the enum lfs_open_flags that are bitwise-ored together. 530 | // 531 | // The config struct provides additional config options per file as described 532 | // above. The config struct must be allocated while the file is open, and the 533 | // config struct must be zeroed for defaults and backwards compatibility. 534 | // 535 | // Returns a negative error code on failure. 536 | int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, 537 | const char *path, int flags, 538 | const struct lfs_file_config *config); 539 | 540 | // Close a file 541 | // 542 | // Any pending writes are written out to storage as though 543 | // sync had been called and releases any allocated resources. 544 | // 545 | // Returns a negative error code on failure. 546 | int lfs_file_close(lfs_t *lfs, lfs_file_t *file); 547 | 548 | // Synchronize a file on storage 549 | // 550 | // Any pending writes are written out to storage. 551 | // Returns a negative error code on failure. 552 | int lfs_file_sync(lfs_t *lfs, lfs_file_t *file); 553 | 554 | // Read data from file 555 | // 556 | // Takes a buffer and size indicating where to store the read data. 557 | // Returns the number of bytes read, or a negative error code on failure. 558 | lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, 559 | void *buffer, lfs_size_t size); 560 | 561 | #ifndef LFS_READONLY 562 | // Write data to file 563 | // 564 | // Takes a buffer and size indicating the data to write. The file will not 565 | // actually be updated on the storage until either sync or close is called. 566 | // 567 | // Returns the number of bytes written, or a negative error code on failure. 568 | lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, 569 | const void *buffer, lfs_size_t size); 570 | #endif 571 | 572 | // Change the position of the file 573 | // 574 | // The change in position is determined by the offset and whence flag. 575 | // Returns the new position of the file, or a negative error code on failure. 576 | lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, 577 | lfs_soff_t off, int whence); 578 | 579 | #ifndef LFS_READONLY 580 | // Truncates the size of the file to the specified size 581 | // 582 | // Returns a negative error code on failure. 583 | int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size); 584 | #endif 585 | 586 | // Return the position of the file 587 | // 588 | // Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR) 589 | // Returns the position of the file, or a negative error code on failure. 590 | lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file); 591 | 592 | // Change the position of the file to the beginning of the file 593 | // 594 | // Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_SET) 595 | // Returns a negative error code on failure. 596 | int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file); 597 | 598 | // Return the size of the file 599 | // 600 | // Similar to lfs_file_seek(lfs, file, 0, LFS_SEEK_END) 601 | // Returns the size of the file, or a negative error code on failure. 602 | lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file); 603 | 604 | 605 | /// Directory operations /// 606 | 607 | #ifndef LFS_READONLY 608 | // Create a directory 609 | // 610 | // Returns a negative error code on failure. 611 | int lfs_mkdir(lfs_t *lfs, const char *path); 612 | #endif 613 | 614 | // Open a directory 615 | // 616 | // Once open a directory can be used with read to iterate over files. 617 | // Returns a negative error code on failure. 618 | int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path); 619 | 620 | // Close a directory 621 | // 622 | // Releases any allocated resources. 623 | // Returns a negative error code on failure. 624 | int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir); 625 | 626 | // Read an entry in the directory 627 | // 628 | // Fills out the info structure, based on the specified file or directory. 629 | // Returns a positive value on success, 0 at the end of directory, 630 | // or a negative error code on failure. 631 | int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info); 632 | 633 | // Change the position of the directory 634 | // 635 | // The new off must be a value previous returned from tell and specifies 636 | // an absolute offset in the directory seek. 637 | // 638 | // Returns a negative error code on failure. 639 | int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off); 640 | 641 | // Return the position of the directory 642 | // 643 | // The returned offset is only meant to be consumed by seek and may not make 644 | // sense, but does indicate the current position in the directory iteration. 645 | // 646 | // Returns the position of the directory, or a negative error code on failure. 647 | lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir); 648 | 649 | // Change the position of the directory to the beginning of the directory 650 | // 651 | // Returns a negative error code on failure. 652 | int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir); 653 | 654 | 655 | /// Filesystem-level filesystem operations 656 | 657 | // Finds the current size of the filesystem 658 | // 659 | // Note: Result is best effort. If files share COW structures, the returned 660 | // size may be larger than the filesystem actually is. 661 | // 662 | // Returns the number of allocated blocks, or a negative error code on failure. 663 | lfs_ssize_t lfs_fs_size(lfs_t *lfs); 664 | 665 | // Traverse through all blocks in use by the filesystem 666 | // 667 | // The provided callback will be called with each block address that is 668 | // currently in use by the filesystem. This can be used to determine which 669 | // blocks are in use or how much of the storage is available. 670 | // 671 | // Returns a negative error code on failure. 672 | int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); 673 | 674 | #ifndef LFS_READONLY 675 | #ifdef LFS_MIGRATE 676 | // Attempts to migrate a previous version of littlefs 677 | // 678 | // Behaves similarly to the lfs_format function. Attempts to mount 679 | // the previous version of littlefs and update the filesystem so it can be 680 | // mounted with the current version of littlefs. 681 | // 682 | // Requires a littlefs object and config struct. This clobbers the littlefs 683 | // object, and does not leave the filesystem mounted. The config struct must 684 | // be zeroed for defaults and backwards compatibility. 685 | // 686 | // Returns a negative error code on failure. 687 | int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg); 688 | #endif 689 | #endif 690 | 691 | 692 | #ifdef __cplusplus 693 | } /* extern "C" */ 694 | #endif 695 | 696 | #endif 697 | -------------------------------------------------------------------------------- /src/littlefs/lfs_util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lfs util functions 3 | * 4 | * Copyright (c) 2017, Arm Limited. All rights reserved. 5 | * SPDX-License-Identifier: BSD-3-Clause 6 | */ 7 | #include "lfs_util.h" 8 | 9 | // Only compile if user does not provide custom config 10 | #ifndef LFS_CONFIG 11 | 12 | 13 | // Software CRC implementation with small lookup table 14 | uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) { 15 | static const uint32_t rtable[16] = { 16 | 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 17 | 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 18 | 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 19 | 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c, 20 | }; 21 | 22 | const uint8_t *data = buffer; 23 | 24 | for (size_t i = 0; i < size; i++) { 25 | crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 0)) & 0xf]; 26 | crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 4)) & 0xf]; 27 | } 28 | 29 | return crc; 30 | } 31 | 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/littlefs/lfs_util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * lfs utility functions 3 | * 4 | * Copyright (c) 2017, Arm Limited. All rights reserved. 5 | * SPDX-License-Identifier: BSD-3-Clause 6 | */ 7 | #ifndef LFS_UTIL_H 8 | #define LFS_UTIL_H 9 | 10 | 11 | // Teensy specific use... 12 | #define LFS_NAME_MAX 255 13 | #define LFS_NO_DEBUG 14 | #define LFS_NO_WARN 15 | #define LFS_NO_ERROR 16 | #define LFS_NO_ASSERT 17 | 18 | // Users can override lfs_util.h with their own configuration by defining 19 | // LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h). 20 | // 21 | // If LFS_CONFIG is used, none of the default utils will be emitted and must be 22 | // provided by the config file. To start, I would suggest copying lfs_util.h 23 | // and modifying as needed. 24 | #ifdef LFS_CONFIG 25 | #define LFS_STRINGIZE(x) LFS_STRINGIZE2(x) 26 | #define LFS_STRINGIZE2(x) #x 27 | #include LFS_STRINGIZE(LFS_CONFIG) 28 | #else 29 | 30 | // System includes 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #ifndef LFS_NO_MALLOC 37 | #include 38 | #endif 39 | #ifndef LFS_NO_ASSERT 40 | #include 41 | #endif 42 | #if !defined(LFS_NO_DEBUG) || \ 43 | !defined(LFS_NO_WARN) || \ 44 | !defined(LFS_NO_ERROR) || \ 45 | defined(LFS_YES_TRACE) 46 | #include 47 | #endif 48 | 49 | #ifdef __cplusplus 50 | extern "C" 51 | { 52 | #endif 53 | 54 | 55 | // Macros, may be replaced by system specific wrappers. Arguments to these 56 | // macros must not have side-effects as the macros can be removed for a smaller 57 | // code footprint 58 | 59 | // Logging functions 60 | #ifndef LFS_TRACE 61 | #ifdef LFS_YES_TRACE 62 | #define LFS_TRACE_(fmt, ...) \ 63 | printf("%s:%d:trace: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) 64 | #define LFS_TRACE(...) LFS_TRACE_(__VA_ARGS__, "") 65 | #else 66 | #define LFS_TRACE(...) 67 | #endif 68 | #endif 69 | 70 | #ifndef LFS_DEBUG 71 | #ifndef LFS_NO_DEBUG 72 | #define LFS_DEBUG_(fmt, ...) \ 73 | printf("%s:%d:debug: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) 74 | #define LFS_DEBUG(...) LFS_DEBUG_(__VA_ARGS__, "") 75 | #else 76 | #define LFS_DEBUG(...) 77 | #endif 78 | #endif 79 | 80 | #ifndef LFS_WARN 81 | #ifndef LFS_NO_WARN 82 | #define LFS_WARN_(fmt, ...) \ 83 | printf("%s:%d:warn: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) 84 | #define LFS_WARN(...) LFS_WARN_(__VA_ARGS__, "") 85 | #else 86 | #define LFS_WARN(...) 87 | #endif 88 | #endif 89 | 90 | #ifndef LFS_ERROR 91 | #ifndef LFS_NO_ERROR 92 | #define LFS_ERROR_(fmt, ...) \ 93 | printf("%s:%d:error: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) 94 | #define LFS_ERROR(...) LFS_ERROR_(__VA_ARGS__, "") 95 | #else 96 | #define LFS_ERROR(...) 97 | #endif 98 | #endif 99 | 100 | // Runtime assertions 101 | #ifndef LFS_ASSERT 102 | #ifndef LFS_NO_ASSERT 103 | #define LFS_ASSERT(test) assert(test) 104 | #else 105 | #define LFS_ASSERT(test) 106 | #endif 107 | #endif 108 | 109 | 110 | // Builtin functions, these may be replaced by more efficient 111 | // toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more 112 | // expensive basic C implementation for debugging purposes 113 | 114 | // Min/max functions for unsigned 32-bit numbers 115 | static inline uint32_t lfs_max(uint32_t a, uint32_t b) { 116 | return (a > b) ? a : b; 117 | } 118 | 119 | static inline uint32_t lfs_min(uint32_t a, uint32_t b) { 120 | return (a < b) ? a : b; 121 | } 122 | 123 | // Align to nearest multiple of a size 124 | static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) { 125 | return a - (a % alignment); 126 | } 127 | 128 | static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) { 129 | return lfs_aligndown(a + alignment-1, alignment); 130 | } 131 | 132 | // Find the smallest power of 2 greater than or equal to a 133 | static inline uint32_t lfs_npw2(uint32_t a) { 134 | #if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) 135 | return 32 - __builtin_clz(a-1); 136 | #else 137 | uint32_t r = 0; 138 | uint32_t s; 139 | a -= 1; 140 | s = (a > 0xffff) << 4; a >>= s; r |= s; 141 | s = (a > 0xff ) << 3; a >>= s; r |= s; 142 | s = (a > 0xf ) << 2; a >>= s; r |= s; 143 | s = (a > 0x3 ) << 1; a >>= s; r |= s; 144 | return (r | (a >> 1)) + 1; 145 | #endif 146 | } 147 | 148 | // Count the number of trailing binary zeros in a 149 | // lfs_ctz(0) may be undefined 150 | static inline uint32_t lfs_ctz(uint32_t a) { 151 | #if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__) 152 | return __builtin_ctz(a); 153 | #else 154 | return lfs_npw2((a & -a) + 1) - 1; 155 | #endif 156 | } 157 | 158 | // Count the number of binary ones in a 159 | static inline uint32_t lfs_popc(uint32_t a) { 160 | #if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) 161 | return __builtin_popcount(a); 162 | #else 163 | a = a - ((a >> 1) & 0x55555555); 164 | a = (a & 0x33333333) + ((a >> 2) & 0x33333333); 165 | return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24; 166 | #endif 167 | } 168 | 169 | // Find the sequence comparison of a and b, this is the distance 170 | // between a and b ignoring overflow 171 | static inline int lfs_scmp(uint32_t a, uint32_t b) { 172 | return (int)(unsigned)(a - b); 173 | } 174 | 175 | // Convert between 32-bit little-endian and native order 176 | static inline uint32_t lfs_fromle32(uint32_t a) { 177 | #if !defined(LFS_NO_INTRINSICS) && ( \ 178 | (defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ 179 | (defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \ 180 | (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) 181 | return a; 182 | #elif !defined(LFS_NO_INTRINSICS) && ( \ 183 | (defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ 184 | (defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \ 185 | (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) 186 | return __builtin_bswap32(a); 187 | #else 188 | return (((uint8_t*)&a)[0] << 0) | 189 | (((uint8_t*)&a)[1] << 8) | 190 | (((uint8_t*)&a)[2] << 16) | 191 | (((uint8_t*)&a)[3] << 24); 192 | #endif 193 | } 194 | 195 | static inline uint32_t lfs_tole32(uint32_t a) { 196 | return lfs_fromle32(a); 197 | } 198 | 199 | // Convert between 32-bit big-endian and native order 200 | static inline uint32_t lfs_frombe32(uint32_t a) { 201 | #if !defined(LFS_NO_INTRINSICS) && ( \ 202 | (defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ 203 | (defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \ 204 | (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) 205 | return __builtin_bswap32(a); 206 | #elif !defined(LFS_NO_INTRINSICS) && ( \ 207 | (defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ 208 | (defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \ 209 | (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) 210 | return a; 211 | #else 212 | return (((uint8_t*)&a)[0] << 24) | 213 | (((uint8_t*)&a)[1] << 16) | 214 | (((uint8_t*)&a)[2] << 8) | 215 | (((uint8_t*)&a)[3] << 0); 216 | #endif 217 | } 218 | 219 | static inline uint32_t lfs_tobe32(uint32_t a) { 220 | return lfs_frombe32(a); 221 | } 222 | 223 | // Calculate CRC-32 with polynomial = 0x04c11db7 224 | uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size); 225 | 226 | // Allocate memory, only used if buffers are not provided to littlefs 227 | // Note, memory must be 64-bit aligned 228 | static inline void *lfs_malloc(size_t size) { 229 | #ifndef LFS_NO_MALLOC 230 | return malloc(size); 231 | #else 232 | (void)size; 233 | return NULL; 234 | #endif 235 | } 236 | 237 | // Deallocate memory, only used if buffers are not provided to littlefs 238 | static inline void lfs_free(void *p) { 239 | #ifndef LFS_NO_MALLOC 240 | free(p); 241 | #else 242 | (void)p; 243 | #endif 244 | } 245 | 246 | 247 | #ifdef __cplusplus 248 | } /* extern "C" */ 249 | #endif 250 | 251 | #endif 252 | #endif 253 | --------------------------------------------------------------------------------