/* PacoMouseCYD throttle -- F. CaƱada 2025-2026 -- https://usuaris.tinet.cat/fmco/ */ bool checkName(char *fileName) { bool result; uint16_t lng; result = false; lng = strlen(fileName); if (lng > 4) { if ((fileName[lng - 4] == '.') && (fileName[lng - 3] == 'c') && (fileName[lng - 2] == 's') && (fileName[lng - 1] == 'v')) return true; } return result; } bool saveLocoData(fs::FS &fs, uint16_t pos) { // save loco data in .csv file char field[30]; uint16_t cnt; bool dataOK, isDir; File myFile; dataOK = false; myFile = fs.open("/loco"); if (myFile) { isDir = myFile.isDirectory(); myFile.close(); if (!isDir) return dataOK; DEBUG_MSG("/loco is a directory"); } else { DEBUG_MSG("Directory /loco not found. Creating...") fs.mkdir("/loco"); } sprintf (field, "/loco/%d.csv", locoData[pos].myAddr.address); myFile = fs.open(field, FILE_WRITE); if (myFile) { DEBUG_MSG("File %s opened for writting", field); getLabelTxt(LBL_NAME, field); // Header myFile.print(field); myFile.print(CSV_FILE_DELIMITER); getLabelTxt(LBL_IMAGE, field); myFile.print(field); myFile.print(CSV_FILE_DELIMITER); getLabelTxt(LBL_VMAX, field); myFile.print(field); for (cnt = 0; cnt < 29; cnt++) { myFile.print(CSV_FILE_DELIMITER); myFile.print('F'); myFile.print(cnt); } myFile.print("\r\n"); myFile.print(locoData[pos].myName); // Loco data myFile.print(CSV_FILE_DELIMITER); myFile.print(locoData[pos].myLocoID); myFile.print(CSV_FILE_DELIMITER); myFile.print(locoData[pos].myVmax); for (cnt = 0; cnt < 29; cnt++) { myFile.print(CSV_FILE_DELIMITER); myFile.print(locoData[pos].myFuncIcon[cnt] >> 1); } myFile.print("\r\n"); myFile.close(); dataOK = true; } return dataOK; } void deleteLocoData (fs::FS &fs, uint16_t num) { char line[200]; sprintf (line, "/loco/%d.csv", num); if (fs.remove(line)) { DEBUG_MSG("File %s deleted", line); } else { DEBUG_MSG("File %s delete failed", line); } } bool readLocoData(fs::FS &fs, uint16_t num, uint16_t pos) { // read loco data from .csv file char line[200]; bool dataOK; uint16_t n; File myFile; dataOK = false; sprintf (line, "/loco/%d.csv", num); myFile = fs.open(line); if (myFile) { if (readCSV(myFile, line, sizeof(line), false)) { // skip header line if (readCSV(myFile, locoData[pos].myName, sizeof(locoData[pos].myName), true)) { // read data field: Name if (readCSV(myFile, line, sizeof(line), true)) { // read data field: Picture locoData[pos].myLocoID = atoi(line); if (readCSV(myFile, line, sizeof(line), true)) { locoData[pos].myVmax = atoi(line); DEBUG_MSG("%d %s %d %d", num, locoData[pos].myName, locoData[pos].myLocoID, locoData[pos].myVmax) for (n = 0; n < 29; n++) { if (!readCSV(myFile, line, sizeof(line), true)) { // read data field: Functions myFile.close(); return false; } locoData[pos].myFuncIcon[n] = (uint8_t)(atoi(line) << 1); //DEBUG_MSG("Func: %d - %d", n, locoData[pos].myFuncIcon[n]) } locoData[pos].myAddr.address = num; dataOK = true; } } } } myFile.close(); } return dataOK; } bool readCSV(File & f, char* line, uint16_t maxLen, bool getField) { uint16_t n; // read field or line from CSV file char chr; n = 0; yield(); while (n < maxLen) { chr = f.read(); switch (chr) { case 0: line[n] = '\0'; return false; // EOF break; case '\r': line[n] = '\0'; break; case '\n': line[n] = '\0'; return true; break; case ',': case ';': if (getField) { line[n] = '\0'; return true; } break; default: line[n++] = chr; break; } } return false; // too long } void loadLocoFiles(fs::FS &fs, const char * dirname) { uint16_t pos, adr; char nameFile[50]; File myFile; if (wifiSetting.protocol == CLIENT_ECOS) return; File root = fs.open(dirname); if (!root) { //DEBUG_MSG("Failed to open directory %s", dirname); return; } if (!root.isDirectory()) { //DEBUG_MSG("%s Not a directory", dirname); return; } pos = 0; File file = root.openNextFile(); while (file) { if (! file.isDirectory()) { if (pos < LOCOS_IN_STACK) { sprintf(nameFile, "%s", file.name()); adr = atoi(nameFile); //DEBUG_MSG("%d %s", adr, nameFile); if (readLocoData(fs, adr, pos++)) pushLoco(adr); } } file = root.openNextFile(); } } void initImageList() { uint16_t n, maxImg; uint16_t pos, id; char nameFile[50]; File myFile; for (n = 0; n < MAX_LOCO_IMAGE; n++) // add to list system images locoImages[n] = (n < (MAX_SYS_LPIC - 1)) ? n + 1 : 0; if (wifiSetting.protocol != CLIENT_ECOS) { if (sdDetected) { // add to list user images from SD File root = SD.open("/image"); if (root) { if (root.isDirectory()) { pos = MAX_SYS_LPIC - 1; File file = root.openNextFile(); while (file) { if (! file.isDirectory()) { if (pos < MAX_LOCO_IMAGE) { sprintf(nameFile, "%s", file.name()); id = atoi(nameFile); if (id >= 1000) { locoImages[pos++] = id; } } } file = root.openNextFile(); } } } } } locoImageIndex = 0; } void populateImageList() { uint16_t n; for (n = 0; n < 6; n++) lpicData[LPIC_SEL_IMG1 + n].id = locoImages[locoImageIndex + n]; } bool saveCurrAccPanel(fs::FS &fs) { char field[30]; uint16_t cnt; bool dataOK, isDir; File myFile; dataOK = false; myFile = fs.open("/acc"); if (myFile) { isDir = myFile.isDirectory(); myFile.close(); if (!isDir) return dataOK; DEBUG_MSG("/acc is a directory"); } else { DEBUG_MSG("Directory /acc not found. Creating...") fs.mkdir("/acc"); } sprintf (field, "/acc/%d.csv", currPanel); myFile = fs.open(field, FILE_WRITE); if (myFile) { DEBUG_MSG("File %s opened for writting", field); getLabelTxt(LBL_ACC_TYPE, field); // Header myFile.print(field); myFile.print(CSV_FILE_DELIMITER); getLabelTxt(LBL_ACC_ADDR, field); myFile.print(field); myFile.print(CSV_FILE_DELIMITER); getLabelTxt(LBL_ACC_ADDR, field); myFile.print(field); myFile.print(CSV_FILE_DELIMITER); getLabelTxt(LBL_ACC_NAME, field); myFile.print(field); myFile.print(CSV_FILE_DELIMITER); getLabelTxt(LBL_BITS, field); myFile.print(field); myFile.print(CSV_FILE_DELIMITER); myFile.print("\r\n"); for (cnt = 0; cnt < 16; cnt++) { myFile.print(accPanel[cnt].type); // Acc data myFile.print(CSV_FILE_DELIMITER); myFile.print(accPanel[cnt].addr); myFile.print(CSV_FILE_DELIMITER); myFile.print(accPanel[cnt].addr2); myFile.print(CSV_FILE_DELIMITER); myFile.print(accPanel[cnt].accName); myFile.print(CSV_FILE_DELIMITER); myFile.print(accPanel[cnt].activeOutput); myFile.print(CSV_FILE_DELIMITER); myFile.print("\r\n"); } myFile.close(); dataOK = true; } return dataOK; } bool loadAccPanel(fs::FS & fs) { char line[200]; bool dataOK; uint16_t cnt = 0; File myFile; dataOK = false; sprintf (line, "/acc/%d.csv", currPanel); myFile = fs.open(line); if (myFile) { if (readCSV(myFile, line, sizeof(line), false)) { // skip header line for (cnt = 0; cnt < 16; cnt++) { readCSV(myFile, line, sizeof(line), true); accPanel[cnt].type = (accType)atoi(line); readCSV(myFile, line, sizeof(line), true); accPanel[cnt].addr = atoi(line); readCSV(myFile, line, sizeof(line), true); accPanel[cnt].addr2 = atoi(line); readCSV(myFile, line, sizeof(line), true); snprintf(accPanel[cnt].accName, ACC_LNG + 1, line); readCSV(myFile, line, sizeof(line), false); accPanel[cnt].activeOutput = atoi(line); DEBUG_MSG("Line %d: %d, %d, %d, \"%s\", %d", cnt, accPanel[cnt].type, accPanel[cnt].addr, accPanel[cnt].addr2, accPanel[cnt].accName, accPanel[cnt].activeOutput) } } myFile.close(); dataOK = true; } return dataOK; } bool saveAccPanelNames(fs::FS & fs) { char field[30]; uint16_t cnt; bool dataOK, isDir; File myFile; dataOK = false; myFile = fs.open("/acc"); if (myFile) { isDir = myFile.isDirectory(); myFile.close(); if (!isDir) return dataOK; DEBUG_MSG("/acc is a directory"); } else { DEBUG_MSG("Directory /acc not found. Creating...") fs.mkdir("/acc"); } sprintf (field, "/acc/panel.csv"); myFile = fs.open(field, FILE_WRITE); if (myFile) { DEBUG_MSG("File %s opened for writting", field); getLabelTxt(LBL_NAME, field); // Header myFile.print(field); myFile.print("\r\n"); for (cnt = 0; cnt < 16; cnt++) { myFile.print(panelNamesBuf[cnt]); // Panel names myFile.print("\r\n"); } myFile.close(); dataOK = true; } return dataOK; } bool loadAccPanelNames(fs::FS & fs) { char line[200]; bool dataOK; uint16_t n; File myFile; dataOK = false; sprintf (line, "/acc/panel.csv"); myFile = fs.open(line); if (myFile) { if (readCSV(myFile, line, sizeof(line), false)) { // skip header line for (n = 0; n < 16; n++) { if (readCSV(myFile, line, sizeof(line), true)) { // read data field snprintf(panelNamesBuf[n], PANEL_LNG + 1, line); DEBUG_MSG("%s", panelNamesBuf[n]) } } } myFile.close(); } else { for (n = 0; n < 16; n++) // default names snprintf(panelNamesBuf[n], PANEL_LNG + 1, "Panel %d", n); } return dataOK; } /* 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.path(), levels - 1); } } else { Serial.print(" FILE: "); Serial.print(file.name()); Serial.print(" SIZE: "); Serial.println(file.size()); } file = root.openNextFile(); } } */