#include <LittleFS.h>
// #define ROOTONLY // NORMAL is NOT DEFINED!
//#define TEST_RAM
//#define TEST_SPI
//#define TEST_QSPI
#define TEST_PROG
// Set for SPI usage
const int FlashChipSelect = 6; // digital pin for flash chip CS pin
#ifdef TEST_RAM
LittleFS_RAM myfs;
DMAMEM char buf[490000]; // USE DMAMEM for more memory than ITCM allows - or remove
char szDiskMem[] = "RAM_DISK";
#elif defined(TEST_SPI)
//const int FlashChipSelect = 21; // Arduino 101 built-in SPI Flash
#define FORMATSPI
//#define FORMATSPI2
LittleFS_SPIFlash myfs;
char szDiskMem[] = "SPI_DISK";
#elif defined(TEST_PROG)
LittleFS_Program myfs;
char szDiskMem[] = "PRO_DISK";
#else // TEST_QSPI
LittleFS_QSPIFlash myfs;
char szDiskMem[] = "QSPI_DISK";
#endif
File file3;
#define SUBADD 1 // bytes added each pass (*times file number)
#define BIGADD 10 // bytes added each pass - bigger will quickly consume more space
#define MAXNUM 26 // ALPHA A-Z is 26, less for fewer files
#define DELDELAY 0 // delay before DEL files : delayMicroseconds
#define ADDDELAY 0 // delay on ADD FILE : delayMicroseconds
const uint32_t lowOffset = 'a' - 'A';
const uint32_t lowShift = 13;
uint32_t lCnt = 0;
uint32_t LoopCnt = 0;
uint32_t rdCnt = 0;
uint32_t wrCnt = 0;
unsigned int filecount = 0;
void setup() {
while (!Serial) ; // wait
Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
Serial.println("LittleFS Test : File Integrity"); delay(5);
#ifdef TEST_RAM
if (!myfs.begin(buf, sizeof(buf))) {
#elif defined(TEST_SPI)
#ifdef FORMATSPI
if (!myfs.begin( FlashChipSelect )) {
#elif defined(FORMATSPI2)
pinMode(FlashChipSelect, OUTPUT);
digitalWriteFast(FlashChipSelect, LOW);
SPI2.setMOSI(50);
SPI2.setMISO(54);
SPI2.setSCK(49);
SPI2.begin();
if (!myfs.begin(51, SPI2)) {
#endif
#elif defined(TEST_PROG)
if (!myfs.begin(1024*1024*4)) {
#else
if (!myfs.begin()) {
#endif
Serial.printf("Error starting %s\n", szDiskMem);
checkInput( 1 );
}
// parseCmd( 'f' ); // ENABLE this is disk won't allow startup
printDirectory();
parseCmd( '?' );
#ifndef ROOTONLY // Don't make subdirs - 11/9/20 : QSPI only makes first two usable, creates third that 'goes away'
makeRootDirs();
#endif
checkInput( 1 );
filecount = printDirectoryFilecount( myfs.open("/") );
printDirectory();
}
void makeRootDirs() {
char szDir[16];
for ( int ii = 0; ii < 10; ii++ ) {
sprintf( szDir, "/%c_dir", '0' + ii );
myfs.mkdir( szDir );
}
}
int loopLimit = 0; // -1 continuous, otherwise # to count down to 0
bool pauseDir = false; // Start Pause on each off
bool showDir = false; // false Start Dir on each off
void loop() {
char szDir[16];
LoopCnt++;
uint32_t chStep;
if ( loopLimit != 0 ) {
#ifdef ROOTONLY // ii=0-9 are subdirs. #10 is Root
for ( int ii = 10; ii < 11; ii++ )
#else
for ( int ii = 0; ii < 11; ii++ )
#endif
{
if ( ii == 10 )
sprintf( szDir, "/" );
else
sprintf( szDir, "/%c_dir", '0' + ii );
chStep = fileCycle(szDir);
while ( chStep != fileCycle(szDir) && ( loopLimit != 0 ) ) checkInput( 0 ); // user input can 0 loopLimit
}
checkInput( 0 );
if ( loopLimit > 0 ) // -1 means continuous
loopLimit--;
}
else
checkInput( 1 );
}
char szInputs[] = "0123456789rdchkfvplmu?";
void checkInput( int step ) { // prompt for input without user input with step != 0
char retVal = 0, temp;
char *pTemp;
if ( step != 0 ) {
Serial.printf( "[%6.2f M] Awaiting input %s loops left %d >", millis() / 60000.0, szInputs, loopLimit );
}
else {
if ( !Serial.available() ) return;
Serial.printf( "[%6.2f] Awaiting input %s loops left %d >", millis() / 60000.0, szInputs, loopLimit );
while ( Serial.available() ) {
temp = Serial.read( );
if ( (pTemp = strchr(szInputs, temp)) ) {
retVal = pTemp[0];
parseCmd( retVal );
}
}
}
while ( !Serial.available() );
while ( Serial.available() ) {
temp = Serial.read();
if ( (pTemp = strchr(szInputs, temp)) ) {
retVal = pTemp[0];
parseCmd( retVal );
}
}
Serial.print( '\n' );
if ( '?' == retVal ) checkInput( 1 ); // recurse on '?' to allow command show and response
return;
}
void parseCmd( char chIn ) { // pass chIn == '?' for help
switch (chIn ) {
case '?':
Serial.printf( "%s\n", " 0, 1-9 '#' passes continue loop before Pause\n\
'r' Restart Teensy\n\
'd' Directory of LittleFS\n\
'c' Continuous Loop\n\
'h' Hundred loops\n\
'k' Thousand loops\n\
'f' Format: RAM Restart : Q/SPI->'myfs.format();'\n\
'v' Verbose All Dir Prints - TOGGLE\n\
'p' Pause after all Dir prints - TOGGLE\n\
'l' Show count of loop()'s, Bytes Read,Written\n\
'm' Make ROOT dirs (needed after format !ROOTONLY)\n\
'u' Update Filecount\n\
'?' Help list" );
break;
case 'r':
Serial.print(" RESET Teensy ...");
delay(100);
SCB_AIRCR = 0x05FA0004;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
loopLimit = chIn - '0';
break;
case 'c':
loopLimit = -1;
break;
case 'd':
Serial.print( " d\n" );
printDirectory();
Serial.print( '\n' );
parseCmd( 'l' );
checkInput( 1 );
chIn = 0;
break;
case 'h':
loopLimit = 100;
break;
case 'k':
loopLimit = 1000;
break;
case 'f': // format
#ifdef TEST_RAM
parseCmd( 'r' );
#elif defined(TEST_SPI)
myfs.format();
#elif defined(TEST_PROG)
//myfs.format();
#else // TEST_QSPI
myfs.format();
#endif
parseCmd( 'u' );
break;
case 'v': // verbose dir
showDir = !showDir;
showDir ? Serial.print(" Verbose on: ") : Serial.print(" Verbose off: ");
chIn = 0;
break;
case 'p': // pause on dirs
pauseDir = !pauseDir;
pauseDir ? Serial.print(" Pause on: ") : Serial.print(" Pause off: ");
chIn = 0;
break;
case 'l': // Show Loop Count
Serial.printf("\n\t Loop Count: %u (#fileCycle=%u), Bytes read %u, written %u, #Files=%u\n", LoopCnt, lCnt, rdCnt, wrCnt, filecount );
chIn = 0;
break;
case 'm':
Serial.printf("m \n\t Making Root Dirs\n" );
makeRootDirs();
parseCmd( 'd' );
chIn = 0;
break;
case 'u': // Show Loop Count
filecount = printDirectoryFilecount( myfs.open("/") );
Serial.printf("u \n\t Updated filecount %u\n", filecount );
chIn = 0;
break;
default:
Serial.println( chIn ); // never see without unhandled char in szInputs[]
break;
}
if ( 0 != chIn ) Serial.print( chIn );
}
uint32_t fTot, totSize;
void printDirectory() {
fTot = 0, totSize = 0;
Serial.printf("printDirectory %s\n--------------\n", szDiskMem);
printDirectory(myfs.open("/"), 0);
//Serial.println();
Serial.printf(" %Total %u files of Size %u Bytes\n", fTot, totSize);
Serial.printf("Bytes Used: %llu, Bytes Total:%llu\n", myfs.usedSize(), myfs.totalSize());
}
unsigned int printDirectoryFilecount(File dir) {
unsigned int filecnt = 0;
while (true) {
File entry = dir.openNextFile();
if (! entry) {
// no more files
break;
}
if (entry.isDirectory()) {
filecnt+=printDirectoryFilecount(entry);
} else {
filecnt++;
}
entry.close();
}
return filecnt;
}
void printDirectory(File dir, int numTabs) {
//dir.whoami();
uint32_t fSize = 0, dCnt = 0, fCnt = 0;
if ( 0 == dir ) {
Serial.printf( "\t>>>\t>>>>> No Dir\n" );
return;
}
while (true) {
File entry = dir.openNextFile();
if (! entry) {
// no more files
Serial.printf("\n %u dirs with %u files of Size %u Bytes\n", dCnt, fCnt, fSize);
fTot += fCnt;
totSize += fSize;
break;
}
for (uint8_t i = 0; i < numTabs; i++) {
Serial.print('\t');
}
if (entry.isDirectory()) {
Serial.print("DIR\t");
dCnt++;
} else {
Serial.print("FILE\t");
fCnt++;
fSize += entry.size();
}
Serial.print(entry.name());
if (entry.isDirectory()) {
Serial.println(" / ");
printDirectory(entry, numTabs + 1);
} else {
// files have sizes, directories do not
Serial.print("\t\t");
Serial.println(entry.size(), DEC);
}
entry.close();
//Serial.flush();
}
}
uint32_t cCnt = 0;
uint32_t fileCycle(const char *dir) {
static char szFile[] = "_file.txt";
char szPath[150];
int ii;
lCnt++;
byte nNum = lCnt % MAXNUM;
char chNow = 'A' + lCnt % MAXNUM;
lfs_ssize_t resW = 1;
if ( dir[1] == 0 ) // catch root
sprintf( szPath, "/%c%s", chNow, szFile );
else
sprintf( szPath, "%s/%c%s", dir, chNow, szFile );
if ( cCnt >= 3 && myfs.exists(szPath) ) { // DELETE ALL KNOWN FILES
if ( nNum == 1 ) {
Serial.print( "\n == == == DELETE PASS START == == == = \n");
if ( showDir ) {
printDirectory();
Serial.print( " == == == DELETE PASS START == == == = \n");
}
delayMicroseconds(DELDELAY);
}
}
Serial.printf( ":: %s ", szPath );
if ( cCnt >= 3 && myfs.exists(szPath) ) { // DELETE ALL KNOWN FILES
readVerify( szPath, chNow );
myfs.remove(szPath);
filecount--;
Serial.printf(" %s ----DEL----", szDiskMem);
Serial.printf(" -- %c", chNow);
if ( showDir ) {
Serial.print("\n");
printDirectory(myfs.open(dir), 1);
}
if ( pauseDir ) checkInput( 1 );
Serial.println();
}
else {
if ( nNum == 0 ) {
nNum = 10;
cCnt++;
if ( cCnt >= 5 ) cCnt = 0;
}
file3 = myfs.open(szPath, FILE_WRITE);
if ( 0 == file3 ) {
Serial.printf( "\tXXX\tXXX\tXXX\tXXX\tFail File open \n" );
checkInput( 1 ); // PAUSE on CmdLine
}
delayMicroseconds(ADDDELAY);
char mm = chNow + lowOffset;
uint32_t jj = file3.size() + 1;
if ( jj == 1) filecount++;
for ( ii = 0; ii < (nNum * SUBADD + BIGADD ) && resW > 0; ii++ ) {
if ( 0 == ((ii + jj) / lowShift) % 2 )
resW = file3.write( &mm , 1 );
else
resW = file3.write( &chNow , 1 );
wrCnt++;
// if ( lCnt%100 == 50 ) mm='x'; // GENERATE ERROR to detect on DELETE read verify
}
file3.close();
Serial.printf(" %s +++ Add +++ [sz %u add %u]", szDiskMem, jj - 1, ii);
if (resW < 0) {
Serial.printf( "\twrite fail %i", resW );
checkInput( 1 ); // PAUSE on CmdLine
}
Serial.printf(" ++ %c ", chNow);
readVerify( szPath, chNow );
if ( showDir ) {
Serial.print("\n");
printDirectory(myfs.open(dir), 1);
}
if ( pauseDir ) checkInput( 1 );
Serial.print("\n");
delayMicroseconds(ADDDELAY);
}
if ( filecount != printDirectoryFilecount( myfs.open("/") ) ) {
Serial.printf( "\tFilecount mismatch %u != %u\n", filecount, printDirectoryFilecount( myfs.open("/") ) );
checkInput( 1 ); // PAUSE on CmdLine
}
return cCnt;
}
void readVerify( char szPath[], char chNow ) {
file3 = myfs.open(szPath);
if ( 0 == file3 ) {
Serial.printf( "\tV\t Fail File open %s\n", szPath );
checkInput( 1 );
}
char mm;
char chNow2 = chNow + lowOffset;
uint32_t ii = 0;
while ( file3.available() ) {
file3.read( &mm , 1 );
rdCnt++;
//Serial.print( mm ); // show chars as read
ii++;
if ( 0 == (ii / lowShift) % 2 ) {
if ( chNow2 != mm ) {
Serial.printf( "<Bad Byte! %c! = %c [0x%X] @%u\n", chNow2, mm, mm, ii );
checkInput( 1 );
}
}
else {
if ( chNow != mm ) {
Serial.printf( "<Bad Byte! %c! = %c [0x%X] @%u\n", chNow, mm, mm, ii );
checkInput( 1 );
}
}
}
Serial.printf( "\tVerify %s bytes %u ", szPath, ii );
if (ii != file3.size()) {
Serial.printf( "\n\tRead Count fail! :: read %u != f.size %u",ii, file3.size() );
checkInput( 1 ); // PAUSE on CmdLine
}
file3.close();
}