diff --git a/README.md b/README.md index 7b67165..774e3d2 100644 --- a/README.md +++ b/README.md @@ -234,6 +234,20 @@ Work is in progress... --- ## Version history +### 0.9.511 +**!!! a [full update](#update-over-web-interface) with Sketch data upload is required. After updating please press CTRL+F5 in browser !!!** +In this version, the contents of the data/www directory have changed, so that the first time you flash it, you will be greeted by WEB Board Uploader. Just upload all the files from data/www (11 pcs) to it +- fixed a bug with saving smartstart mode +- fixed a bug with no restart when initially uploading files to spiffs +- fixed a bug with hanging on unavailable hosts +- fixed a bug with attempting to connect with an empty playlist +- fixed a bug with passing strings with quotes in mqtt +- fixing some other bugs +- rewritten the web interface (almost), added bugs 👍 +- added listening to links in the browser in playlistEditor-e +- added reboot format and reset buttons to the settings +- the beginnings of theming (theme.css) + ### 0.9.434 - fixed the issue with exiting Screensaver Blank Screen mode via button presses and IR commands. - reduced the minimum frequency for tone control on I2S modules to 80Hz. diff --git a/yoRadio/data/www/dragpl.js.gz b/yoRadio/data/www/dragpl.js.gz index 55df2e0..57bd7a8 100644 Binary files a/yoRadio/data/www/dragpl.js.gz and b/yoRadio/data/www/dragpl.js.gz differ diff --git a/yoRadio/data/www/elogo.png b/yoRadio/data/www/elogo.png deleted file mode 100644 index 9d9d42c..0000000 Binary files a/yoRadio/data/www/elogo.png and /dev/null differ diff --git a/yoRadio/data/www/elogo84.png b/yoRadio/data/www/elogo84.png deleted file mode 100644 index ee369bf..0000000 Binary files a/yoRadio/data/www/elogo84.png and /dev/null differ diff --git a/yoRadio/data/www/index.html b/yoRadio/data/www/index.html deleted file mode 100644 index 0b454dc..0000000 --- a/yoRadio/data/www/index.html +++ /dev/null @@ -1,107 +0,0 @@ - - - - - - - - - - ёRadio - Player - - - -
- - -
-
-
 
-
 
-
-
-
-
-
-
-
-
-
- -
-
WebSD
- -
- -
-
volume: 0
-
bitrate: 0kBit
-
rssi: 0dBm
-
-
    -
-
-
- -
-
powered by ёRadio | v%VERSION%
-
- - - - diff --git a/yoRadio/data/www/ir.css.gz b/yoRadio/data/www/ir.css.gz index c901046..eb74a48 100644 Binary files a/yoRadio/data/www/ir.css.gz and b/yoRadio/data/www/ir.css.gz differ diff --git a/yoRadio/data/www/ir.html b/yoRadio/data/www/ir.html deleted file mode 100644 index 89b451d..0000000 --- a/yoRadio/data/www/ir.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - ёRadio - IR Recorder - - - -
-

IR Recorder

-
-
-
-
-
-
-
1
2
3
-
4
5
6
-
7
8
9
-
*
0
#
-
-
-

Welcome to IR Recorder!

- Press the button on the left
to record the code. - DONE -
- -
-
-
-
powered by ёRadio | v%VERSION%
-
- - diff --git a/yoRadio/data/www/ir.js.gz b/yoRadio/data/www/ir.js.gz index 6a85b00..0addc3d 100644 Binary files a/yoRadio/data/www/ir.js.gz and b/yoRadio/data/www/ir.js.gz differ diff --git a/yoRadio/data/www/irrecord.html.gz b/yoRadio/data/www/irrecord.html.gz new file mode 100644 index 0000000..5ed70a4 Binary files /dev/null and b/yoRadio/data/www/irrecord.html.gz differ diff --git a/yoRadio/data/www/logo.svg.gz b/yoRadio/data/www/logo.svg.gz new file mode 100644 index 0000000..83cd879 Binary files /dev/null and b/yoRadio/data/www/logo.svg.gz differ diff --git a/yoRadio/data/www/options.html.gz b/yoRadio/data/www/options.html.gz new file mode 100644 index 0000000..1cba12c Binary files /dev/null and b/yoRadio/data/www/options.html.gz differ diff --git a/yoRadio/data/www/player.html.gz b/yoRadio/data/www/player.html.gz new file mode 100644 index 0000000..66c47f5 Binary files /dev/null and b/yoRadio/data/www/player.html.gz differ diff --git a/yoRadio/data/www/script.js.gz b/yoRadio/data/www/script.js.gz index 7b10373..656b931 100644 Binary files a/yoRadio/data/www/script.js.gz and b/yoRadio/data/www/script.js.gz differ diff --git a/yoRadio/data/www/settings.css.gz b/yoRadio/data/www/settings.css.gz deleted file mode 100644 index 40da547..0000000 Binary files a/yoRadio/data/www/settings.css.gz and /dev/null differ diff --git a/yoRadio/data/www/settings.html b/yoRadio/data/www/settings.html deleted file mode 100644 index edd2560..0000000 --- a/yoRadio/data/www/settings.html +++ /dev/null @@ -1,261 +0,0 @@ - - - - - - - - - - - ёRadio - Settings - - - -
-

SёTTINGS

- -
-
- - - - - - -
 
-
-
- -
powered by ёRadio | v%VERSION%
-
- - diff --git a/yoRadio/data/www/style.css.gz b/yoRadio/data/www/style.css.gz index 01bbd1a..210bbf3 100644 Binary files a/yoRadio/data/www/style.css.gz and b/yoRadio/data/www/style.css.gz differ diff --git a/yoRadio/data/www/theme.css b/yoRadio/data/www/theme.css new file mode 100644 index 0000000..185a6c4 --- /dev/null +++ b/yoRadio/data/www/theme.css @@ -0,0 +1,16 @@ +:root { +--main-bg-color: #010101; +--main-text-color: #cccccc; +--main-hl-color: #ffffff; +--odd-bg-color: #272727; +--accent-color: #e3d25f; +--accent-dark: #ad9838; +--accent-text-color: #000000; +--input-border: #2d2d2d; +--logo: #e3d25f; +--logo-red: #ff0000; +--logo-shadow: #a89c46; +--playlist-hover: #323232; +--section-gradient: #111111; +--section-border: #555555; +} diff --git a/yoRadio/data/www/update.html b/yoRadio/data/www/update.html deleted file mode 100644 index a522f3f..0000000 --- a/yoRadio/data/www/update.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - ёRadio - Update - - - -
- -
-
-
Please choose firmware or SPIFFS *.bin file
-
-
- - -
-
- - -
-
- - Cancel -
-
-
powered by ёRadio | v%VERSION%
-
- - - diff --git a/yoRadio/data/www/updform.html.gz b/yoRadio/data/www/updform.html.gz new file mode 100644 index 0000000..4206d17 Binary files /dev/null and b/yoRadio/data/www/updform.html.gz differ diff --git a/yoRadio/src/audioI2S/Audio.cpp b/yoRadio/src/audioI2S/Audio.cpp index 8e82bfe..f98fdf1 100644 --- a/yoRadio/src/audioI2S/Audio.cpp +++ b/yoRadio/src/audioI2S/Audio.cpp @@ -3604,15 +3604,27 @@ void Audio::playAudioData(){ } //--------------------------------------------------------------------------------------------------------------------- bool Audio::parseHttpResponseHeader() { // this is the response to a GET / request - + static uint32_t notavailablefor = 0; if(getDatamode() != HTTP_RESPONSE_HEADER) return false; - if(_client->available() == 0) return false; - + if(_client->available() == 0) { + if (notavailablefor == 0) notavailablefor = millis(); + if (millis() - notavailablefor > HEADER_TIMEOUT) { + notavailablefor = 0; + if(audio_showstation) audio_showstation(""); + if(audio_icydescription) audio_icydescription(""); + if(audio_icyurl) audio_icyurl(""); + AUDIO_ERROR("Host %s not available", m_lastHost); + m_lastHost[0] = '\0'; + setDatamode(AUDIO_NONE); + stopSong(); + } + return false; + } + notavailablefor = 0; char rhl[512]; // responseHeaderline bool ct_seen = false; uint32_t ctime = millis(); uint32_t timeout = 2500; // ms - while(true){ // outer while uint16_t pos = 0; if((millis() - ctime) > timeout) { diff --git a/yoRadio/src/audioI2S/AudioEx.h b/yoRadio/src/audioI2S/AudioEx.h index c25184c..448d23e 100644 --- a/yoRadio/src/audioI2S/AudioEx.h +++ b/yoRadio/src/audioI2S/AudioEx.h @@ -36,6 +36,10 @@ #define AUDIOBUFFER_MULTIPLIER2 8 #endif +#ifndef HEADER_TIMEOUT +#define HEADER_TIMEOUT 5000 +#endif + #if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) #include "hal/gpio_ll.h" #endif diff --git a/yoRadio/src/core/config.cpp b/yoRadio/src/core/config.cpp index 0f1cfdc..f478000 100644 --- a/yoRadio/src/core/config.cpp +++ b/yoRadio/src/core/config.cpp @@ -18,14 +18,22 @@ void u8fix(char *src){ } bool Config::_isFSempty() { - const char* reqiredFiles[] = {"dragpl.js.gz","elogo.png","elogo84.png","index.html", - "ir.css.gz","ir.html","ir.js.gz","script.js.gz", - "settings.css.gz","settings.html","style.css.gz","update.html"}; - const uint8_t reqiredFilesSize = 12; + const char* reqiredFiles[] = {"dragpl.js.gz","ir.css.gz","irrecord.html.gz","ir.js.gz","logo.svg.gz","options.html.gz","player.html.gz","script.js.gz", + "style.css.gz","updform.html.gz","theme.css"}; + const uint8_t reqiredFilesSize = 11; char fullpath[28]; + if(SPIFFS.exists("/www/settings.html")) SPIFFS.remove("/www/settings.html"); + if(SPIFFS.exists("/www/update.html")) SPIFFS.remove("/www/update.html"); + if(SPIFFS.exists("/www/index.html")) SPIFFS.remove("/www/index.html"); + if(SPIFFS.exists("/www/ir.html")) SPIFFS.remove("/www/ir.html"); + if(SPIFFS.exists("/www/elogo.png")) SPIFFS.remove("/www/elogo.png"); + if(SPIFFS.exists("/www/elogo84.png")) SPIFFS.remove("/www/elogo84.png"); for (uint8_t i=0; iexists(INDEX_SD_PATH)) { File index = SDPLFS()->open(INDEX_SD_PATH, "r"); - store.countStation = index.size() / 4; + //store.countStation = index.size() / 4; if(doIndex){ lastStation(_randomStation()); sdResumePos = 0; } index.close(); - saveValue(&store.countStation, store.countStation, true, true); + //saveValue(&store.countStation, store.countStation, true, true); } } @@ -190,6 +200,7 @@ bool Config::spiffsCleanup(){ void Config::initPlaylistMode(){ uint16_t _lastStation = 0; + uint16_t cs = playlistLength(); #ifdef USE_SD if(getMode()==PM_SDCARD){ if(!sdman.start()){ @@ -203,7 +214,8 @@ void Config::initPlaylistMode(){ initSDPlaylist(); if(_bootDone) Serial.println("done"); else BOOTLOG("done"); _lastStation = store.lastSdStation; - if(_lastStation>store.countStation && store.countStation>0){ + + if(_lastStation>cs && cs>0){ _lastStation=1; } if(_lastStation==0) { @@ -220,7 +232,7 @@ void Config::initPlaylistMode(){ #endif if(getMode()==PM_WEB && !emptyFS) initPlaylist(); log_i("%d" ,_lastStation); - if (_lastStation == 0 && store.countStation > 0) { + if (_lastStation == 0 && cs > 0) { _lastStation = getMode()==PM_WEB?1:_randomStation(); } lastStation(_lastStation); @@ -467,28 +479,37 @@ void Config::indexPlaylist() { } void Config::initPlaylist() { - store.countStation = 0; + //store.countStation = 0; if (!SPIFFS.exists(INDEX_PATH)) indexPlaylist(); - if (SPIFFS.exists(INDEX_PATH)) { + /*if (SPIFFS.exists(INDEX_PATH)) { File index = SPIFFS.open(INDEX_PATH, "r"); store.countStation = index.size() / 4; index.close(); saveValue(&store.countStation, store.countStation, true, true); - } + }*/ } - -void Config::loadStation(uint16_t ls) { +uint16_t Config::playlistLength(){ + uint16_t out = 0; + if (SDPLFS()->exists(REAL_INDEX)) { + File index = SDPLFS()->open(REAL_INDEX, "r"); + out = index.size() / 4; + index.close(); + } + return out; +} +bool Config::loadStation(uint16_t ls) { char sName[BUFLEN], sUrl[BUFLEN]; int sOvol; - if (store.countStation == 0) { + uint16_t cs = playlistLength(); + if (cs == 0) { memset(station.url, 0, BUFLEN); memset(station.name, 0, BUFLEN); strncpy(station.name, "ёRadio", BUFLEN); station.ovol = 0; - return; + return false; } - if (ls > store.countStation) { + if (ls > playlistLength()) { ls = 1; } File playlist = SDPLFS()->open(REAL_PLAYL, "r"); @@ -507,6 +528,7 @@ void Config::loadStation(uint16_t ls) { setLastStation(ls); } playlist.close(); + return true; } char * Config::stationByNum(uint16_t num){ @@ -527,7 +549,7 @@ uint8_t Config::fillPlMenu(int from, uint8_t count, bool fromNextion) { int ls = from; uint8_t c = 0; bool finded = false; - if (store.countStation == 0) { + if (playlistLength() == 0) { return 0; } File playlist = SDPLFS()->open(REAL_PLAYL, "r"); @@ -569,6 +591,19 @@ uint8_t Config::fillPlMenu(int from, uint8_t count, bool fromNextion) { return c; } +void Config::escapeQuotes(const char* input, char* output, size_t maxLen) { + size_t j = 0; + for (size_t i = 0; input[i] != '\0' && j < maxLen - 1; ++i) { + if (input[i] == '"' && j < maxLen - 2) { + output[j++] = '\\'; + output[j++] = '"'; + } else { + output[j++] = input[i]; + } + } + output[j] = '\0'; +} + bool Config::parseCSV(const char* line, char* name, char* url, int &ovol) { char *tmpe; const char* cursor = line; diff --git a/yoRadio/src/core/config.h b/yoRadio/src/core/config.h index 74bf37c..6c564ff 100644 --- a/yoRadio/src/core/config.h +++ b/yoRadio/src/core/config.h @@ -204,11 +204,12 @@ class Config { uint8_t setLastSSID(uint8_t val); void setTitle(const char* title); void setStation(const char* station); + void escapeQuotes(const char* input, char* output, size_t maxLen); bool parseCSV(const char* line, char* name, char* url, int &ovol); bool parseJSON(const char* line, char* name, char* url, int &ovol); bool parseWsCommand(const char* line, char* cmd, char* val, uint8_t cSize); bool parseSsid(const char* line, char* ssid, char* pass); - void loadStation(uint16_t station); + bool loadStation(uint16_t station); bool initNetwork(); bool saveWifi(); bool saveWifiFromNextion(const char* post); @@ -220,6 +221,7 @@ class Config { void initSDPlaylist(); void changeMode(int newmode=-1); #endif + uint16_t playlistLength(); uint16_t lastStation(){ return getMode()==PM_WEB?store.lastStation:store.lastSdStation; } diff --git a/yoRadio/src/core/controls.cpp b/yoRadio/src/core/controls.cpp index fb82302..7eb5803 100644 --- a/yoRadio/src/core/controls.cpp +++ b/yoRadio/src/core/controls.cpp @@ -223,7 +223,7 @@ void irNumber(uint8_t num) { display.putRequest(NEWMODE, NUMBERS); if (display.numOfNextStation > UINT16_MAX / 10) return; s = display.numOfNextStation * 10 + num; - if (s > config.store.countStation) return; + if (s > config.playlistLength()) return; display.numOfNextStation = s; display.putRequest(NEXTSTATION, s); } @@ -464,8 +464,9 @@ void controlsEvent(bool toRight, int8_t volDelta) { if (display.mode() == STATIONS) { display.resetQueue(); int p = toRight ? display.currentPlItem + 1 : display.currentPlItem - 1; - if (p < 1) p = config.store.countStation; - if (p > config.store.countStation) p = 1; + uint16_t cs = config.playlistLength(); + if (p < 1) p = cs; + if (p > cs) p = 1; display.currentPlItem = p; display.putRequest(DRAWPLAYLIST, p); } diff --git a/yoRadio/src/core/mqtt.cpp b/yoRadio/src/core/mqtt.cpp index a1180a3..497c83a 100644 --- a/yoRadio/src/core/mqtt.cpp +++ b/yoRadio/src/core/mqtt.cpp @@ -39,7 +39,11 @@ void mqttPublishStatus() { memset(topic, 0, 140); memset(status, 0, BUFLEN*3); sprintf(topic, "%s%s", MQTT_ROOT_TOPIC, "status"); - sprintf(status, "{\"status\": %d, \"station\": %d, \"name\": \"%s\", \"title\": \"%s\", \"on\": %d}", player.status()==PLAYING?1:0, config.lastStation(), config.station.name, config.station.title, config.store.dspon); + char name[BUFLEN*2]; + char title[BUFLEN*2]; + config.escapeQuotes(config.station.name, name, sizeof(name)); + config.escapeQuotes(config.station.title, title, sizeof(name)); + sprintf(status, "{\"status\": %d, \"station\": %d, \"name\": \"%s\", \"title\": \"%s\", \"on\": %d}", player.status()==PLAYING?1:0, config.lastStation(), name, title, config.store.dspon); mqttClient.publish(topic, 0, true, status); } } @@ -131,7 +135,8 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties int sb; if (sscanf(buf, "play %d", &sb) == 1 ) { if (sb < 1) sb = 1; - if (sb >= config.store.countStation) sb = config.store.countStation; + uint16_t cs = config.playlistLength(); + if (sb >= cs) sb = cs; player.sendCommand({PR_PLAY, (uint16_t)sb}); return; } diff --git a/yoRadio/src/core/netserver.cpp b/yoRadio/src/core/netserver.cpp index 0ec3c36..c11a543 100644 --- a/yoRadio/src/core/netserver.cpp +++ b/yoRadio/src/core/netserver.cpp @@ -21,7 +21,7 @@ #define NSQ_SEND_DELAY (TickType_t)100 //portMAX_DELAY? #endif -//#define CORS_DEBUG +//#define CORS_DEBUG //Enable CORS policy: 'Access-Control-Allow-Origin' (for testing) NetServer netserver; @@ -61,8 +61,10 @@ bool NetServer::begin(bool quiet) { irRecordEnable = false; nsQueue = xQueueCreate( 20, sizeof( nsRequestParams_t ) ); while(nsQueue==NULL){;} + if(config.emptyFS){ - webserver.on("/", HTTP_GET, [](AsyncWebServerRequest * request) { request->send_P(200, "text/html", emptyfs_html, processor); }); + webserver.on("/", HTTP_GET, [](AsyncWebServerRequest * request) { request->send_P(200, "text/html", emptyfs_html); }); + webserver.on("/webboard", HTTP_POST, [](AsyncWebServerRequest *request) { request->redirect("/"); ESP.restart(); }, handleUploadWeb); webserver.on("/", HTTP_POST, [](AsyncWebServerRequest *request) { if(request->arg("ssid")!="" && request->arg("pass")!=""){ char buf[BUFLEN]; @@ -73,11 +75,14 @@ bool NetServer::begin(bool quiet) { return; } request->redirect("/"); - ESP.restart(); + ESP.restart(); }, handleUploadWeb); }else{ webserver.on("/", HTTP_ANY, handleHTTPArgs); - webserver.on("/webboard", HTTP_GET, [](AsyncWebServerRequest * request) { request->send_P(200, "text/html", emptyfs_html, processor); }); + webserver.on("/settings.html", HTTP_GET, handleHTTPArgs); + webserver.on("/update.html", HTTP_GET, handleHTTPArgs); + webserver.on("/ir.html", HTTP_GET, handleHTTPArgs); + webserver.on("/webboard", HTTP_GET, [](AsyncWebServerRequest * request) { request->send_P(200, "text/html", emptyfs_html); }); webserver.on("/webboard", HTTP_POST, [](AsyncWebServerRequest *request) { request->redirect("/"); }, handleUploadWeb); } @@ -90,8 +95,13 @@ bool NetServer::begin(bool quiet) { webserver.on("/upload", HTTP_POST, beginUpload, handleUpload); webserver.on("/update", HTTP_GET, handleHTTPArgs); webserver.on("/update", HTTP_POST, beginUpdate, handleUpdate); - webserver.on("/settings", HTTP_GET, handleHTTPArgs); - if (IR_PIN != 255) webserver.on("/ir", HTTP_GET, handleHTTPArgs); + + webserver.on("/variables.js", HTTP_GET, [](AsyncWebServerRequest * request) { + char varjsbuf[BUFLEN]; + sprintf (varjsbuf, "var yoVersion='%s';\nvar formAction='%s';\nvar playMode='%s';\n", YOVERSION, (network.status == CONNECTED && !config.emptyFS)?"webboard":"", (network.status == CONNECTED)?"player":"ap"); + request->send(200, "text/html", varjsbuf); + }); + webserver.serveStatic("/", SPIFFS, "/www/").setCacheControl("max-age=31536000"); #ifdef CORS_DEBUG DefaultHeaders::Instance().addHeader(F("Access-Control-Allow-Origin"), F("*")); @@ -275,7 +285,7 @@ void NetServer::processQueue(){ sprintf (wsbuf, "{\"act\":[%s]}", act.c_str()); break; } - case GETMODE: sprintf (wsbuf, "{\"pmode\":\"%s\"}", network.status == CONNECTED ? "player" : "ap"); break; + //case STARTUP: sprintf (wsbuf, "{\"command\":\"startup\", \"payload\": {\"mode\":\"%s\", \"version\":\"%s\"}}", network.status == CONNECTED ? "player" : "ap", YOVERSION); break; case GETINDEX: { requestOnChange(STATION, clientId); requestOnChange(TITLE, clientId); @@ -334,11 +344,11 @@ void NetServer::processQueue(){ break; case DSPON: sprintf (wsbuf, "{\"dspontrue\":%d}", 1); break; case STATION: requestOnChange(STATIONNAME, clientId); requestOnChange(ITEM, clientId); break; - case STATIONNAME: sprintf (wsbuf, "{\"nameset\": \"%s\"}", config.station.name); break; + case STATIONNAME: sprintf (wsbuf, "{\"payload\":[{\"id\":\"nameset\", \"value\": \"%s\"}]}", config.station.name); break; case ITEM: sprintf (wsbuf, "{\"current\": %d}", config.lastStation()); break; - case TITLE: sprintf (wsbuf, "{\"meta\": \"%s\"}", config.station.title); telnet.printf("##CLI.META#: %s\n> ", config.station.title); break; - case VOLUME: sprintf (wsbuf, "{\"vol\": %d}", config.store.volume); telnet.printf("##CLI.VOL#: %d\n", config.store.volume); break; - case NRSSI: sprintf (wsbuf, "{\"rssi\": %d}", rssi); /*rssi = 255;*/ break; + case TITLE: sprintf (wsbuf, "{\"payload\":[{\"id\":\"meta\", \"value\": \"%s\"}]}", config.station.title); telnet.printf("##CLI.META#: %s\n> ", config.station.title); break; + case VOLUME: sprintf (wsbuf, "{\"payload\":[{\"id\":\"volume\", \"value\": %d}]}", config.store.volume); telnet.printf("##CLI.VOL#: %d\n", config.store.volume); break; + case NRSSI: sprintf (wsbuf, "{\"payload\":[{\"id\":\"rssi\", \"value\": %d}]}", rssi); /*rssi = 255;*/ break; case SDPOS: sprintf (wsbuf, "{\"sdpos\": %d,\"sdend\": %d,\"sdtpos\": %d,\"sdtend\": %d}", player.getFilePos(), player.getFileSize(), @@ -347,10 +357,10 @@ void NetServer::processQueue(){ break; case SDLEN: sprintf (wsbuf, "{\"sdmin\": %d,\"sdmax\": %d}", player.sd_min, player.sd_max); break; case SDSNUFFLE: sprintf (wsbuf, "{\"snuffle\": %d}", config.store.sdsnuffle); break; - case BITRATE: sprintf (wsbuf, "{\"bitrate\": %d, \"format\": \"%s\"}", config.station.bitrate, getFormat(config.configFmt)); break; - case MODE: sprintf (wsbuf, "{\"mode\": \"%s\"}", player.status() == PLAYING ? "playing" : "stopped"); telnet.info(); break; - case EQUALIZER: sprintf (wsbuf, "{\"bass\": %d, \"middle\": %d, \"trebble\": %d}", config.store.bass, config.store.middle, config.store.trebble); break; - case BALANCE: sprintf (wsbuf, "{\"balance\": %d}", config.store.balance); break; + case BITRATE: sprintf (wsbuf, "{\"payload\":[{\"id\":\"bitrate\", \"value\": %d}, {\"id\":\"fmt\", \"value\": \"%s\"}]}", config.station.bitrate, getFormat(config.configFmt)); break; + case MODE: sprintf (wsbuf, "{\"payload\":[{\"id\":\"playerwrap\", \"value\": \"%s\"}]}", player.status() == PLAYING ? "playing" : "stopped"); telnet.info(); break; + case EQUALIZER: sprintf (wsbuf, "{\"payload\":[{\"id\":\"bass\", \"value\": %d}, {\"id\": \"middle\", \"value\": %d}, {\"id\": \"trebble\", \"value\": %d}]}", config.store.bass, config.store.middle, config.store.trebble); break; + case BALANCE: sprintf (wsbuf, "{\"payload\":[{\"id\": \"balance\", \"value\": %d}]}", config.store.balance); break; case SDINIT: sprintf (wsbuf, "{\"sdinit\": %d}", SDC_CS!=255); break; case GETPLAYERMODE: sprintf (wsbuf, "{\"playermode\": \"%s\"}", config.getMode()==PM_SDCARD?"modesd":"modeweb"); break; #ifdef USE_SD @@ -405,7 +415,7 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client data[len] = 0; char cmd[65], val[65]; if (config.parseWsCommand((const char*)data, cmd, val, 65)) { - if (strcmp(cmd, "getmode") == 0 ) { requestOnChange(GETMODE, clientId); return; } + //if (strcmp(cmd, "getmode") == 0 ) { requestOnChange(GETMODE, clientId); return; } if (strcmp(cmd, "getindex") == 0 ) { requestOnChange(GETINDEX, clientId); return; } if (strcmp(cmd, "getsystem") == 0 ) { requestOnChange(GETSYSTEM, clientId); return; } if (strcmp(cmd, "getscreen") == 0 ) { requestOnChange(GETSCREEN, clientId); return; } @@ -433,6 +443,12 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client display.putRequest(SHOWVUMETER); return; } + if (strcmp(cmd, "prev") == 0) { player.prev(); return; } + if (strcmp(cmd, "toggle") == 0) { player.toggle(); return; } + if (strcmp(cmd, "next") == 0) { player.next(); return; } + if (strcmp(cmd, "volm") == 0) { player.stepVol(false); return; } + if (strcmp(cmd, "volp") == 0) { player.stepVol(true); return; } + if (strcmp(cmd, "play") == 0) { uint16_t valb = atoi(val); player.sendCommand({PR_PLAY, valb}); return; } if (strcmp(cmd, "softap") == 0) { uint8_t valb = atoi(val); config.saveValue(&config.store.softapdelay, valb); @@ -718,7 +734,7 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client netserver.requestOnChange(BALANCE, 0); return; } - if (strcmp(cmd, "treble") == 0) { + if (strcmp(cmd, "trebble") == 0) { int8_t valb = atoi(val); player.setTone(config.store.bass, config.store.middle, valb); config.setTone(config.store.bass, config.store.middle, valb); @@ -739,6 +755,19 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client netserver.requestOnChange(EQUALIZER, 0); return; } + if (strcmp(cmd, "reboot") == 0) { + ESP.restart(); + return; + } + if (strcmp(cmd, "format") == 0) { + SPIFFS.format(); + ESP.restart(); + return; + } + if (strcmp(cmd, "reset") == 0) { + config.reset(); + return; + } if (strcmp(cmd, "submitplaylist") == 0) { return; } @@ -884,7 +913,7 @@ void handleUploadWeb(AsyncWebServerRequest *request, String filename, size_t ind void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) { switch (type) { - case WS_EVT_CONNECT: if (config.store.audioinfo) Serial.printf("[WEBSOCKET] client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str()); break; + case WS_EVT_CONNECT: /*netserver.requestOnChange(STARTUP, client->id()); */if (config.store.audioinfo) Serial.printf("[WEBSOCKET] client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str()); break; case WS_EVT_DISCONNECT: if (config.store.audioinfo) Serial.printf("[WEBSOCKET] client #%u disconnected\n", client->id()); break; case WS_EVT_DATA: netserver.onWsMessage(arg, data, len, client->id()); break; case WS_EVT_PONG: @@ -912,17 +941,21 @@ void handleHTTPArgs(AsyncWebServerRequest * request) { } return; } + Serial.println(request->url()); if (strcmp(request->url().c_str(), "/") == 0 && request->params() == 0) { - netserver.chunkedHtmlPage(String(), request, network.status == CONNECTED ? "/www/index.html" : "/www/settings.html"); - return; - } - if (strcmp(request->url().c_str(), "/update") == 0 || strcmp(request->url().c_str(), "/settings") == 0 || strcmp(request->url().c_str(), "/ir") == 0) { - char buf[40] = { 0 }; - sprintf(buf, "/www%s.html", request->url().c_str()); - netserver.chunkedHtmlPage(String(), request, buf); + if(network.status == CONNECTED){ + request->send_P(200, "text/html", index_html); + }else{ + request->redirect("/settings.html"); + } + //netserver.chunkedHtmlPage(String(), request, network.status == CONNECTED ? "/www/index.html" : "/www/settings.html", false); return; } } + if (strcmp(request->url().c_str(), "/settings.html") == 0 || strcmp(request->url().c_str(), "/update.html") == 0 || strcmp(request->url().c_str(), "/ir.html") == 0){ + request->send_P(200, "text/html", index_html); + return; + } if (network.status == CONNECTED) { bool commandFound=false; if (request->hasArg("start")) { player.sendCommand({PR_PLAY, config.lastStation()}); commandFound=true; } @@ -969,7 +1002,8 @@ void handleHTTPArgs(AsyncWebServerRequest * request) { AsyncWebParameter* p = request->getParam(request->hasArg("playstation") ? "playstation" : "play", request->method() == HTTP_POST); int id = atoi(p->value().c_str()); if (id < 1) id = 1; - if (id > config.store.countStation) id = config.store.countStation; + uint16_t cs = config.playlistLength(); + if (id > cs) id = cs; //config.sdResumePos = 0; player.sendCommand({PR_PLAY, id}); commandFound=true; diff --git a/yoRadio/src/core/netserver.h b/yoRadio/src/core/netserver.h index be015d0..2584aa7 100644 --- a/yoRadio/src/core/netserver.h +++ b/yoRadio/src/core/netserver.h @@ -5,22 +5,28 @@ #include "../AsyncWebServer/ESPAsyncWebServer.h" #include "AsyncUDP.h" -enum requestType_e : uint8_t { PLAYLIST=1, STATION=2, STATIONNAME=3, ITEM=4, TITLE=5, VOLUME=6, NRSSI=7, BITRATE=8, MODE=9, EQUALIZER=10, BALANCE=11, PLAYLISTSAVED=12, GETMODE=13, GETINDEX=14, GETACTIVE=15, GETSYSTEM=16, GETSCREEN=17, GETTIMEZONE=18, GETWEATHER=19, GETCONTROLS=20, DSPON=21, SDPOS=22, SDLEN=23, SDSNUFFLE=24, SDINIT=25, GETPLAYERMODE=26, CHANGEMODE=27 }; +enum requestType_e : uint8_t { PLAYLIST=1, STATION=2, STATIONNAME=3, ITEM=4, TITLE=5, VOLUME=6, NRSSI=7, BITRATE=8, MODE=9, EQUALIZER=10, BALANCE=11, PLAYLISTSAVED=12, STARTUP=13, GETINDEX=14, GETACTIVE=15, GETSYSTEM=16, GETSCREEN=17, GETTIMEZONE=18, GETWEATHER=19, GETCONTROLS=20, DSPON=21, SDPOS=22, SDLEN=23, SDSNUFFLE=24, SDINIT=25, GETPLAYERMODE=26, CHANGEMODE=27 }; enum import_e : uint8_t { IMDONE=0, IMPL=1, IMWIFI=2 }; const char emptyfs_html[] PROGMEM = R"( -ёRadio - WEB Board Uploader + + +

ёRadio - WEB Board Uploader


Select ALL files from yoRadio/data/www/
and upload them using the form below

-
+


-= OPTIONAL =-
You can also upload playlist.csv
and wifi.csv files from your backup
@@ -28,9 +34,9 @@ input[type=text],input[type=password]{width:170px;background:#272727;color:#e3d2

-
+

-
+ -= OPTIONAL =-
If you can't connect from PC to 192.168.4.1 address
setup WiFi connection first
@@ -39,9 +45,38 @@ input[type=text],input[type=password]{width:170px;background:#272727;color:#e3d2
- +
powered by ёRadio
+ + + +)"; +const char index_html[] PROGMEM = R"( + + + + + + + + + + + + + + + + + +
+ + )"; - struct nsRequestParams_t { requestType_e type; diff --git a/yoRadio/src/core/options.h b/yoRadio/src/core/options.h index 4a909df..03d2376 100644 --- a/yoRadio/src/core/options.h +++ b/yoRadio/src/core/options.h @@ -1,7 +1,7 @@ #ifndef options_h #define options_h -#define YOVERSION "0.9.434" +#define YOVERSION "0.9.511" /******************************************************* DO NOT EDIT THIS FILE. diff --git a/yoRadio/src/core/player.cpp b/yoRadio/src/core/player.cpp index 2a52361..0436afc 100644 --- a/yoRadio/src/core/player.cpp +++ b/yoRadio/src/core/player.cpp @@ -186,6 +186,7 @@ void Player::setOutputPins(bool isPlaying) { void Player::_play(uint16_t stationId) { log_i("%s called, stationId=%d", __func__, stationId); setError(""); + setDefaults(); remoteStationName = false; config.setDspOn(1); config.vuThreshold = 0; @@ -193,21 +194,23 @@ void Player::_play(uint16_t stationId) { config.screensaverTicks=SCREENSAVERSTARTUPDELAY; config.screensaverPlayingTicks=SCREENSAVERSTARTUPDELAY; if(config.getMode()!=PM_SDCARD) { - display.putRequest(PSTOP); + display.putRequest(PSTOP); } setOutputPins(false); //config.setTitle(config.getMode()==PM_WEB?const_PlConnect:""); + if(!config.loadStation(stationId)) return; config.setTitle(config.getMode()==PM_WEB?const_PlConnect:"[next track]"); config.station.bitrate=0; config.setBitrateFormat(BF_UNCNOWN); - config.loadStation(stationId); + _loadVol(config.store.volume); display.putRequest(DBITRATE); display.putRequest(NEWSTATION); netserver.requestOnChange(STATION, 0); netserver.loop(); netserver.loop(); - config.setSmartStart(0); + if(config.store.smartstart!=2) + config.setSmartStart(0); bool isConnected = false; if(config.getMode()==PM_SDCARD && SDC_CS!=255){ isConnected=connecttoFS(sdman,config.station.url,config.sdResumePos==0?_resumeFilePos:config.sdResumePos-player.sd_min); @@ -223,7 +226,8 @@ void Player::_play(uint16_t stationId) { config.saveValue(&config.store.lastSdStation, stationId); } //config.setTitle(""); - config.setSmartStart(1); + if(config.store.smartstart!=2) + config.setSmartStart(1); netserver.requestOnChange(MODE, 0); setOutputPins(true); display.putRequest(NEWMODE, PLAYER); @@ -268,7 +272,7 @@ void Player::prev() { uint16_t lastStation = config.lastStation(); if(config.getMode()==PM_WEB || !config.store.sdsnuffle){ - if (lastStation == 1) config.lastStation(config.store.countStation); else config.lastStation(lastStation-1); + if (lastStation == 1) config.lastStation(config.playlistLength()); else config.lastStation(lastStation-1); } sendCommand({PR_PLAY, config.lastStation()}); } @@ -276,9 +280,9 @@ void Player::prev() { void Player::next() { uint16_t lastStation = config.lastStation(); if(config.getMode()==PM_WEB || !config.store.sdsnuffle){ - if (lastStation == config.store.countStation) config.lastStation(1); else config.lastStation(lastStation+1); + if (lastStation == config.playlistLength()) config.lastStation(1); else config.lastStation(lastStation+1); }else{ - config.lastStation(random(1, config.store.countStation)); + config.lastStation(random(1, config.playlistLength())); } sendCommand({PR_PLAY, config.lastStation()}); } diff --git a/yoRadio/src/core/telnet.cpp b/yoRadio/src/core/telnet.cpp index 8fe5baf..9ed812a 100644 --- a/yoRadio/src/core/telnet.cpp +++ b/yoRadio/src/core/telnet.cpp @@ -292,7 +292,8 @@ void Telnet::on_input(const char* str, uint8_t clientId) { int sb; if (sscanf(str, "play(%d)", &sb) == 1 || sscanf(str, "cli.play(\"%d\")", &sb) == 1 || sscanf(str, "play %d", &sb) == 1 ) { if (sb < 1) sb = 1; - if (sb >= config.store.countStation) sb = config.store.countStation; + uint16_t cs = config.playlistLength(); + if (sb >= cs) sb = cs; player.sendCommand({PR_PLAY, (uint16_t)sb}); return; } diff --git a/yoRadio/src/displays/nextion.cpp b/yoRadio/src/displays/nextion.cpp index 08bc409..9c8024c 100644 --- a/yoRadio/src/displays/nextion.cpp +++ b/yoRadio/src/displays/nextion.cpp @@ -209,14 +209,14 @@ void Nextion::loop() { if(strcmp(scanBuf, "up") == 0) { display.resetQueue(); int p = display.currentPlItem - 1; - if (p < 1) p = config.store.countStation; + if (p < 1) p = config.playlistLength(); display.currentPlItem = p; display.putRequest(DRAWPLAYLIST, p); } if(strcmp(scanBuf, "dn") == 0) { display.resetQueue(); int p = display.currentPlItem + 1; - if (p > config.store.countStation) p = 1; + if (p > config.playlistLength()) p = 1; display.currentPlItem = p; display.putRequest(DRAWPLAYLIST, p); }