// *********************** Example useage ****************************** // listDir(SD, "/", 0); // writeFile(SD, "/protocols.txt", "Protocols Groups 1 thru 3\n"); // createDir(SD, "/mydir"); // listDir(SD, "/", 0); // removeDir(SD, "/mydir"); // listDir(SD, "/", 2); // writeFile(SD, "/hello.txt", "Hello "); // appendFile(SD, "/hello.txt", "World!\n"); // readFile(SD, "/hello.txt"); // deleteFile(SD, "/foo.txt"); // renameFile(SD, "/hello.txt", "/foo.txt"); // readFile(SD, "/foo.txt"); // testFileIO(SD, "/test.txt"); // ********************************************************************* void listDir(fs::FS &fs, const char * dirname, uint8_t levels){ Serial.printf("Listing directory: %s\n", dirname); File root = fs.open(dirname); if(!root){ Serial.println("Failed to open directory"); return;} if(!root.isDirectory()){ Serial.println("Not a directory"); return;} File file = root.openNextFile(); while(file){ if(file.isDirectory()){ Serial.print(" DIR : "); Serial.println(file.name()); if(levels){ listDir(fs, file.name(), levels -1); } } else { Serial.print(" FILE: "); Serial.print(file.name()); Serial.print(" SIZE: "); Serial.println(file.size()); } file = root.openNextFile(); } } // ********************************************************************* void createDir(fs::FS &fs, const char * path){ Serial.printf("Creating Dir: %s\n", path); if(fs.mkdir(path)){ Serial.println("Dir created"); } else { Serial.println("mkdir failed"); } } // ********************************************************************* void removeDir(fs::FS &fs, const char * path){ Serial.printf("Removing Dir: %s\n", path); if(fs.rmdir(path)){ Serial.println("Dir removed"); } else { Serial.println("rmdir failed"); } } // ********************************************************************* void readFile(fs::FS &fs, const char * path){ ssidStr = ""; // clear the string to null passwordStr = ""; // clear the string to null Serial.printf("Reading file: %s\n", path); File file = fs.open(path); if(!file){ Serial.println("Failed to open file for reading\n"); wifiEnable = 0; // return with an failure flag set return; } // while(file.available()){ // fetch the ssid and password asciiChar = file.read(); if(asciiChar != 0x0A && singleShot == 0) String(ssidStr += asciiChar); // ssid to the ssidStr string else { singleShot = 1; if(asciiChar != 0X0A) String(passwordStr += asciiChar); // password to the passwordStr string } } singleShot = 0; // restore singleShote to a 0X00 file.close(); // close the file noRead = 1; // set this flag to allow this routine to only run once //Serial.print(ssidStr); //Serial.print(" "); //Serial.println(passwordStr); } // ********************************************************************* void readFileModified(fs::FS &fs, const char * path){ // Routine reads the SD card and places formatted data into the ESP32 Flash Memory // Format data conforms to the needs of the ESP32 Four Channel Function Generator Pgm // All "Serial.print" and "Serial.write" strickly used for debugging Serial.printf("Reading file: %s\n", path); File file = fs.open(path); if(!file){ Serial.println("Failed to open file for reading"); fileRtnCode = 1; return; } fileRtnCode = 0; if(DEBUG) Serial.print("Read from file: "); protoID = 1; intCnt = 0; freqRecCnt = 0; // no frequency records loaded yet sawCtrlRec = 0; // and no end-of-file record seen yet workStr = ""; // clear out work string eeAddress = FLASH_START; // Everything ahead of the '#' is free text, so a file can carry a descriptive // name above its records. read() returns -1 once the file runs out, and -1 is // not '#', so the end has to be watched for here: a file with no '#' in it at // all -- an empty one, or one truncated by a card pulled mid-write -- would // otherwise spin in this loop forever and hang the boot. int headChar = file.read(); // int, so end-of-file stays distinguishable while(headChar != '#'){ // loop till found and point to NL character if(headChar < 0){ // ran out of file without ever finding it file.close(); fileRtnCode = 2; // fileFetch() reports this on the OLED return; } Serial.write((char)headChar); // headChar = file.read(); // } Serial.write('#'); // asciiChar = file.read(); // get of the following NL Serial.write(asciiChar); // while(file.available()){ asciiChar = file.read(); // very 1st read should be a number //Serial.write(asciiChar); if(isDigit(asciiChar) || asciiChar == 0x2E) // check for number or period workStr += asciiChar; // add character to work string else if(asciiChar == ',' || asciiChar == 0X0A){ if(intCnt==0){ protoID=workStr.toInt(); // protoID used as protocol and line identifier } if(protoID != 0){ if(intCnt==1) F1 = workStr.toFloat(); // F1 Frequency 1 if(intCnt==2) D1 = workStr.toFloat(); // D1 Duty Cycle 1 if(intCnt==3) F2 = workStr.toFloat(); // F2 if(intCnt==4) D2 = workStr.toFloat(); // D2 if(intCnt==5) F3 = workStr.toFloat(); // F3 if(intCnt==6) D3 = workStr.toFloat(); // D3 if(intCnt==7) F4 = workStr.toFloat(); // F4 if(intCnt==8) D4 = workStr.toFloat(); // D4 } else { if(intCnt==1) memGrp = workStr.toInt(); // memGrp field if(intCnt==2) modeID = workStr.toInt(); // modeID field if(intCnt==3) endTime1 = workStr.toInt() * 1000; // CSV stores whole seconds (matches screen 5's // "Freq Time" display) -- convert to ms, which // is what endTime1 is used as everywhere else if(intCnt==4) endTime2 = workStr.toInt() * 60000; // CSV stores whole minutes (matches screen 5's // "Progm Time" display) -- convert to ms, same // reasoning as endTime1 above if(intCnt==5) startFreqGrpID = workStr.toInt(); // startFreqGrpID field if(intCnt==6) stopFreqGrpID = workStr.toInt(); // stopFreqGrpID field if(intCnt==7) startSweepGrpID = workStr.toInt();// startSweepGrpID field if(intCnt==8) stopSweepGrpID = workStr.toInt(); // stopSweepGrpID field if(intCnt==9) sweepFreqInc = workStr.toFloat(); // Sweep Frequency Increment value if(intCnt==10) fileNo = workStr.toInt(); // file number ranging from 0 to 9 } workStr = ""; // clear out work string if(intCnt < 8 || (protoID==0 && intCnt<10)) // 8 or 10 numbers depending on protoID ++intCnt; else { intCnt = 0; // if here move a data line to Flash Memory if(protoID == 0) // decide which format to use loadIntegers(); // load a completed interger line of data else loadFloats(); // load a completed float line to data if(fileRtnCode != 0){ // loadFloats() refused -- file holds more file.close(); // records than EEPROM can hold. Stop now return; // rather than load a half a file. } } } } file.close(); // release the read handle now that parsing is done // A protocol file is only usable if it holds records AND the 0,... control // record that ends it. A save interrupted part way through leaves exactly that // -- records but no end record, because the end record is written last -- and // without this check it loaded silently, taking the mode, both run times and // all four group IDs from whatever happened to be sitting in memory. The USB // upload has always refused such a file; now the card reader does too. if(freqRecCnt == 0) fileRtnCode = 5; // nothing but a header else if(!sawCtrlRec) fileRtnCode = 4; // records, but the file just stops } // ********************************************************************* void loadFloats(){ // Used with above 'readFileModified and store to ESP32 Flash memory if(freqRecCnt >= MAX_FREQ_RECORDS){ // one record too many for the EEPROM image. fileRtnCode = 3; // Refusing here is what keeps the control return; // record's slot at the end of the image free } // -- see MAX_FREQ_RECORDS in the main .ino ++freqRecCnt; // count it before it goes in EPromFreqObject customVar1; // Variables to store custom objects in EEPROM customVar1.field0=protoID; // protoID customVar1.field1=F1; // F1 customVar1.field2=D1; // D1 customVar1.field3=F2; // F2 customVar1.field4=D2; // D2 customVar1.field5=F3; // F3 customVar1.field6=D3; // D3 customVar1.field7=F4; // F4 customVar1.field8=D4; // D4 EEPROM.put(eeAddress,customVar1); // Update Flash memory0 protoIDlast=protoID; // this could potentially be last protoID sizeofVar1 = sizeof(customVar1); // fetch the size of this array eeAddress += sizeofVar1; // prepare for next Flash Memory placement } // ********************************************************************* void loadIntegers(){ // Used with above 'readFileModified and store to ESP32 Flash memory EPromControlObject customVar2; // Variables to store custom objects in EEPROM customVar2.field0=protoID; // Always zero (0) to indicate ENDING GROUP customVar2.field1=memGrp; // This will contain last Memory Group used by program customVar2.field2=modeID; // This will contain last mode used/set customVar2.field3=endTime1; // This will contain Frequency run time customVar2.field4=endTime2; // This will contain Program run time customVar2.field5=startFreqGrpID; // Contains Start Memory Group for Protocol Mode 2 customVar2.field6=stopFreqGrpID; // Contains Stop Memory Group for Protocol Mode 2 customVar2.field7=startSweepGrpID; // Contains Start Memory Group for Sweep Mode 3 customVar2.field8=stopSweepGrpID; // Cpmtaoms Stop Memory Group for Sweep Mode 3 customVar2.field9=sweepFreqInc; // sweep frequenc increment value, zero = auto sweep customVar2.field10=fileNo; // file number ranging from 0 to 9 EEPROM.put(eeAddress,customVar2); // Update Flash memory0 sawCtrlRec = 1; // the file has its end record sizeofVar2 = sizeof(customVar2); // fetch the size of this array eeAddress2 = eeAddress; // save this FLash Memory address // eeAddress += sizeofVar2; // prepare for next Flash Memory placement if(DEBUG == 1){ Serial.print("Size Of CustomVar1 = "); Serial.print(sizeofVar1); Serial.print(" Size Of CustomVar2 = "); Serial.println(sizeofVar2); } } // ********************************************************************* void writeFile(fs::FS &fs, const char * path, const char * message){ Serial.printf("Writing file: %s\n", path); File file = fs.open(path, FILE_WRITE); if(!file){ Serial.println("Failed to open file for writing"); return; } if(file.print(message)){ Serial.println("File written"); } else { Serial.println("Write failed"); } file.close(); // force the write to actually reach the SD card now -- // without this, data can sit unflushed and be lost if // reset/power-off happens before it's otherwise synced } // ********************************************************************* void appendFile(fs::FS &fs, const char * path, const char * message){ Serial.printf("Appending to file: %s\n", path); File file = fs.open(path, FILE_APPEND); if(!file){ Serial.println("Failed to open file for appending"); return; } if(file.print(message)){ Serial.println("Message appended"); } else { Serial.println("Append failed"); } file.close(); // force the write to actually reach the SD card now -- // without this, data can sit unflushed and be lost if // reset/power-off happens before it's otherwise synced } // ********************************************************************* void renameFile(fs::FS &fs, const char * path1, const char * path2){ Serial.printf("Renaming file %s to %s\n", path1, path2); if (fs.rename(path1, path2)) { Serial.println("File renamed"); } else { Serial.println("Rename failed"); } } // ********************************************************************* void deleteFile(fs::FS &fs, const char * path){ Serial.printf("Deleting file: %s\n", path); if(fs.remove(path)){ Serial.println("File deleted"); } else { Serial.println("Delete failed"); } } // ********************************************************************* void testFileIO(fs::FS &fs, const char * path){ File file = fs.open(path); static uint8_t buf[512]; size_t len = 0; uint32_t start = millis(); uint32_t end = start; if(file){ len = file.size(); size_t flen = len; start = millis(); while(len){ size_t toRead = len; if(toRead > 512){ toRead = 512; } file.read(buf, toRead); len -= toRead; } end = millis() - start; Serial.printf("%u bytes read for %u ms\n", flen, end); file.close(); } else { Serial.println("Failed to open file for reading"); } file = fs.open(path, FILE_WRITE); if(!file){ Serial.println("Failed to open file for writing"); return; } size_t i; start = millis(); for(i=0; i<2048; i++){ file.write(buf, 512); } end = millis() - start; Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end); file.close(); }