This commit is contained in:
e2002
2023-06-02 12:12:21 +03:00
parent 3cd53f226a
commit aba29fcf16
15 changed files with 263 additions and 178 deletions

View File

@@ -226,6 +226,11 @@ Work is in progress...
--- ---
## Version history ## Version history
#### v0.9.235
- SD card playlist moved from SPIFFS to SD card
- new parameter #define SD_MAX_LEVELS - Search depth for files on SD card
- fixed bugs with SD card in multi-threaded mode
#### v0.9.220 #### v0.9.220
- fixed SD prelist indexing error when switching Web>>SD - fixed SD prelist indexing error when switching Web>>SD
- fixed a bug of switching to the next track when accidentally playing SD - fixed a bug of switching to the next track when accidentally playing SD

View File

@@ -65,7 +65,7 @@ const char const_DlgLost[] PROGMEM = "* LOST *";
const char const_DlgUpdate[] PROGMEM = "* UPDATING *"; const char const_DlgUpdate[] PROGMEM = "* UPDATING *";
const char const_DlgNextion[] PROGMEM = "* NEXTION *"; const char const_DlgNextion[] PROGMEM = "* NEXTION *";
const char const_getWeather[] PROGMEM = ""; const char const_getWeather[] PROGMEM = "";
const char const_waitForSD[] PROGMEM = "WAIT FOR SD"; const char const_waitForSD[] PROGMEM = "INDEX SD";
const char apNameTxt[] PROGMEM = "AP NAME"; const char apNameTxt[] PROGMEM = "AP NAME";
const char apPassTxt[] PROGMEM = "PASSWORD"; const char apPassTxt[] PROGMEM = "PASSWORD";

View File

@@ -65,7 +65,7 @@ const char const_DlgLost[] PROGMEM = "ОТКЛЮЧЕНО";
const char const_DlgUpdate[] PROGMEM = "ОБНОВЛЕНИЕ"; const char const_DlgUpdate[] PROGMEM = "ОБНОВЛЕНИЕ";
const char const_DlgNextion[] PROGMEM = "NEXTION"; const char const_DlgNextion[] PROGMEM = "NEXTION";
const char const_getWeather[] PROGMEM = ""; const char const_getWeather[] PROGMEM = "";
const char const_waitForSD[] PROGMEM = "ОЖИДАНИЕ SD"; const char const_waitForSD[] PROGMEM = "ИНДЕКС SD";
const char apNameTxt[] PROGMEM = "ТОЧКА ДОСТУПА"; const char apNameTxt[] PROGMEM = "ТОЧКА ДОСТУПА";
const char apPassTxt[] PROGMEM = "ПАРОЛЬ"; const char apPassTxt[] PROGMEM = "ПАРОЛЬ";

View File

@@ -63,7 +63,7 @@ void Config::init() {
if(emptyFS) BOOTLOG("SPIFFS is empty!"); if(emptyFS) BOOTLOG("SPIFFS is empty!");
ssidsCount = 0; ssidsCount = 0;
_cardStatus = CS_NONE; _cardStatus = CS_NONE;
mountSDbusy = false; _SDplaylistFS = getMode()==PM_SDCARD?&SD:(true?&SPIFFS:_SDplaylistFS);
if(SDC_CS!=255) randomSeed(analogRead(SDC_CS)); if(SDC_CS!=255) randomSeed(analogRead(SDC_CS));
backupSDStation = 0; backupSDStation = 0;
//checkSD(); //checkSD();
@@ -71,8 +71,8 @@ void Config::init() {
bootInfo(); bootInfo();
} }
#ifdef USE_SD
bool Config::_sdCardIsConnected() { bool Config::_sdCardIsConnected() {
#if SDC_CS!=255
sdog.takeMutex(); sdog.takeMutex();
if(SD.sectorSize()<1) { if(SD.sectorSize()<1) {
sdog.giveMutex(); sdog.giveMutex();
@@ -83,8 +83,6 @@ bool Config::_sdCardIsConnected() {
if(SD.sectorSize()>0 && !bread) SD.end(); if(SD.sectorSize()>0 && !bread) SD.end();
sdog.giveMutex(); sdog.giveMutex();
return bread; return bread;
#endif
return false;
} }
bool Config::_sdBegin(){ bool Config::_sdBegin(){
@@ -102,8 +100,6 @@ bool Config::_sdBegin(){
} }
void Config::checkSD(){ void Config::checkSD(){
#if SDC_CS!=255
mountSDbusy = true;
cardStatus_e prevCardStatus = _cardStatus; cardStatus_e prevCardStatus = _cardStatus;
if(_sdCardIsConnected()){ if(_sdCardIsConnected()){
if(_cardStatus==CS_NONE || _cardStatus==CS_PRESENT || _cardStatus==CS_EJECTED) { if(_cardStatus==CS_NONE || _cardStatus==CS_PRESENT || _cardStatus==CS_EJECTED) {
@@ -119,22 +115,10 @@ void Config::checkSD(){
} }
backupSDStation = 0; backupSDStation = 0;
} }
mountSDbusy = false;
#endif
}
bool Config::spiffsCleanup(){
bool ret = (SPIFFS.exists(PLAYLIST_SD_PATH)) || (SPIFFS.exists(INDEX_SD_PATH)) || (SPIFFS.exists(INDEX_PATH));
if(SPIFFS.exists(PLAYLIST_SD_PATH)) SPIFFS.remove(PLAYLIST_SD_PATH);
if(SPIFFS.exists(INDEX_SD_PATH)) SPIFFS.remove(INDEX_SD_PATH);
if(SPIFFS.exists(INDEX_PATH)) SPIFFS.remove(INDEX_PATH);
return ret;
} }
void Config::_mountSD(){ void Config::_mountSD(){
#if SDC_CS!=255 if(display.mode()==SDCHANGE) return;
if(SDC_CS==255 || mountSDbusy || display.mode()==SDCHANGE) return;
mountSDbusy = true;
sdog.takeMutex(); uint16_t ssz = SD.sectorSize(); sdog.giveMutex(); sdog.takeMutex(); uint16_t ssz = SD.sectorSize(); sdog.giveMutex();
if(ssz<1) SDinit = _sdBegin(); if(ssz<1) SDinit = _sdBegin();
if(!_sdCardIsConnected()) { if(!_sdCardIsConnected()) {
@@ -150,14 +134,155 @@ void Config::_mountSD(){
} }
} }
} }
mountSDbusy = false; }
#endif
void Config::changeMode(int newmode){
if(!SDinit) {
_mountSD();
if(!SDinit){
Serial.println("##[ERROR]#\tSD Not Found");
netserver.requestOnChange(GETPLAYERMODE, 0);
return;
}
}
if(getMode()==PM_SDCARD) store.lastStation = config.backupLastStation;
if(newmode<0){
store.play_mode++;
if(getMode() > MAX_PLAY_MODE){
store.play_mode=0;
}
}else{
store.play_mode=(playMode_e)newmode;
}
save();
_SDplaylistFS = getMode()==PM_SDCARD?&SD:(true?&SPIFFS:_SDplaylistFS);
if(getMode()==PM_SDCARD && _cardStatus!=CS_MOUNTED){
display.putRequest(NEWMODE, SDCHANGE);
while(display.mode()!=SDCHANGE)
delay(10);
delay(50);
}
initPlaylistMode();
if (store.smartstart == 1) player.sendCommand({PR_PLAY, store.lastStation});
//else
// player.sendCommand({PR_STOP, 0});
netserver.resetQueue();
netserver.requestOnChange(GETPLAYERMODE, 0);
netserver.requestOnChange(GETMODE, 0);
display.resetQueue();
display.putRequest(NEWMODE, PLAYER);
display.putRequest(NEWSTATION);
}
bool endsWith (const char* base, const char* str) {
int slen = strlen(str) - 1;
const char *p = base + strlen(base) - 1;
while(p > base && isspace(*p)) p--;
p -= slen;
if (p < base) return false;
return (strncmp(p, str, slen) == 0);
}
uint32_t sdFCount;
void Config::listSD(File &plSDfile, File &plSDindex, const char * dirname, uint8_t levels){
sdog.takeMutex(); File root = SD.open(dirname); sdog.giveMutex();
if(!root){
Serial.println("##[ERROR]#\tFailed to open directory");
return;
}
sdog.takeMutex();
if(!root.isDirectory()){
Serial.println("##[ERROR]#\tNot a directory");
sdog.giveMutex();
return;
}
sdog.giveMutex();
sdog.takeMutex(); File file = root.openNextFile(); sdog.giveMutex();
uint32_t pos = 0;
while(file){
vTaskDelay(2);
sdog.takeMutex();
bool fid = file.isDirectory();
const char * fp = file.path();
const char * fn = file.name();
sdog.giveMutex();
if(fid){
if(levels && !checkNoMedia(fp)){
listSD(plSDfile, plSDindex, fp, levels -1);
}
} else {
if(endsWith(strlwr((char*)fn), ".mp3") || endsWith(fn, ".m4a") || endsWith(fn, ".aac") || endsWith(fn, ".wav") || endsWith(fn, ".flac")){
sdog.takeMutex();
pos = plSDfile.position();
plSDfile.print(fn); plSDfile.print("\t"); plSDfile.print(fp); plSDfile.print("\t"); plSDfile.println(0);
plSDindex.write((byte *) &pos, 4);
sdog.giveMutex();
Serial.print(".");
if(display.mode()==SDCHANGE) display.putRequest(SDFILEINDEX, sdFCount+1);
sdFCount++;
if(sdFCount%64==0) Serial.println();
}
}
sdog.takeMutex(); if(file) file.close(); file = root.openNextFile(); sdog.giveMutex();
}
sdog.takeMutex(); if(root) root.close(); sdog.giveMutex();
}
void Config::indexSDPlaylist() {
sdFCount = 0;
sdog.takeMutex();
if(SDPLFS()->exists(PLAYLIST_SD_PATH)) SDPLFS()->remove(PLAYLIST_SD_PATH);
if(SDPLFS()->exists(INDEX_SD_PATH)) SDPLFS()->remove(INDEX_SD_PATH);
File playlist = SDPLFS()->open(PLAYLIST_SD_PATH, "w", true);
sdog.giveMutex();
if (!playlist) {
return;
}
sdog.takeMutex();
File index = SDPLFS()->open(INDEX_SD_PATH, "w", true);
sdog.giveMutex();
listSD(playlist, index, "/", SD_MAX_LEVELS);
sdog.takeMutex();
index.flush();
index.close();
playlist.flush();
playlist.close();
sdog.giveMutex();
Serial.println();
delay(50);
}
void Config::initSDPlaylist(bool doIndex) {
store.countStation = 0;
if(doIndex) indexSDPlaylist();
sdog.takeMutex();
if (SDPLFS()->exists(INDEX_SD_PATH)) {
File index = SDPLFS()->open(INDEX_SD_PATH, "r");
store.countStation = index.size() / 4;
if(doIndex){
store.lastStation = random(1, store.countStation);
backupSDStation = store.lastStation;
}
index.close();
save();
}
sdog.giveMutex();
}
#endif //#ifdef USE_SD
bool Config::spiffsCleanup(){
bool ret = (SPIFFS.exists(PLAYLIST_SD_PATH)) || (SPIFFS.exists(INDEX_SD_PATH)) || (SPIFFS.exists(INDEX_PATH));
if(SPIFFS.exists(PLAYLIST_SD_PATH)) SPIFFS.remove(PLAYLIST_SD_PATH);
if(SPIFFS.exists(INDEX_SD_PATH)) SPIFFS.remove(INDEX_SD_PATH);
if(SPIFFS.exists(INDEX_PATH)) SPIFFS.remove(INDEX_PATH);
return ret;
} }
void Config::initPlaylistMode(){ void Config::initPlaylistMode(){
sdResumePos = 0; sdResumePos = 0;
SDinit = false; SDinit = false;
if(SDC_CS!=255){ #ifdef USE_SD
if(!_sdBegin()){ if(!_sdBegin()){
store.play_mode=PM_WEB; store.play_mode=PM_WEB;
Serial.println("SD Mount Failed"); Serial.println("SD Mount Failed");
@@ -178,14 +303,13 @@ void Config::initPlaylistMode(){
} }
SDinit = true; SDinit = true;
} }
}else{ #else
store.play_mode=PM_WEB; store.play_mode=PM_WEB;
} #endif
if(getMode()==PM_WEB && !emptyFS) initPlaylist(); if(getMode()==PM_WEB && !emptyFS) initPlaylist();
if (store.lastStation == 0 && store.countStation > 0) { if (store.lastStation == 0 && store.countStation > 0) {
store.lastStation = getMode()==PM_WEB?1:random(1, store.countStation); store.lastStation = getMode()==PM_WEB?1:random(1, store.countStation);
//save();
} }
save(); save();
_bootDone = true; _bootDone = true;
@@ -346,45 +470,6 @@ void Config::setSnuffle(bool sn){
if(sdSnuffle) player.next(); if(sdSnuffle) player.next();
} }
void Config::changeMode(int newmode){
#if SDC_CS!=255
if(SDC_CS==255) return;
if(!SDinit) {
_mountSD();
if(!SDinit){
Serial.println("##[ERROR]#\tSD Not Found");
netserver.requestOnChange(GETPLAYERMODE, 0);
return;
}
}
if(getMode()==PM_SDCARD) store.lastStation = config.backupLastStation;
if(newmode<0){
store.play_mode++;
if(getMode() > MAX_PLAY_MODE){
store.play_mode=0;
}
}else{
store.play_mode=(playMode_e)newmode;
}
save();
if(getMode()==PM_SDCARD && _cardStatus!=CS_MOUNTED){
display.putRequest(NEWMODE, SDCHANGE);
while(display.mode()!=SDCHANGE)
delay(10);
delay(50);
}
initPlaylistMode();
if (store.smartstart == 1) player.sendCommand({PR_PLAY, store.lastStation});
else
player.sendCommand({PR_STOP, 0});
netserver.requestOnChange(GETPLAYERMODE, 0);
netserver.requestOnChange(GETMODE, 0);
display.putRequest(NEWMODE, PLAYER);
display.putRequest(NEWSTATION);
#endif
}
#if IR_PIN!=255 #if IR_PIN!=255
void Config::saveIR(){ void Config::saveIR(){
eepromWrite(EEPROM_START_IR, ircodes); eepromWrite(EEPROM_START_IR, ircodes);
@@ -485,15 +570,6 @@ void Config::initPlaylist() {
} }
} }
bool endsWith (const char* base, const char* str) {
int slen = strlen(str) - 1;
const char *p = base + strlen(base) - 1;
while(p > base && isspace(*p)) p--;
p -= slen;
if (p < base) return false;
return (strncmp(p, str, slen) == 0);
}
bool Config::checkNoMedia(const char* path){ bool Config::checkNoMedia(const char* path){
char nomedia[BUFLEN]= {0}; char nomedia[BUFLEN]= {0};
strlcat(nomedia, path, BUFLEN); strlcat(nomedia, path, BUFLEN);
@@ -502,75 +578,6 @@ bool Config::checkNoMedia(const char* path){
return nm; return nm;
} }
void Config::listSD(File &plSDfile, File &plSDindex, const char * dirname, uint8_t levels){
sdog.takeMutex(); File root = SD.open(dirname); sdog.giveMutex();
if(!root){
Serial.println("##[ERROR]#\tFailed to open directory");
return;
}
if(!root.isDirectory()){
Serial.println("##[ERROR]#\tNot a directory");
return;
}
sdog.takeMutex(); File file = root.openNextFile(); sdog.giveMutex();
uint32_t pos = 0;
//vTaskDelay(5);
while(file){
sdog.takeMutex();
bool fid = file.isDirectory();
const char * fp = file.path();
const char * fn = file.name();
sdog.giveMutex();
if(fid){
if(levels && !checkNoMedia(fp)){
listSD(plSDfile, plSDindex, fp, levels -1);
}
} else {
if(endsWith(strlwr((char*)fn), ".mp3") || endsWith(fn, ".m4a") || endsWith(fn, ".aac") || endsWith(fn, ".wav") || endsWith(fn, ".flac")){
pos = plSDfile.position();
plSDfile.print(fn); plSDfile.print("\t"); plSDfile.print(fp); plSDfile.print("\t"); plSDfile.println(0);
plSDindex.write((byte *) &pos, 4);
}
}
sdog.takeMutex(); file = root.openNextFile(); sdog.giveMutex();
}
}
void Config::indexSDPlaylist() {
mountSDbusy = true;
if(SPIFFS.exists(PLAYLIST_SD_PATH)) SPIFFS.remove(PLAYLIST_SD_PATH);
if(SPIFFS.exists(INDEX_SD_PATH)) SPIFFS.remove(INDEX_SD_PATH);
File playlist = SPIFFS.open(PLAYLIST_SD_PATH, "w");
if (!playlist) {
mountSDbusy = false;
return;
}
File index = SPIFFS.open(INDEX_SD_PATH, "w");
listSD(playlist, index, "/", 2);
index.flush();
index.close();
playlist.flush();
playlist.close();
delay(50);
mountSDbusy = false;
}
void Config::initSDPlaylist(bool doIndex) {
store.countStation = 0;
if(doIndex) indexSDPlaylist();
if (SPIFFS.exists(INDEX_SD_PATH)) {
File index = SPIFFS.open(INDEX_SD_PATH, "r");
store.countStation = index.size() / 4;
if(doIndex){
store.lastStation = random(1, store.countStation);
backupSDStation = store.lastStation;
}
index.close();
save();
}
}
void Config::loadStation(uint16_t ls) { void Config::loadStation(uint16_t ls) {
char sName[BUFLEN], sUrl[BUFLEN]; char sName[BUFLEN], sUrl[BUFLEN];
int sOvol; int sOvol;
@@ -584,9 +591,10 @@ void Config::loadStation(uint16_t ls) {
if (ls > store.countStation) { if (ls > store.countStation) {
ls = 1; ls = 1;
} }
File playlist = SPIFFS.open(REAL_PLAYL, "r"); sdog.takeMutex();
File playlist = SDPLFS()->open(REAL_PLAYL, "r");
File index = SPIFFS.open(REAL_INDEX, "r"); File index = SDPLFS()->open(REAL_INDEX, "r");
index.seek((ls - 1) * 4, SeekSet); index.seek((ls - 1) * 4, SeekSet);
uint32_t pos; uint32_t pos;
@@ -603,11 +611,13 @@ void Config::loadStation(uint16_t ls) {
setLastStation(ls); setLastStation(ls);
} }
playlist.close(); playlist.close();
sdog.giveMutex();
} }
char * Config::stationByNum(uint16_t num){ char * Config::stationByNum(uint16_t num){
File playlist = SPIFFS.open(REAL_PLAYL, "r"); sdog.takeMutex();
File index = SPIFFS.open(REAL_INDEX, "r"); File playlist = SDPLFS()->open(REAL_PLAYL, "r");
File index = SDPLFS()->open(REAL_INDEX, "r");
index.seek((num - 1) * 4, SeekSet); index.seek((num - 1) * 4, SeekSet);
uint32_t pos; uint32_t pos;
memset(_stationBuf, 0, BUFLEN/2); memset(_stationBuf, 0, BUFLEN/2);
@@ -616,6 +626,7 @@ char * Config::stationByNum(uint16_t num){
playlist.seek(pos, SeekSet); playlist.seek(pos, SeekSet);
strncpy(_stationBuf, playlist.readStringUntil('\t').c_str(), BUFLEN/2); strncpy(_stationBuf, playlist.readStringUntil('\t').c_str(), BUFLEN/2);
playlist.close(); playlist.close();
sdog.giveMutex();
return _stationBuf; return _stationBuf;
} }
@@ -626,8 +637,10 @@ uint8_t Config::fillPlMenu(int from, uint8_t count, bool fromNextion) {
if (store.countStation == 0) { if (store.countStation == 0) {
return 0; return 0;
} }
File playlist = SPIFFS.open(REAL_PLAYL, "r"); sdog.takeMutex();
File index = SPIFFS.open(REAL_INDEX, "r"); File playlist = SDPLFS()->open(REAL_PLAYL, "r");
File index = SDPLFS()->open(REAL_INDEX, "r");
sdog.giveMutex();
while (true) { while (true) {
if (ls < 1) { if (ls < 1) {
ls++; ls++;
@@ -639,15 +652,19 @@ uint8_t Config::fillPlMenu(int from, uint8_t count, bool fromNextion) {
continue; continue;
} }
if (!finded) { if (!finded) {
sdog.takeMutex();
index.seek((ls - 1) * 4, SeekSet); index.seek((ls - 1) * 4, SeekSet);
uint32_t pos; uint32_t pos;
index.readBytes((char *) &pos, 4); index.readBytes((char *) &pos, 4);
finded = true; finded = true;
index.close(); index.close();
playlist.seek(pos, SeekSet); playlist.seek(pos, SeekSet);
sdog.giveMutex();
} }
while (playlist.available()) { while (playlist.available()) {
sdog.takeMutex();
String stationName = playlist.readStringUntil('\n'); String stationName = playlist.readStringUntil('\n');
sdog.giveMutex();
stationName = stationName.substring(0, stationName.indexOf('\t')); stationName = stationName.substring(0, stationName.indexOf('\t'));
if(config.store.numplaylist) stationName = String(from+c)+" "+stationName; if(config.store.numplaylist) stationName = String(from+c)+" "+stationName;
if(!fromNextion) display.printPLitem(c, stationName.c_str()); if(!fromNextion) display.printPLitem(c, stationName.c_str());
@@ -660,7 +677,7 @@ uint8_t Config::fillPlMenu(int from, uint8_t count, bool fromNextion) {
break; break;
} }
return c; return c;
playlist.close(); sdog.takeMutex();playlist.close();sdog.giveMutex();
} }
bool Config::parseCSV(const char* line, char* name, char* url, int &ovol) { bool Config::parseCSV(const char* line, char* name, char* url, int &ovol) {

View File

@@ -35,6 +35,10 @@
#define REAL_INDEX getMode()==PM_WEB?INDEX_PATH:INDEX_SD_PATH #define REAL_INDEX getMode()==PM_WEB?INDEX_PATH:INDEX_SD_PATH
#define MAX_PLAY_MODE 1 #define MAX_PLAY_MODE 1
#if SDC_CS!=255
#define USE_SD
#endif
enum playMode_e : uint8_t { PM_WEB=0, PM_SDCARD=1 }; enum playMode_e : uint8_t { PM_WEB=0, PM_SDCARD=1 };
enum cardStatus_e : uint8_t { CS_NONE=0, CS_PRESENT=1, CS_MOUNTED=2, CS_EJECTED=3 }; enum cardStatus_e : uint8_t { CS_NONE=0, CS_PRESENT=1, CS_MOUNTED=2, CS_EJECTED=3 };
enum BitrateFormat { BF_UNCNOWN, BF_MP3, BF_AAC, BF_FLAC, BF_OGG, BF_WAV }; enum BitrateFormat { BF_UNCNOWN, BF_MP3, BF_AAC, BF_FLAC, BF_OGG, BF_WAV };
@@ -164,7 +168,6 @@ class Config {
bool sdSnuffle; bool sdSnuffle;
bool emptyFS; bool emptyFS;
bool SDinit; bool SDinit;
bool mountSDbusy;
public: public:
Config() {}; Config() {};
void save(); void save();
@@ -194,8 +197,12 @@ class Config {
void setBitrateFormat(BitrateFormat fmt) { configFmt = fmt; } void setBitrateFormat(BitrateFormat fmt) { configFmt = fmt; }
void initPlaylist(); void initPlaylist();
void indexPlaylist(); void indexPlaylist();
#ifdef USE_SD
void initSDPlaylist(bool doIndex = true); void initSDPlaylist(bool doIndex = true);
void indexSDPlaylist(); void indexSDPlaylist();
void changeMode(int newmode=-1);
void checkSD();
#endif
uint8_t fillPlMenu(int from, uint8_t count, bool fromNextion=false); uint8_t fillPlMenu(int from, uint8_t count, bool fromNextion=false);
char * stationByNum(uint16_t num); char * stationByNum(uint16_t num);
void setTimezone(int8_t tzh, int8_t tzm); void setTimezone(int8_t tzh, int8_t tzm);
@@ -207,29 +214,32 @@ class Config {
void bootInfo(); void bootInfo();
void doSleepW(); void doSleepW();
void setSnuffle(bool sn); void setSnuffle(bool sn);
void changeMode(int newmode=-1);
uint8_t getMode() { return store.play_mode & 0b11; } uint8_t getMode() { return store.play_mode & 0b11; }
void initPlaylistMode(); void initPlaylistMode();
void checkSD();
cardStatus_e getSDStatus(){ return _cardStatus; }; cardStatus_e getSDStatus(){ return _cardStatus; };
void clearCardStatus() { if(_cardStatus!=CS_NONE) _cardStatus=CS_NONE; } void clearCardStatus() { if(_cardStatus!=CS_NONE) _cardStatus=CS_NONE; }
bool spiffsCleanup(); bool spiffsCleanup();
FS* SDPLFS(){ return _SDplaylistFS; }
private: private:
template <class T> int eepromWrite(int ee, const T& value); template <class T> int eepromWrite(int ee, const T& value);
template <class T> int eepromRead(int ee, T& value); template <class T> int eepromRead(int ee, T& value);
cardStatus_e _cardStatus; cardStatus_e _cardStatus;
bool _bootDone; bool _bootDone;
FS* _SDplaylistFS;
void setDefaults(); void setDefaults();
Ticker _sleepTimer; Ticker _sleepTimer;
static void doSleep(); static void doSleep();
uint16_t color565(uint8_t r, uint8_t g, uint8_t b); uint16_t color565(uint8_t r, uint8_t g, uint8_t b);
#ifdef USE_SD
void listSD(File &plSDfile, File &plSDindex, const char * dirname, uint8_t levels); void listSD(File &plSDfile, File &plSDindex, const char * dirname, uint8_t levels);
bool checkNoMedia(const char* path);
void _initHW();
bool _isFSempty();
bool _sdCardIsConnected(); bool _sdCardIsConnected();
void _mountSD(); void _mountSD();
bool _sdBegin(); bool _sdBegin();
#endif
bool checkNoMedia(const char* path);
void _initHW();
bool _isFSempty();
char _stationBuf[BUFLEN/2]; char _stationBuf[BUFLEN/2];
}; };

View File

@@ -503,10 +503,12 @@ void onBtnClick(int id) {
} }
break; break;
} }
#ifdef USE_SD
case EVT_BTNMODE: { case EVT_BTNMODE: {
config.changeMode(); config.changeMode();
break; break;
} }
#endif
default: break; default: break;
} }
} }

View File

@@ -424,6 +424,10 @@ void Display::loop() {
if(_bootstring) _bootstring->setText(const_waitForSD); if(_bootstring) _bootstring->setText(const_waitForSD);
break; break;
} }
case SDFILEINDEX: {
if(_mode == SDCHANGE) _nums.setText(request.payload, "%d");
break;
}
case DSPRSSI: if(_rssi){ _setRSSI(request.payload); } if (_heapbar && config.store.audioinfo) _heapbar->setValue(player.isRunning()?player.inBufferFilled():0); break; case DSPRSSI: if(_rssi){ _setRSSI(request.payload); } if (_heapbar && config.store.audioinfo) _heapbar->setValue(player.isRunning()?player.inBufferFilled():0); break;
case PSTART: _layoutChange(true); break; case PSTART: _layoutChange(true); break;
case PSTOP: _layoutChange(false); break; case PSTOP: _layoutChange(false); break;

View File

@@ -12,7 +12,7 @@ enum displayMode_e { PLAYER, VOL, STATIONS, NUMBERS, LOST, UPDATING, INFO, SETTI
enum pages_e : uint8_t { PG_PLAYER=0, PG_DIALOG=1, PG_PLAYLIST=2 }; enum pages_e : uint8_t { PG_PLAYER=0, PG_DIALOG=1, PG_PLAYLIST=2 };
//enum dialogType_e : uint8_t { DG_NONE=0, DG_VOLUME=1, DG_LOST=2, DG_UPDATING=3, DG_NEXTION=4 }; //enum dialogType_e : uint8_t { DG_NONE=0, DG_VOLUME=1, DG_LOST=2, DG_UPDATING=3, DG_NEXTION=4 };
enum displayRequestType_e { BOOTSTRING, NEWMODE, CLOCK, NEWTITLE, NEWSTATION, NEXTSTATION, DRAWPLAYLIST, DRAWVOL, DBITRATE, AUDIOINFO, SHOWVUMETER, DSPRSSI, SHOWWEATHER, NEWWEATHER, PSTOP, PSTART, DSP_START, WAITFORSD }; enum displayRequestType_e { BOOTSTRING, NEWMODE, CLOCK, NEWTITLE, NEWSTATION, NEXTSTATION, DRAWPLAYLIST, DRAWVOL, DBITRATE, AUDIOINFO, SHOWVUMETER, DSPRSSI, SHOWWEATHER, NEWWEATHER, PSTOP, PSTART, DSP_START, WAITFORSD, SDFILEINDEX };
struct requestParams_t struct requestParams_t
{ {
displayRequestType_e type; displayRequestType_e type;

View File

@@ -10,11 +10,19 @@
#include "mqtt.h" #include "mqtt.h"
#include "controls.h" #include "controls.h"
#include <Update.h> #include <Update.h>
#include "spidog.h"
#ifndef MIN_MALLOC #ifndef MIN_MALLOC
#define MIN_MALLOC 24112 #define MIN_MALLOC 24112
#endif #endif
#ifdef USE_SD
#define CARDLOCK() sdog.tm()
#define CARDUNLOCK() sdog.gm()
#else
#define CARDLOCK() {}
#define CARDUNLOCK() {}
#endif
//#define CORS_DEBUG //#define CORS_DEBUG
NetServer netserver; NetServer netserver;
@@ -152,17 +160,33 @@ void NetServer::beginUpload(AsyncWebServerRequest *request) {
} }
size_t NetServer::chunkedHtmlPageCallback(uint8_t* buffer, size_t maxLen, size_t index){ size_t NetServer::chunkedHtmlPageCallback(uint8_t* buffer, size_t maxLen, size_t index){
File requiredfile = SPIFFS.open(netserver.chunkedPathBuffer, "r"); File requiredfile;
bool sdpl = strcmp(netserver.chunkedPathBuffer, PLAYLIST_SD_PATH) == 0;
if(sdpl){
CARDLOCK();
requiredfile = config.SDPLFS()->open(netserver.chunkedPathBuffer, "r");
CARDUNLOCK();
}else{
requiredfile = SPIFFS.open(netserver.chunkedPathBuffer, "r");
}
if (!requiredfile) return 0; if (!requiredfile) return 0;
if(sdpl) CARDLOCK();
size_t filesize = requiredfile.size(); size_t filesize = requiredfile.size();
if(sdpl) CARDUNLOCK();
size_t needread = filesize - index; size_t needread = filesize - index;
if (!needread) return 0; if (!needread) {
if(sdpl) { CARDLOCK(); requiredfile.close(); CARDUNLOCK(); }
return 0;
}
size_t canread = (needread > maxLen) ? maxLen : needread; size_t canread = (needread > maxLen) ? maxLen : needread;
DBGVB("[%s] seek to %d in %s and read %d bytes with maxLen=%d", __func__, index, netserver.chunkedPathBuffer, canread, maxLen); DBGVB("[%s] seek to %d in %s and read %d bytes with maxLen=%d", __func__, index, netserver.chunkedPathBuffer, canread, maxLen);
if(sdpl) CARDLOCK();
requiredfile.seek(index, SeekSet); requiredfile.seek(index, SeekSet);
//vTaskDelay(1);
requiredfile.read(buffer, canread); requiredfile.read(buffer, canread);
index += canread; index += canread;
if (requiredfile) requiredfile.close(); if (requiredfile) requiredfile.close();
if(sdpl) CARDUNLOCK();
return canread; return canread;
} }
@@ -204,10 +228,12 @@ void NetServer::processQueue(){
switch (request.type) { switch (request.type) {
case PLAYLIST: getPlaylist(clientId); break; case PLAYLIST: getPlaylist(clientId); break;
case PLAYLISTSAVED: { case PLAYLISTSAVED: {
#ifdef USE_SD
if(config.getMode()==PM_SDCARD) { if(config.getMode()==PM_SDCARD) {
// config.indexSDPlaylist(); // config.indexSDPlaylist();
config.initSDPlaylist(); config.initSDPlaylist();
} }
#endif
if(config.getMode()==PM_WEB){ if(config.getMode()==PM_WEB){
config.indexPlaylist(); config.indexPlaylist();
config.initPlaylist(); config.initPlaylist();
@@ -268,7 +294,9 @@ void NetServer::processQueue(){
case BALANCE: sprintf (wsbuf, "{\"balance\": %d}", config.store.balance); break; case BALANCE: sprintf (wsbuf, "{\"balance\": %d}", config.store.balance); break;
case SDINIT: sprintf (wsbuf, "{\"sdinit\": %d}", SDC_CS!=255); break; case SDINIT: sprintf (wsbuf, "{\"sdinit\": %d}", SDC_CS!=255); break;
case GETPLAYERMODE: sprintf (wsbuf, "{\"playermode\": \"%s\"}", config.getMode()==PM_SDCARD?"modesd":"modeweb"); break; case GETPLAYERMODE: sprintf (wsbuf, "{\"playermode\": \"%s\"}", config.getMode()==PM_SDCARD?"modesd":"modeweb"); break;
#ifdef USE_SD
case CHANGEMODE: config.changeMode(newConfigMode); return; break; case CHANGEMODE: config.changeMode(newConfigMode); return; break;
#endif
default: break; default: break;
} }
if (strlen(wsbuf) > 0) { if (strlen(wsbuf) > 0) {
@@ -685,6 +713,10 @@ void NetServer::requestOnChange(requestType_e request, uint8_t clientId) {
xQueueSend(nsQueue, &nsrequest, portMAX_DELAY); xQueueSend(nsQueue, &nsrequest, portMAX_DELAY);
} }
void NetServer::resetQueue(){
xQueueReset(nsQueue);
}
String processor(const String& var) { // %Templates% String processor(const String& var) { // %Templates%
if (var == "ACTION") return (network.status == CONNECTED && !config.emptyFS)?"webboard":""; if (var == "ACTION") return (network.status == CONNECTED && !config.emptyFS)?"webboard":"";
if (var == "UPLOADWIFI") return (network.status == CONNECTED)?" hidden":""; if (var == "UPLOADWIFI") return (network.status == CONNECTED)?" hidden":"";
@@ -698,8 +730,8 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index,
if(filename!="tempwifi.csv"){ if(filename!="tempwifi.csv"){
if(SPIFFS.exists(PLAYLIST_PATH)) SPIFFS.remove(PLAYLIST_PATH); if(SPIFFS.exists(PLAYLIST_PATH)) SPIFFS.remove(PLAYLIST_PATH);
if(SPIFFS.exists(INDEX_PATH)) SPIFFS.remove(INDEX_PATH); if(SPIFFS.exists(INDEX_PATH)) SPIFFS.remove(INDEX_PATH);
if(SPIFFS.exists(PLAYLIST_SD_PATH)) SPIFFS.remove(PLAYLIST_PATH); if(SPIFFS.exists(PLAYLIST_SD_PATH)) SPIFFS.remove(PLAYLIST_SD_PATH);
if(SPIFFS.exists(INDEX_SD_PATH)) SPIFFS.remove(INDEX_PATH); if(SPIFFS.exists(INDEX_SD_PATH)) SPIFFS.remove(INDEX_SD_PATH);
config.clearCardStatus(); config.clearCardStatus();
} }
freeSpace = (float)SPIFFS.totalBytes()/100*68-SPIFFS.usedBytes(); freeSpace = (float)SPIFFS.totalBytes()/100*68-SPIFFS.usedBytes();

View File

@@ -66,6 +66,7 @@ class NetServer {
#if IR_PIN!=255 #if IR_PIN!=255
void irToWs(const char* protocol, uint64_t irvalue); void irToWs(const char* protocol, uint64_t irvalue);
void irValsToWs(); void irValsToWs();
void resetQueue();
#endif #endif
private: private:
requestType_e request; requestType_e request;

View File

@@ -47,8 +47,8 @@ void ticks() {
netserver.setRSSI(WiFi.RSSI()); netserver.setRSSI(WiFi.RSSI());
netserver.requestOnChange(NRSSI, 0); netserver.requestOnChange(NRSSI, 0);
display.putRequest(DSPRSSI, netserver.getRSSI()); display.putRequest(DSPRSSI, netserver.getRSSI());
#if SDC_CS!=255 #ifdef USE_SD
if(!config.mountSDbusy) player.sendCommand({PR_CHECKSD, 0}); if(display.mode()!=SDCHANGE) player.sendCommand({PR_CHECKSD, 0});
#endif #endif
} }
} }

View File

@@ -1,7 +1,7 @@
#ifndef options_h #ifndef options_h
#define options_h #define options_h
#define YOVERSION "0.9.220" #define YOVERSION "0.9.235"
/******************************************************* /*******************************************************
DO NOT EDIT THIS FILE. DO NOT EDIT THIS FILE.
@@ -311,6 +311,9 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti
#ifndef SD_AUTOPLAY #ifndef SD_AUTOPLAY
#define SD_AUTOPLAY true // auto play from SD card when inserted #define SD_AUTOPLAY true // auto play from SD card when inserted
#endif #endif
#ifndef SD_MAX_LEVELS
#define SD_MAX_LEVELS 3 // search depth for files on the SD card
#endif
/* /*
*** ST7735 display submodel *** *** ST7735 display submodel ***
INITR_BLACKTAB // 1.8' https://aliexpress.ru/item/1005002822797745.html INITR_BLACKTAB // 1.8' https://aliexpress.ru/item/1005002822797745.html

View File

@@ -139,10 +139,12 @@ void Player::loop() {
Audio::setVolume(volToI2S(requestP.payload)); Audio::setVolume(volToI2S(requestP.payload));
break; break;
} }
#ifdef USE_SD
case PR_CHECKSD: { case PR_CHECKSD: {
config.checkSD(); config.checkSD();
break; break;
} }
#endif
default: break; default: break;
} }
} }
@@ -173,7 +175,7 @@ void Player::_play(uint16_t stationId) {
setError(""); setError("");
remoteStationName = false; remoteStationName = false;
config.setDspOn(1); config.setDspOn(1);
display.putRequest(PSTOP); //display.putRequest(PSTOP);
setOutputPins(false); setOutputPins(false);
config.setTitle(config.getMode()==PM_WEB?const_PlConnect:""); config.setTitle(config.getMode()==PM_WEB?const_PlConnect:"");
config.station.bitrate=0; config.station.bitrate=0;

View File

@@ -7,19 +7,20 @@ SPIDog::SPIDog() {
} }
bool SPIDog::begin(){ bool SPIDog::begin(){
if(_spiMutex==NULL){
_spiMutex = xSemaphoreCreateMutex(); _spiMutex = xSemaphoreCreateMutex();
return (_spiMutex != NULL); if(_spiMutex==NULL) return false;
}
return true;
} }
bool SPIDog::takeMutex(){ bool SPIDog::takeMutex(){
if(_spiMutex == NULL) { if(_spiMutex == NULL) {
return false; return false;
} }
if(xSemaphoreTake(_spiMutex, SDOG_PORT_DELAY) == pdTRUE){ do { } while (xSemaphoreTake(_spiMutex, portMAX_DELAY) != pdPASS);
_busy = true; _busy = true;
return true; return true;
}
return false;
} }
void SPIDog::giveMutex(){ void SPIDog::giveMutex(){
@@ -27,6 +28,13 @@ void SPIDog::giveMutex(){
_busy = false; _busy = false;
} }
bool SPIDog::canTake(){
if(_spiMutex == NULL) {
return false;
}
return xSemaphoreTake(_spiMutex, 0) == pdPASS;
}
bool SPIDog::breakMutex(uint8_t ticks){ bool SPIDog::breakMutex(uint8_t ticks){
if(!_busy){ if(!_busy){
giveMutex(); giveMutex();

View File

@@ -17,6 +17,7 @@ class SPIDog {
bool begin(); bool begin();
bool takeMutex(); bool takeMutex();
void giveMutex(); void giveMutex();
bool canTake();
bool breakMutex(uint8_t ticks=5); bool breakMutex(uint8_t ticks=5);
bool busy() { return _busy; } bool busy() { return _busy; }
bool tm() { return takeMutex(); } bool tm() { return takeMutex(); }