diff --git a/yoRadio/data/www/options.html.gz b/yoRadio/data/www/options.html.gz
index fabd443..5249457 100644
Binary files a/yoRadio/data/www/options.html.gz 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
index 66c47f5..b4f92f1 100644
Binary files a/yoRadio/data/www/player.html.gz 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 656b931..4d47b38 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/style.css.gz b/yoRadio/data/www/style.css.gz
index 210bbf3..b4cb834 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
index 185a6c4..6ce043e 100644
--- a/yoRadio/data/www/theme.css
+++ b/yoRadio/data/www/theme.css
@@ -13,4 +13,5 @@
--playlist-hover: #323232;
--section-gradient: #111111;
--section-border: #555555;
+--heapbar-color: #3ea220;
}
diff --git a/yoRadio/src/AsyncWebServer/AsyncTCP.h b/yoRadio/src/AsyncWebServer/AsyncTCP.h
index c898bf0..ed381a6 100644
--- a/yoRadio/src/AsyncWebServer/AsyncTCP.h
+++ b/yoRadio/src/AsyncWebServer/AsyncTCP.h
@@ -40,20 +40,23 @@ extern "C" {
#endif
#ifndef XTASK_MEM_SIZE
- #define XTASK_MEM_SIZE 6144 // 8192 / 2
+ //#define XTASK_MEM_SIZE 6144 // 8192 / 2
+ #define XTASK_MEM_SIZE 1024*5
#endif
#ifndef XTASK_PRIOTITY
- #define XTASK_PRIOTITY 5 //3
+ #define XTASK_PRIOTITY 3 //3
#endif
#ifndef ATCP_TASK_DELAY
#define ATCP_TASK_DELAY 2
#endif
#ifndef XQUEUE_SIZE
- #define XQUEUE_SIZE 128 // (32)
+ //#define XQUEUE_SIZE 128 // (32)
+ #define XQUEUE_SIZE 32
#endif
#ifndef SEND_ASYNC_EVENT_DELAY
- #define SEND_ASYNC_EVENT_DELAY portMAX_DELAY
+ //#define SEND_ASYNC_EVENT_DELAY portMAX_DELAY
+ #define SEND_ASYNC_EVENT_DELAY pdMS_TO_TICKS(1000)
#endif
class AsyncClient;
diff --git a/yoRadio/src/audioI2S/Audio.cpp b/yoRadio/src/audioI2S/Audio.cpp
index e444487..d3799ff 100644
--- a/yoRadio/src/audioI2S/Audio.cpp
+++ b/yoRadio/src/audioI2S/Audio.cpp
@@ -61,9 +61,9 @@ size_t AudioBuffer::init() {
if(m_buffer == NULL) {
// PSRAM not found, not configured or not enough available
m_f_psram = false;
- m_buffSize = m_buffSizeRAM;
+ m_buffSize = m_buffSizeRAM * config.store.abuff;
m_buffer = (uint8_t*) calloc(m_buffSize, sizeof(uint8_t));
- m_buffSize = m_buffSizeRAM - m_resBuffSizeRAM;
+ m_buffSize = m_buffSizeRAM * config.store.abuff - m_resBuffSizeRAM;
}
if(!m_buffer)
return 0;
@@ -183,7 +183,7 @@ Audio::Audio(bool internalDAC /* = false */, uint8_t channelEnabled /* = I2S_DAC
m_i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
m_i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1; // interrupt priority
#ifdef OLD_DMABUF_PARAMS
- m_i2s_config.dma_buf_count = 16; // 4×512×16=32768
+ m_i2s_config.dma_buf_count = 16; // 4×512×16=32768
#else
m_i2s_config.dma_buf_count = psramInit()?16:DMA_BUFCOUNT;
#endif
@@ -370,6 +370,20 @@ void Audio::setConnectionTimeout(uint16_t timeout_ms, uint16_t timeout_ms_ssl){
if(timeout_ms_ssl) m_timeout_ms_ssl = timeout_ms_ssl;
}
+void Audio::connectTask(void* pvParams) {
+ ConnectParams* params = static_cast(pvParams);
+ Audio* self = params->instance;
+ if(self->_client){
+ self->_connectionResult = self->_client->connect(params->hostwoext, params->port/*, self->m_f_ssl ? self->m_timeout_ms_ssl : self->m_timeout_ms*/);
+ }else{
+ self->_connectionResult = false;
+ }
+ free((void*)params->hostwoext);
+ delete params;
+ self->_connectTaskHandle = nullptr;
+ vTaskDelete(nullptr);
+}
+
//---------------------------------------------------------------------------------------------------------------------
bool Audio::connecttohost(const char* host, const char* user, const char* pwd) {
// user and pwd for authentification only, can be empty
@@ -480,7 +494,22 @@ bool Audio::connecttohost(const char* host, const char* user, const char* pwd) {
uint32_t t = millis();
if(m_f_Log) AUDIO_INFO("connect to %s on port %d path %s", hostwoext, port, extension);
- res = _client->connect(hostwoext, port, m_f_ssl ? m_timeout_ms_ssl : m_timeout_ms);
+ if(!config.store.watchdog){
+ res = _client->connect(hostwoext, port, m_f_ssl ? m_timeout_ms_ssl : m_timeout_ms);
+ }else{
+ ConnectParams* params = new ConnectParams{ strdup(hostwoext), port, this }; _connectionResult = false;
+ xTaskCreatePinnedToCore(connectTask, "ConnectTask", WATCHDOG_TASK_SIZE, params, WATCHDOG_TASK_PRIORITY, &_connectTaskHandle, WATCHDOG_TASK_CORE_ID);
+ for(;;){
+ if(millis()-t>(m_f_ssl ? m_timeout_ms_ssl : m_timeout_ms) || _connectionResult) break;
+ vTaskDelay(10);
+ }
+ res = _connectionResult;
+ if (_connectTaskHandle!=nullptr) {
+ vTaskDelete(_connectTaskHandle);
+ _connectTaskHandle = nullptr;
+ AUDIO_INFO("WATCH DOG HAS FINISHED A WORK, BYE!");
+ }
+ }
if(res){
uint32_t dt = millis() - t;
strcpy(m_lastHost, l_host);
diff --git a/yoRadio/src/audioI2S/AudioEx.h b/yoRadio/src/audioI2S/AudioEx.h
index c25184c..0051d9f 100644
--- a/yoRadio/src/audioI2S/AudioEx.h
+++ b/yoRadio/src/audioI2S/AudioEx.h
@@ -32,10 +32,6 @@
#include
#endif // SDFATFS_USED
-#ifndef AUDIOBUFFER_MULTIPLIER2
-#define AUDIOBUFFER_MULTIPLIER2 8
-#endif
-
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
#include "hal/gpio_ll.h"
#endif
@@ -149,7 +145,7 @@ public:
protected:
size_t m_buffSizePSRAM = 300000; // most webstreams limit the advance to 100...300Kbytes
//size_t m_buffSizeRAM = 1600 * 5;
- size_t m_buffSizeRAM = 1600 * AUDIOBUFFER_MULTIPLIER2;
+ size_t m_buffSizeRAM = 1600;
size_t m_buffSize = 0;
size_t m_freeSpace = 0;
size_t m_writeSpace = 0;
@@ -287,6 +283,7 @@ private:
void IIR_calculateCoefficients(int8_t G1, int8_t G2, int8_t G3);
bool ts_parsePacket(uint8_t* packet, uint8_t* packetStart, uint8_t* packetLength);
void _computeVUlevel(int16_t sample[2]);
+ static void connectTask(void* pvParams);
// implement several function with respect to the index of string
void trim(char *s) {
//fb trim in place
@@ -425,11 +422,11 @@ private:
if(str == NULL) return 0;
uint32_t hash = 0;
for(int i=0; i m_playlistContent; // m3u8 playlist buffer
std::vector m_playlistURL; // m3u8 streamURLs buffer
std::vector m_hashQueue;
-
+
+ struct ConnectParams {
+ char *hostwoext = NULL;
+ uint16_t port = 80;
+ Audio* instance;
+ };
+ volatile bool _connectionResult;
+ TaskHandle_t _connectTaskHandle = nullptr;
+
const size_t m_frameSizeWav = 1600;
const size_t m_frameSizeMP3 = 1600;
const size_t m_frameSizeAAC = 1600;
diff --git a/yoRadio/src/audioVS1053/audioVS1053Ex.cpp b/yoRadio/src/audioVS1053/audioVS1053Ex.cpp
index 199dd7e..e6dea96 100644
--- a/yoRadio/src/audioVS1053/audioVS1053Ex.cpp
+++ b/yoRadio/src/audioVS1053/audioVS1053Ex.cpp
@@ -42,9 +42,9 @@ size_t AudioBuffer::init() {
}
}
} else { // no PSRAM available, use ESP32 Flash Memory"
- m_buffSize = m_buffSizeRAM;
+ m_buffSize = m_buffSizeRAM * config.store.abuff;
m_buffer = (uint8_t*) calloc(m_buffSize, sizeof(uint8_t));
- m_buffSize = m_buffSizeRAM - m_resBuffSizeRAM;
+ m_buffSize = m_buffSizeRAM * config.store.abuff - m_resBuffSizeRAM;
}
if(!m_buffer)
return 0;
@@ -1713,6 +1713,22 @@ void Audio::setConnectionTimeout(uint16_t timeout_ms, uint16_t timeout_ms_ssl){
if(timeout_ms) m_timeout_ms = timeout_ms;
if(timeout_ms_ssl) m_timeout_ms_ssl = timeout_ms_ssl;
}
+
+void Audio::connectTask(void* pvParams) {
+ ConnectParams* params = static_cast(pvParams);
+ Audio* self = params->instance;
+ bool res = true;
+ if(self->_client){
+ self->_connectionResult = self->_client->connect(params->hostwoext, params->port/*, self->m_f_ssl ? self->m_timeout_ms_ssl : self->m_timeout_ms*/);
+ }else{
+ self->_connectionResult = false;
+ }
+ free((void*)params->hostwoext);
+ delete params;
+ self->_connectTaskHandle = nullptr;
+ vTaskDelete(nullptr);
+}
+
//---------------------------------------------------------------------------------------------------------------------
bool Audio::connecttohost(String host){
return connecttohost(host.c_str());
@@ -1827,8 +1843,24 @@ bool Audio::connecttohost(const char* host, const char* user, const char* pwd) {
uint32_t t = millis();
if(m_f_Log) AUDIO_INFO("connect to %s on port %d path %s", hostwoext, port, extension);
- res = _client->connect(hostwoext, port, m_f_ssl ? m_timeout_ms_ssl : m_timeout_ms);
-
+ //res = _client->connect(hostwoext, port, m_f_ssl ? m_timeout_ms_ssl : m_timeout_ms);
+ if(!config.store.watchdog){
+ res = _client->connect(hostwoext, port, m_f_ssl ? m_timeout_ms_ssl : m_timeout_ms);
+ }else{
+ ConnectParams* params = new ConnectParams{ strdup(hostwoext), port, this }; _connectionResult = false;
+ xTaskCreatePinnedToCore(connectTask, "ConnectTask", WATCHDOG_TASK_SIZE, params, WATCHDOG_TASK_PRIORITY, &_connectTaskHandle, WATCHDOG_TASK_CORE_ID);
+ for(;;){
+ if(millis()-t>(m_f_ssl ? m_timeout_ms_ssl : m_timeout_ms) || _connectionResult) break;
+ vTaskDelay(10);
+ }
+ res = _connectionResult;
+ if (_connectTaskHandle!=nullptr) {
+ vTaskDelete(_connectTaskHandle);
+ _connectTaskHandle = nullptr;
+ AUDIO_INFO("WATCH DOG HAS FINISHED A WORK, BYE!");
+ }
+ }
+
if(res){
uint32_t dt = millis() - t;
strcpy(m_lastHost, l_host);
diff --git a/yoRadio/src/audioVS1053/audioVS1053Ex.h b/yoRadio/src/audioVS1053/audioVS1053Ex.h
index 9640c3e..441ad4c 100644
--- a/yoRadio/src/audioVS1053/audioVS1053Ex.h
+++ b/yoRadio/src/audioVS1053/audioVS1053Ex.h
@@ -9,11 +9,7 @@
#ifndef _vs1053_ext
#define _vs1053_ext
-#ifndef AUDIOBUFFER_MULTIPLIER2
-#define AUDIOBUFFER_MULTIPLIER2 10
-#endif
-
-#define VS1053VOLM 128 // 128 or 96 only
+#define VS1053VOLM 128 // 128 or 96 only
#define VS1053VOL(v) (VS1053VOLM==128?log10(((float)v+1)) * 50.54571334 + 128:log10(((float)v+1)) * 64.54571334 + 96)
@@ -107,7 +103,7 @@ public:
protected:
const size_t m_buffSizePSRAM = 300000; // most webstreams limit the advance to 100...300Kbytes
//const size_t m_buffSizeRAM = 1600 * 10;
- const size_t m_buffSizeRAM = 1600 * AUDIOBUFFER_MULTIPLIER2;
+ const size_t m_buffSizeRAM = 1600;
size_t m_buffSize = 0;
size_t m_freeSpace = 0;
size_t m_writeSpace = 0;
@@ -136,6 +132,15 @@ private:
std::vector m_playlistURL; // m3u8 streamURLs buffer
std::vector m_hashQueue;
+ struct ConnectParams {
+ char *hostwoext = NULL;
+ uint16_t port = 80;
+ Audio* instance;
+ };
+ volatile bool _connectionResult;
+ TaskHandle_t _connectTaskHandle = nullptr;
+ static void connectTask(void* pvParams);
+
private:
enum : int { AUDIO_NONE, HTTP_RESPONSE_HEADER , AUDIO_DATA, AUDIO_LOCALFILE, AUDIO_METADATA, AUDIO_PLAYLISTINIT,
AUDIO_PLAYLISTHEADER, AUDIO_PLAYLISTDATA, VS1053_SWM, VS1053_OGG};
@@ -146,10 +151,10 @@ private:
enum : int { ST_NONE = 0, ST_WEBFILE = 1, ST_WEBSTREAM = 2};
private:
- uint8_t cs_pin ; // Pin where CS line is connected
- uint8_t dcs_pin ; // Pin where DCS line is connected
- uint8_t dreq_pin ; // Pin where DREQ line is connected
- uint8_t curvol ; // Current volume setting 0..100%
+ uint8_t cs_pin ; // Pin where CS line is connected
+ uint8_t dcs_pin ; // Pin where DCS line is connected
+ uint8_t dreq_pin ; // Pin where DREQ line is connected
+ uint8_t curvol ; // Current volume setting 0..100%
const uint8_t vs1053_chunk_size = 32 ;
int8_t m_balance = 0; // -16 (mute left) ... +16 (mute right)
@@ -171,11 +176,11 @@ private:
const uint8_t SCI_AICTRL2 = 0xE ;
const uint8_t SCI_AICTRL3 = 0xF ;
// SCI_MODE bits
- const uint8_t SM_SDINEW = 11 ; // Bitnumber in SCI_MODE always on
- const uint8_t SM_RESET = 2 ; // Bitnumber in SCI_MODE soft reset
- const uint8_t SM_CANCEL = 3 ; // Bitnumber in SCI_MODE cancel song
- const uint8_t SM_TESTS = 5 ; // Bitnumber in SCI_MODE for tests
- const uint8_t SM_LINE1 = 14 ; // Bitnumber in SCI_MODE for Line input
+ const uint8_t SM_SDINEW = 11 ; // Bitnumber in SCI_MODE always on
+ const uint8_t SM_RESET = 2 ; // Bitnumber in SCI_MODE soft reset
+ const uint8_t SM_CANCEL = 3 ; // Bitnumber in SCI_MODE cancel song
+ const uint8_t SM_TESTS = 5 ; // Bitnumber in SCI_MODE for tests
+ const uint8_t SM_LINE1 = 14 ; // Bitnumber in SCI_MODE for Line input
SPIClass* spi_VS1053 = NULL;
SPISettings VS1053_SPI_DATA; // SPI settings normal speed
@@ -242,7 +247,7 @@ protected:
inline void DCS_LOW() {(dcs_pin&0x20) ? GPIO.out1_w1tc.data = 1 << (dcs_pin - 32) : GPIO.out_w1tc = 1 << dcs_pin;}
inline void CS_HIGH() {( cs_pin&0x20) ? GPIO.out1_w1ts.data = 1 << ( cs_pin - 32) : GPIO.out_w1ts = 1 << cs_pin;}
inline void CS_LOW() {( cs_pin&0x20) ? GPIO.out1_w1tc.data = 1 << ( cs_pin - 32) : GPIO.out_w1tc = 1 << cs_pin;}
- inline void await_data_request() {while(!digitalRead(dreq_pin)) NOP();} // Very short delay
+ inline void await_data_request() {while(!digitalRead(dreq_pin)) NOP();} // Very short delay
inline bool data_request() {return(digitalRead(dreq_pin) == HIGH);}
void initInBuff();
@@ -319,8 +324,8 @@ public:
size_t bufferFree();
size_t inBufferFilled(){ return bufferFilled(); }
size_t inBufferFree(){ return bufferFree(); }
- void setBalance(int8_t bal = 0);
- void setTone(int8_t gainLowPass, int8_t gainBandPass, int8_t gainHighPass);
+ void setBalance(int8_t bal = 0);
+ void setTone(int8_t gainLowPass, int8_t gainBandPass, int8_t gainHighPass);
void setDefaults();
void forceMono(bool m) {} // TODO
/* VU METER */
diff --git a/yoRadio/src/core/audiohandlers.h b/yoRadio/src/core/audiohandlers.h
index eb9231f..737648d 100644
--- a/yoRadio/src/core/audiohandlers.h
+++ b/yoRadio/src/core/audiohandlers.h
@@ -61,7 +61,7 @@ void audio_showstation(const char *info) {
void audio_showstreamtitle(const char *info) {
DBGH();
- if (strstr(info, "Account already in use") != NULL || strstr(info, "HTTP/1.0 401") != NULL) player.setError(info);
+ if (strstr(info, "Account already in use") != NULL || strstr(info, "HTTP/1.0 401") != NULL || strstr(info, "HTTP/1.1 401") != NULL) player.setError(info);
bool p = printable(info) && (strlen(info) > 0);
#ifdef DEBUG_TITLES
config.setTitle(DEBUG_TITLES);
@@ -73,7 +73,7 @@ void audio_showstreamtitle(const char *info) {
void audio_error(const char *info) {
//config.setTitle(info);
player.setError(info);
- telnet.printf("##ERROR#:\t%s\n", info);
+ //telnet.printf("##ERROR#:\t%s\n", info);
}
void audio_id3artist(const char *info){
@@ -88,11 +88,11 @@ void audio_id3album(const char *info){
if(strlen(config.station.title)==0){
config.setTitle(info);
}else{
- char out[BUFLEN]= {0};
- strlcat(out, config.station.title, BUFLEN);
- strlcat(out, " - ", BUFLEN);
- strlcat(out, info, BUFLEN);
- config.setTitle(out);
+ size_t tbs = sizeof(config.tmpBuf);
+ strlcat(config.tmpBuf, config.station.title, tbs);
+ strlcat(config.tmpBuf, " - ", tbs);
+ strlcat(config.tmpBuf, info, tbs);
+ config.setTitle(config.tmpBuf);
}
}
}
diff --git a/yoRadio/src/core/commandhandler.cpp b/yoRadio/src/core/commandhandler.cpp
index 57c7969..deea5b4 100644
--- a/yoRadio/src/core/commandhandler.cpp
+++ b/yoRadio/src/core/commandhandler.cpp
@@ -5,6 +5,7 @@
#include "config.h"
#include "controls.h"
#include "options.h"
+#include "telnet.h"
CommandHandler cmd;
@@ -65,11 +66,16 @@ bool CommandHandler::exec(const char *command, const char *value, uint8_t cid) {
if (strEquals(command, "screensaverplayingenabled")){ config.setScreensaverPlayingEnabled(static_cast(atoi(value))); return true; }
if (strEquals(command, "screensaverplayingtimeout")){ config.setScreensaverPlayingTimeout(static_cast(atoi(value))); return true; }
if (strEquals(command, "screensaverplayingblank")) { config.setScreensaverPlayingBlank(static_cast(atoi(value))); return true; }
+ if (strEquals(command, "abuff")){ config.saveValue(&config.store.abuff, static_cast(atoi(value))); return true; }
+ if (strEquals(command, "telnet")){ config.saveValue(&config.store.telnet, static_cast(atoi(value))); telnet.toggle(); return true; }
+ if (strEquals(command, "watchdog")){ config.saveValue(&config.store.watchdog, static_cast(atoi(value))); return true; }
- if (strEquals(command, "tzh")) { config.saveValue(&config.store.tzHour, static_cast(atoi(value))); return true; }
- if (strEquals(command, "tzm")) { config.saveValue(&config.store.tzMin, static_cast(atoi(value))); return true; }
- if (strEquals(command, "sntp2")) { config.saveValue(config.store.sntp2, value, 35, false); return true; }
- if (strEquals(command, "sntp1")) { config.setSntpOne(value); return true; }
+ if (strEquals(command, "tzh")) { config.saveValue(&config.store.tzHour, static_cast(atoi(value))); return true; }
+ if (strEquals(command, "tzm")) { config.saveValue(&config.store.tzMin, static_cast(atoi(value))); return true; }
+ if (strEquals(command, "sntp2")) { config.saveValue(config.store.sntp2, value, 35, false); return true; }
+ if (strEquals(command, "sntp1")) { config.setSntpOne(value); return true; }
+ if (strEquals(command, "timeint")) { config.saveValue(&config.store.timeSyncInterval, static_cast(atoi(value))); return true; }
+ if (strEquals(command, "timeintrtc")) { config.saveValue(&config.store.timeSyncIntervalRTC, static_cast(atoi(value))); return true; }
if (strEquals(command, "volsteps")) { config.saveValue(&config.store.volsteps, static_cast(atoi(value))); return true; }
if (strEquals(command, "encacc")) { setEncAcceleration(static_cast(atoi(value))); return true; }
@@ -79,32 +85,33 @@ bool CommandHandler::exec(const char *command, const char *value, uint8_t cid) {
if (strEquals(command, "lat")) { config.saveValue(config.store.weatherlat, value, 10, false); return true; }
if (strEquals(command, "lon")) { config.saveValue(config.store.weatherlon, value, 10, false); return true; }
if (strEquals(command, "key")) { config.setWeatherKey(value); return true; }
- //<-----TODO
+ if (strEquals(command, "wint")) { config.saveValue(&config.store.weatherSyncInterval, static_cast(atoi(value))); return true; }
+
if (strEquals(command, "volume")) { player.setVol(static_cast(atoi(value))); return true; }
if (strEquals(command, "sdpos")) { config.setSDpos(static_cast(atoi(value))); return true; }
if (strEquals(command, "snuffle")) { config.setSnuffle(strcmp(value, "true") == 0); return true; }
if (strEquals(command, "balance")) { config.setBalance(static_cast(atoi(value))); return true; }
if (strEquals(command, "reboot")) { ESP.restart(); return true; }
+ if (strEquals(command, "boot")) { ESP.restart(); return true; }
if (strEquals(command, "format")) { SPIFFS.format(); ESP.restart(); return true; }
- if (strEquals(command, "submitplaylist")) { return true; }
-
+ if (strEquals(command, "submitplaylist")) { player.sendCommand({PR_STOP, 0}); return true; }
+
#if IR_PIN!=255
if (strEquals(command, "irbtn")) { config.setIrBtn(atoi(value)); return true; }
if (strEquals(command, "chkid")) { config.irchck = static_cast(atoi(value)); return true; }
if (strEquals(command, "irclr")) { config.ircodes.irVals[config.irindex][static_cast(atoi(value))] = 0; return true; }
#endif
if (strEquals(command, "reset")) { config.resetSystem(value, cid); return true; }
-
+
if (strEquals(command, "smartstart")){ uint8_t ss = atoi(value) == 1 ? 1 : 2; if (!player.isRunning() && ss == 1) ss = 0; config.setSmartStart(ss); return true; }
if (strEquals(command, "audioinfo")) { config.saveValue(&config.store.audioinfo, static_cast(atoi(value))); display.putRequest(AUDIOINFO); return true; }
if (strEquals(command, "vumeter")) { config.saveValue(&config.store.vumeter, static_cast(atoi(value))); display.putRequest(SHOWVUMETER); return true; }
if (strEquals(command, "softap")) { config.saveValue(&config.store.softapdelay, static_cast(atoi(value))); return true; }
if (strEquals(command, "mdnsname")) { config.saveValue(config.store.mdnsname, value, MDNS_LENGTH); return true; }
if (strEquals(command, "rebootmdns")){
- char buf[MDNS_LENGTH*2];
- if(strlen(config.store.mdnsname)>0) snprintf(buf, MDNS_LENGTH*2, "{\"redirect\": \"http://%s.local\"}", config.store.mdnsname);
- else snprintf(buf, MDNS_LENGTH*2, "{\"redirect\": \"http://%s/\"}", WiFi.localIP().toString().c_str());
- websocket.text(cid, buf); delay(500); ESP.restart();
+ if(strlen(config.store.mdnsname)>0) snprintf(config.tmpBuf, sizeof(config.tmpBuf), "{\"redirect\": \"http://%s.local/settings.html\"}", config.store.mdnsname);
+ else snprintf(config.tmpBuf, sizeof(config.tmpBuf), "{\"redirect\": \"http://%s/settings.html\"}", config.ipToStr(WiFi.localIP()));
+ websocket.text(cid, config.tmpBuf); delay(500); ESP.restart();
return true;
}
diff --git a/yoRadio/src/core/config.cpp b/yoRadio/src/core/config.cpp
index d3c520b..21bba5f 100644
--- a/yoRadio/src/core/config.cpp
+++ b/yoRadio/src/core/config.cpp
@@ -6,6 +6,7 @@
#include "network.h"
#include "netserver.h"
#include "controls.h"
+#include "timekeeper.h"
#ifdef USE_SD
#include "sdmanager.h"
#endif
@@ -13,6 +14,19 @@
Config config;
+#ifdef HEAP_DBG
+void printHeapFragmentationInfo(const char* title){
+ size_t freeHeap = heap_caps_get_free_size(MALLOC_CAP_DEFAULT);
+ size_t largestBlock = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT);
+ float fragmentation = 100.0 * (1.0 - ((float)largestBlock / (float)freeHeap));
+ Serial.printf("\n****** %s ******\n", title);
+ Serial.printf("* Free heap: %u bytes\n", freeHeap);
+ Serial.printf("* Largest free block: %u bytes\n", largestBlock);
+ Serial.printf("* Fragmentation: %.2f%%\n", fragmentation);
+ Serial.printf("*************************************\n\n");
+}
+#endif
+
void u8fix(char *src){
char last = src[strlen(src)-1];
if ((uint8_t)last >= 0xC2) src[strlen(src)-1]='\0';
@@ -46,7 +60,8 @@ void Config::init() {
screensaverPlayingTicks = 0;
newConfigMode = 0;
isScreensaver = false;
- bootInfo();
+ memset(tmpBuf, 0, BUFLEN);
+ //bootInfo();
#if RTCSUPPORTED
_rtcFound = false;
BOOTLOG("RTC begin(SDA=%d,SCL=%d)", RTC_SDA, RTC_SCL);
@@ -69,7 +84,7 @@ void Config::init() {
#endif
#endif
eepromRead(EEPROM_START, store);
-
+ bootInfo(); // https://github.com/e2002/yoradio/pull/149
if (store.config_set != 4262) {
setDefaults();
}
@@ -93,6 +108,7 @@ void Config::init() {
_SDplaylistFS = &SPIFFS;
#endif
_bootDone=false;
+ setTimeConf();
}
void Config::_setupVersion(){
@@ -103,9 +119,8 @@ void Config::_setupVersion(){
saveValue(&store.screensaverTimeout, (uint16_t)20);
break;
case 2:
- char buf[MDNS_LENGTH];
- snprintf(buf, MDNS_LENGTH, "yoradio-%x", getChipId());
- saveValue(store.mdnsname, buf, MDNS_LENGTH);
+ snprintf(tmpBuf, MDNS_LENGTH, "yoradio-%x", (unsigned int)getChipId());
+ saveValue(store.mdnsname, tmpBuf, MDNS_LENGTH);
saveValue(&store.skipPlaylistUpDown, false);
break;
case 3:
@@ -114,6 +129,13 @@ void Config::_setupVersion(){
saveValue(&store.screensaverPlayingTimeout, (uint16_t)5);
saveValue(&store.screensaverPlayingBlank, false);
break;
+ case 4:
+ saveValue(&store.abuff, (uint16_t)(VS1053_CS==255?7:10));
+ saveValue(&store.telnet, true);
+ saveValue(&store.watchdog, true);
+ saveValue(&store.timeSyncInterval, (uint16_t)60); //min
+ saveValue(&store.timeSyncIntervalRTC, (uint16_t)24); //hours
+ saveValue(&store.weatherSyncInterval, (uint16_t)30); // min
default:
break;
}
@@ -200,6 +222,44 @@ bool Config::spiffsCleanup(){
return ret;
}
+char * Config::ipToStr(IPAddress ip){
+ snprintf(ipBuf, 16, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
+ return ipBuf;
+}
+bool Config::prepareForPlaying(uint16_t stationId){
+ setDspOn(1);
+ vuThreshold = 0;
+ screensaverTicks=SCREENSAVERSTARTUPDELAY;
+ screensaverPlayingTicks=SCREENSAVERSTARTUPDELAY;
+ if(getMode()!=PM_SDCARD) {
+ display.putRequest(PSTOP);
+ }
+
+ if(!loadStation(stationId)) return false;
+ setTitle(getMode()==PM_WEB?const_PlConnect:"[next track]");
+ station.bitrate=0;
+ setBitrateFormat(BF_UNCNOWN);
+ display.putRequest(DBITRATE);
+ display.putRequest(NEWSTATION);
+ display.putRequest(NEWMODE, PLAYER);
+ netserver.requestOnChange(STATION, 0);
+ netserver.requestOnChange(MODE, 0);
+ netserver.loop();
+ netserver.loop();
+ if(store.smartstart!=2)
+ setSmartStart(0);
+ return true;
+}
+void Config::configPostPlaying(uint16_t stationId){
+ if(getMode()==PM_SDCARD) {
+ sdResumePos = 0;
+ saveValue(&store.lastSdStation, stationId);
+ }
+ if(store.smartstart!=2) setSmartStart(1);
+ netserver.requestOnChange(MODE, 0);
+ //display.putRequest(NEWMODE, PLAYER);
+ display.putRequest(PSTART);
+}
void Config::initPlaylistMode(){
uint16_t _lastStation = 0;
uint16_t cs = playlistLength();
@@ -369,19 +429,17 @@ void Config::setSntpOne(const char *val){
tzdone = true;
}
if (tzdone) {
- network.forceTimeSync = true;
+ timekeeper.forceTimeSync = true;
saveValue(config.store.sntp1, val, 35);
}
}
void Config::setShowweather(bool val){
config.saveValue(&config.store.showweather, val);
- network.trueWeather=false;
- network.forceWeather = true;
+ timekeeper.forceWeather = true;
display.putRequest(SHOWWEATHER);
}
void Config::setWeatherKey(const char *val){
saveValue(store.weatherkey, val, WEATHERKEY_LENGTH);
- network.trueWeather=false;
display.putRequest(NEWMODE, CLEAR);
display.putRequest(NEWMODE, PLAYER);
}
@@ -411,7 +469,10 @@ void Config::resetSystem(const char *val, uint8_t clientId){
saveValue(&store.audioinfo, false, false);
saveValue(&store.vumeter, false, false);
saveValue(&store.softapdelay, (uint8_t)0, false);
- snprintf(store.mdnsname, MDNS_LENGTH, "yoradio-%x", getChipId());
+ saveValue(&store.abuff, (uint16_t)(VS1053_CS==255?7:10), false);
+ saveValue(&store.telnet, true);
+ saveValue(&store.watchdog, true);
+ snprintf(store.mdnsname, MDNS_LENGTH, "yoradio-%x", (unsigned int)getChipId());
saveValue(store.mdnsname, store.mdnsname, MDNS_LENGTH, true, true);
display.putRequest(NEWMODE, CLEAR); display.putRequest(NEWMODE, PLAYER);
netserver.requestOnChange(GETSYSTEM, clientId);
@@ -443,8 +504,10 @@ void Config::resetSystem(const char *val, uint8_t clientId){
saveValue(&store.tzMin, (int8_t)0, false);
saveValue(store.sntp1, "pool.ntp.org", 35, false);
saveValue(store.sntp2, "0.ru.pool.ntp.org", 35);
+ saveValue(&store.timeSyncInterval, (uint16_t)60);
+ saveValue(&store.timeSyncIntervalRTC, (uint16_t)24);
configTime(store.tzHour * 3600 + store.tzMin * 60, getTimezoneOffset(), store.sntp1, store.sntp2);
- network.forceTimeSync = true;
+ timekeeper.forceTimeSync = true;
netserver.requestOnChange(GETTIMEZONE, clientId);
return;
}
@@ -453,7 +516,8 @@ void Config::resetSystem(const char *val, uint8_t clientId){
saveValue(store.weatherlat, "55.7512", 10, false);
saveValue(store.weatherlon, "37.6184", 10, false);
saveValue(store.weatherkey, "", WEATHERKEY_LENGTH);
- network.trueWeather=false;
+ saveValue(&store.weatherSyncInterval, (uint16_t)30);
+ //network.trueWeather=false;
display.putRequest(NEWMODE, CLEAR); display.putRequest(NEWMODE, PLAYER);
netserver.requestOnChange(GETWEATHER, clientId);
return;
@@ -530,11 +594,17 @@ void Config::setDefaults() {
store.screensaverEnabled = false;
store.screensaverTimeout = 20;
store.screensaverBlank = false;
- snprintf(store.mdnsname, MDNS_LENGTH, "yoradio-%x", getChipId());
+ snprintf(store.mdnsname, MDNS_LENGTH, "yoradio-%x", (unsigned int)getChipId());
store.skipPlaylistUpDown = false;
store.screensaverPlayingEnabled = false;
store.screensaverPlayingTimeout = 5;
store.screensaverPlayingBlank = false;
+ store.abuff = VS1053_CS==255?7:10;
+ store.telnet = true;
+ store.watchdog = true;
+ store.timeSyncInterval = 60; //min
+ store.timeSyncIntervalRTC = 24; //hour
+ store.weatherSyncInterval = 30; //min
eepromWrite(EEPROM_START, store);
}
@@ -627,12 +697,11 @@ void Config::indexPlaylist() {
if (!playlist) {
return;
}
- char sName[BUFLEN], sUrl[BUFLEN];
int sOvol;
File index = SPIFFS.open(INDEX_PATH, "w");
while (playlist.available()) {
uint32_t pos = playlist.position();
- if (parseCSV(playlist.readStringUntil('\n').c_str(), sName, sUrl, sOvol)) {
+ if (parseCSV(playlist.readStringUntil('\n').c_str(), tmpBuf, tmpBuf2, sOvol)) {
index.write((uint8_t *) &pos, 4);
}
}
@@ -661,7 +730,6 @@ uint16_t Config::playlistLength(){
return out;
}
bool Config::loadStation(uint16_t ls) {
- char sName[BUFLEN], sUrl[BUFLEN];
int sOvol;
uint16_t cs = playlistLength();
if (cs == 0) {
@@ -681,11 +749,11 @@ bool Config::loadStation(uint16_t ls) {
index.readBytes((char *) &pos, 4);
index.close();
playlist.seek(pos, SeekSet);
- if (parseCSV(playlist.readStringUntil('\n').c_str(), sName, sUrl, sOvol)) {
+ if (parseCSV(playlist.readStringUntil('\n').c_str(), tmpBuf, tmpBuf2, sOvol)) {
memset(station.url, 0, BUFLEN);
memset(station.name, 0, BUFLEN);
- strncpy(station.name, sName, BUFLEN);
- strncpy(station.url, sUrl, BUFLEN);
+ strncpy(station.name, tmpBuf, BUFLEN);
+ strncpy(station.url, tmpBuf2, BUFLEN);
station.ovol = sOvol;
setLastStation(ls);
}
@@ -698,11 +766,11 @@ char * Config::stationByNum(uint16_t num){
File index = SDPLFS()->open(REAL_INDEX, "r");
index.seek((num - 1) * 4, SeekSet);
uint32_t pos;
- memset(_stationBuf, 0, BUFLEN/2);
+ memset(_stationBuf, 0, sizeof(_stationBuf));
index.readBytes((char *) &pos, 4);
index.close();
playlist.seek(pos, SeekSet);
- strncpy(_stationBuf, playlist.readStringUntil('\t').c_str(), BUFLEN/2);
+ strncpy(_stationBuf, playlist.readStringUntil('\t').c_str(), sizeof(_stationBuf));
playlist.close();
return _stationBuf;
}
@@ -880,6 +948,14 @@ bool Config::saveWifi() {
return true;
}
+void Config::setTimeConf(){
+ if(strlen(store.sntp1)>0 && strlen(store.sntp2)>0){
+ configTime(store.tzHour * 3600 + store.tzMin * 60, getTimezoneOffset(), store.sntp1, store.sntp2);
+ }else if(strlen(store.sntp1)>0){
+ configTime(store.tzHour * 3600 + store.tzMin * 60, getTimezoneOffset(), store.sntp1);
+ }
+}
+
bool Config::initNetwork() {
File file = SPIFFS.open(SSIDS_PATH, "r");
if (!file || file.isDirectory()) {
@@ -973,7 +1049,7 @@ void Config::doSleepW(){
void Config::sleepForAfter(uint16_t sf, uint16_t sa){
sleepfor = sf;
- if(sa > 0) _sleepTimer.attach(sa * 60, doSleep);
+ if(sa > 0) timekeeper.waitAndDo(sa * 60, doSleep);
else doSleep();
}
diff --git a/yoRadio/src/core/config.h b/yoRadio/src/core/config.h
index 819e659..bbd577c 100644
--- a/yoRadio/src/core/config.h
+++ b/yoRadio/src/core/config.h
@@ -1,7 +1,6 @@
#ifndef config_h
#define config_h
#include "Arduino.h"
-#include
#include
#include
#include
@@ -47,13 +46,23 @@
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
#define ESP_ARDUINO_3 1
#endif
-#define CONFIG_VERSION 4
+
+#ifdef HEAP_DBG
+ #define HEAP_INFO() printHeapFragmentationInfo(__PRETTY_FUNCTION__)
+ void printHeapFragmentationInfo(const char* title);
+#else
+ #define HEAP_INFO()
+#endif
+
+#define CONFIG_VERSION 5
enum playMode_e : uint8_t { PM_WEB=0, PM_SDCARD=1 };
enum BitrateFormat { BF_UNCNOWN, BF_MP3, BF_AAC, BF_FLAC, BF_OGG, BF_WAV };
void u8fix(char *src);
+void checkAllTasksStack();
+
struct theme_t {
uint16_t background;
uint16_t meta;
@@ -143,6 +152,12 @@ struct config_t
bool screensaverPlayingBlank;
char mdnsname[24];
bool skipPlaylistUpDown;
+ uint16_t abuff;
+ bool telnet;
+ bool watchdog;
+ uint16_t timeSyncInterval;
+ uint16_t timeSyncIntervalRTC;
+ uint16_t weatherSyncInterval;
};
#if IR_PIN!=255
@@ -189,6 +204,10 @@ class Config {
uint16_t screensaverPlayingTicks;
bool isScreensaver;
int newConfigMode;
+ char tmpBuf[BUFLEN];
+ char tmpBuf2[BUFLEN];
+ char ipBuf[16];
+ char _stationBuf[BUFLEN/2];
public:
Config() {};
//void save();
@@ -214,6 +233,7 @@ class Config {
bool loadStation(uint16_t station);
bool initNetwork();
bool saveWifi();
+ void setTimeConf();
bool saveWifiFromNextion(const char* post);
void setSmartStart(uint8_t ss);
void setBitrateFormat(BitrateFormat fmt) { configFmt = fmt; }
@@ -259,8 +279,10 @@ class Config {
void setIrBtn(int val);
#endif
void resetSystem(const char *val, uint8_t clientId);
-
bool spiffsCleanup();
+ char * ipToStr(IPAddress ip);
+ bool prepareForPlaying(uint16_t stationId);
+ void configPostPlaying(uint16_t stationId);
FS* SDPLFS(){ return _SDplaylistFS; }
#if RTCSUPPORTED
bool isRTCFound(){ return _rtcFound; };
@@ -303,7 +325,6 @@ class Config {
#endif
FS* _SDplaylistFS;
void setDefaults();
- Ticker _sleepTimer;
static void doSleep();
uint16_t color565(uint8_t r, uint8_t g, uint8_t b);
void _setupVersion();
@@ -314,7 +335,6 @@ class Config {
uint16_t station = random(1, store.countStation);
return station;
}
- char _stationBuf[BUFLEN/2];
};
extern Config config;
diff --git a/yoRadio/src/core/controls.cpp b/yoRadio/src/core/controls.cpp
index 7eb5803..2175c5d 100644
--- a/yoRadio/src/core/controls.cpp
+++ b/yoRadio/src/core/controls.cpp
@@ -56,12 +56,11 @@ constexpr uint8_t nrOfButtons = sizeof(button) / sizeof(button[0]);
#include "../IRremoteESP8266/IRtext.h"
#include "../IRremoteESP8266/IRutils.h"
uint8_t irVolRepeat = 0;
-const uint16_t kCaptureBufferSize = 1024;
-const uint8_t kTimeout = IR_TIMEOUT;
+//const uint16_t kCaptureBufferSize = 1024;
const uint16_t kMinUnknownSize = 12;
#define LEGACY_TIMING_INFO false
-IRrecv irrecv(IR_PIN, kCaptureBufferSize, kTimeout, true);
+IRrecv irrecv(IR_PIN, IR_BUFSIZE, IR_TIMEOUT, true);
decode_results irResults;
#endif
diff --git a/yoRadio/src/core/display.cpp b/yoRadio/src/core/display.cpp
index bba7421..fd866f3 100644
--- a/yoRadio/src/core/display.cpp
+++ b/yoRadio/src/core/display.cpp
@@ -5,13 +5,48 @@
#include "display.h"
#include "player.h"
#include "network.h"
-
+#include "netserver.h"
+#include "timekeeper.h"
Display display;
#ifdef USE_NEXTION
Nextion nextion;
#endif
+#ifndef CORE_STACK_SIZE
+ #define CORE_STACK_SIZE 1024*4
+#endif
+#ifndef DSP_TASK_PRIORITY
+ #define DSP_TASK_PRIORITY 2
+#endif
+#ifndef DSP_TASK_CORE_ID
+ #define DSP_TASK_CORE_ID 0
+#endif
+
+QueueHandle_t displayQueue;
+
+static void loopDspTask(void * pvParameters){
+ while(true){
+ #ifndef DUMMYDISPLAY
+ if(displayQueue==NULL) break;
+ netserver.loop();
+ if(timekeeper.loop0())
+ display.loop();
+ // will NOT delay here, would use message dequeue timeout instead
+ //vTaskDelay(DSP_TASK_DELAY);
+ #else
+ netserver.loop();
+ timekeeper.loop0();
+ vTaskDelay(10);
+ #endif
+ }
+ vTaskDelete( NULL );
+}
+
+void Display::_createDspTask(){
+ xTaskCreatePinnedToCore(loopDspTask, "DspTask", CORE_STACK_SIZE, NULL, DSP_TASK_PRIORITY, NULL, DSP_TASK_CORE_ID);
+}
+
#ifndef DUMMYDISPLAY
//============================================================================================================================
DspCore dsp;
@@ -19,40 +54,27 @@ DspCore dsp;
Page *pages[] = { new Page(), new Page(), new Page(), new Page() };
#ifndef DSQ_SEND_DELAY
- #define DSQ_SEND_DELAY portMAX_DELAY
+ //#define DSQ_SEND_DELAY portMAX_DELAY
+ #define DSQ_SEND_DELAY pdMS_TO_TICKS(200)
#endif
-#ifndef CORE_STACK_SIZE
- #define CORE_STACK_SIZE 1024*3
-#endif
#ifndef DSP_TASK_DELAY
- #define DSP_TASK_DELAY pdMS_TO_TICKS(10)
+ #define DSP_TASK_DELAY pdMS_TO_TICKS(20) // cap for 50 fps
#endif
+
+// will use DSP_QUEUE_TICKS as delay interval for display task runner when there are no msgs in a queue to process
+#define DSP_QUEUE_TICKS DSP_TASK_DELAY
+
#if !((DSP_MODEL==DSP_ST7735 && DTYPE==INITR_BLACKTAB) || DSP_MODEL==DSP_ST7789 || DSP_MODEL==DSP_ST7796 || DSP_MODEL==DSP_ILI9488 || DSP_MODEL==DSP_ILI9486 || DSP_MODEL==DSP_ILI9341 || DSP_MODEL==DSP_ILI9225)
#undef BITRATE_FULL
#define BITRATE_FULL false
#endif
-TaskHandle_t DspTask;
-QueueHandle_t displayQueue;
+
void returnPlayer(){
display.putRequest(NEWMODE, PLAYER);
}
-void Display::_createDspTask(){
- xTaskCreatePinnedToCore(loopDspTask, "DspTask", CORE_STACK_SIZE, NULL, 4, &DspTask, !xPortGetCoreID());
-}
-
-void loopDspTask(void * pvParameters){
- while(true){
- if(displayQueue==NULL) break;
- display.loop();
- vTaskDelay(DSP_TASK_DELAY);
- }
- vTaskDelete( NULL );
- DspTask=NULL;
-}
-
void Display::init() {
Serial.print("##[BOOT]#\tdisplay.init\t");
#ifdef USE_NEXTION
@@ -117,7 +139,7 @@ void Display::_buildPager(){
_volbar = new SliderWidget(volbarConf, config.theme.volbarin, config.theme.background, 254, config.theme.volbarout);
#endif
#ifndef HIDE_HEAPBAR
- _heapbar = new SliderWidget(heapbarConf, config.theme.buffer, config.theme.background, psramInit()?300000:1600 * AUDIOBUFFER_MULTIPLIER2);
+ _heapbar = new SliderWidget(heapbarConf, config.theme.buffer, config.theme.background, psramInit()?300000:1600 * config.store.abuff);
#endif
#ifndef HIDE_VOL
_voltxt = new TextWidget(voltxtConf, 10, false, config.theme.vol, config.theme.background);
@@ -197,7 +219,7 @@ void Display::_apScreen() {
TextWidget *appass2 = (TextWidget*) &_boot->addWidget(new TextWidget(apPass2Conf, 30, false, config.theme.clock, config.theme.background));
appass2->setText(apPassword);
ScrollWidget *bootSett = (ScrollWidget*) &_boot->addWidget(new ScrollWidget("*", apSettConf, config.theme.title2, config.theme.background));
- bootSett->setText(WiFi.softAPIP().toString().c_str(), apSettFmt);
+ bootSett->setText(config.ipToStr(WiFi.softAPIP()), apSettFmt);
_pager.addPage(_boot);
_pager.setPage(_boot);
#else
@@ -234,7 +256,7 @@ void Display::_start() {
if(_vuwidget) _vuwidget->lock();
if(_rssi) _setRSSI(WiFi.RSSI());
#ifndef HIDE_IP
- if(_volip) _volip->setText(WiFi.localIP().toString().c_str(), iptxtFmt);
+ if(_volip) _volip->setText(config.ipToStr(WiFi.localIP()), iptxtFmt);
#endif
_pager.setPage( pages[PG_PLAYER]);
_volume();
@@ -254,11 +276,6 @@ void Display::_showDialog(const char *title){
_meta.setText(title);
}
-void Display::_setReturnTicker(uint8_t time_s){
- _returnTicker.detach();
- _returnTicker.once(time_s, returnPlayer);
-}
-
void Display::_swichMode(displayMode_e newmode) {
#ifdef USE_NEXTION
//nextion.swichMode(newmode);
@@ -276,7 +293,6 @@ void Display::_swichMode(displayMode_e newmode) {
dsp.clearDsp();
#endif
numOfNextStation = 0;
- _returnTicker.detach();
#ifdef META_MOVE
_meta.moveBack();
#endif
@@ -304,7 +320,7 @@ void Display::_swichMode(displayMode_e newmode) {
#ifndef HIDE_IP
_showDialog(const_DlgVolume);
#else
- _showDialog(WiFi.localIP().toString().c_str());
+ _showDialog(config.ipToStr(WiFi.localIP()));
#endif
_nums.setText(config.store.volume, numtxtFmt);
}
@@ -329,11 +345,11 @@ void Display::resetQueue(){
void Display::_drawPlaylist() {
dsp.drawPlaylist(currentPlItem);
- _setReturnTicker(30);
+ timekeeper.waitAndReturnPlayer(30);
}
void Display::_drawNextStationNum(uint16_t num) {
- _setReturnTicker(30);
+ timekeeper.waitAndReturnPlayer(30);
_meta.setText(config.stationByNum(num));
_nums.setText(num, "%d");
}
@@ -374,9 +390,7 @@ void Display::_layoutChange(bool played){
}
}
}
-#ifndef DSP_QUEUE_TICKS
- #define DSP_QUEUE_TICKS 0
-#endif
+
void Display::loop() {
if(_bootStep==0) {
_pager.begin();
@@ -429,7 +443,7 @@ void Display::loop() {
if(_weather) _weather->lock(!config.store.showweather);
if(!config.store.showweather){
#ifndef HIDE_IP
- if(_volip) _volip->setText(WiFi.localIP().toString().c_str(), iptxtFmt);
+ if(_volip) _volip->setText(config.ipToStr(WiFi.localIP()), iptxtFmt);
#endif
}else{
if(_weather) _weather->setText(const_getWeather);
@@ -463,13 +477,19 @@ void Display::loop() {
case DSP_START: _start(); break;
case NEWIP: {
#ifndef HIDE_IP
- if(_volip) _volip->setText(WiFi.localIP().toString().c_str(), iptxtFmt);
+ if(_volip) _volip->setText(config.ipToStr(WiFi.localIP()), iptxtFmt);
#endif
break;
}
default: break;
+
+ // check if there are more messages waiting in the Q, in this case break the loop() and go
+ // for another round to evict next message, do not waste time to redraw the screen, etc...
+ if (uxQueueMessagesWaiting(displayQueue))
+ return;
}
}
+
dsp.loop();
#if I2S_DOUT==255
player.computeVUlevel();
@@ -561,7 +581,7 @@ void Display::_volume() {
if(_voltxt) _voltxt->setText(config.store.volume, voltxtFmt);
#endif
if(_mode==VOL) {
- _setReturnTicker(3);
+ timekeeper.waitAndReturnPlayer(3);
_nums.setText(config.store.volume, numtxtFmt);
}
/*#ifdef USE_NEXTION
diff --git a/yoRadio/src/core/display.h b/yoRadio/src/core/display.h
index 6d2247a..cb3b14c 100644
--- a/yoRadio/src/core/display.h
+++ b/yoRadio/src/core/display.h
@@ -3,20 +3,18 @@
#include "options.h"
#include "Arduino.h"
-#include
#include "config.h"
#include "common.h"
#include "../displays/dspcore.h"
-
-
#if NEXTION_RX!=255 && NEXTION_TX!=255
#define USE_NEXTION
#include "../displays/nextion.h"
#endif
+//static void loopDspTask(void * pvParameters);
+
#ifndef DUMMYDISPLAY
- void loopDspTask(void * pvParameters);
class Display {
public:
@@ -54,7 +52,6 @@ class Display {
ClockWidget _clock;
Page *_boot;
TextWidget *_bootstring, *_volip, *_voltxt, *_rssi, *_bitrate;
- Ticker _returnTicker;
uint8_t _bootStep;
void _time(bool redraw = false);
void _apScreen();
@@ -68,7 +65,6 @@ class Display {
void _showDialog(const char *title);
void _buildPager();
void _bootScreen();
- void _setReturnTicker(uint8_t time_s);
void _layoutChange(bool played);
void _setRSSI(int rssi);
};
@@ -98,6 +94,8 @@ class Display {
bool deepsleep(){return true;}
void wakeup(){}
void printPLitem(uint8_t pos, const char* item){}
+ private:
+ void _createDspTask();
};
#endif
diff --git a/yoRadio/src/core/mqtt.cpp b/yoRadio/src/core/mqtt.cpp
index 497c83a..1eda8dd 100644
--- a/yoRadio/src/core/mqtt.cpp
+++ b/yoRadio/src/core/mqtt.cpp
@@ -9,7 +9,7 @@
AsyncMqttClient mqttClient;
TimerHandle_t mqttReconnectTimer;
-char topic[140], status[BUFLEN*3], vol[5], buf[20];
+char topic[100], status[BUFLEN+50];
void connectToMqtt() {
mqttClient.connect();
@@ -25,8 +25,10 @@ void mqttInit() {
connectToMqtt();
}
+void zeroBuffer(){ memset(topic, 0, sizeof(topic)); memset(status, 0, sizeof(status)); }
+
void onMqttConnect(bool sessionPresent) {
- memset(topic, 0, 140);
+ zeroBuffer();
sprintf(topic, "%s%s", MQTT_ROOT_TOPIC, "command");
mqttClient.subscribe(topic, 2);
mqttPublishStatus();
@@ -36,13 +38,12 @@ void onMqttConnect(bool sessionPresent) {
void mqttPublishStatus() {
if(mqttClient.connected()){
- memset(topic, 0, 140);
- memset(status, 0, BUFLEN*3);
+ zeroBuffer();
sprintf(topic, "%s%s", MQTT_ROOT_TOPIC, "status");
- char name[BUFLEN*2];
- char title[BUFLEN*2];
- config.escapeQuotes(config.station.name, name, sizeof(name));
- config.escapeQuotes(config.station.title, title, sizeof(name));
+ char name[BUFLEN/2];
+ char title[BUFLEN/2];
+ config.escapeQuotes(config.station.name, name, sizeof(name)-10);
+ config.escapeQuotes(config.station.title, title, sizeof(title)-10);
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);
}
@@ -50,17 +51,17 @@ void mqttPublishStatus() {
void mqttPublishPlaylist() {
if(mqttClient.connected()){
- memset(topic, 0, 140);
- memset(status, 0, BUFLEN*3);
+ zeroBuffer();
sprintf(topic, "%s%s", MQTT_ROOT_TOPIC, "playlist");
- sprintf(status, "http://%s%s", WiFi.localIP().toString().c_str(), PLAYLIST_PATH);
+ sprintf(status, "http://%s%s", config.ipToStr(WiFi.localIP()), PLAYLIST_PATH);
mqttClient.publish(topic, 0, true, status);
}
}
void mqttPublishVolume(){
if(mqttClient.connected()){
- memset(topic, 0, 140);
+ zeroBuffer();
+ char vol[5];
memset(vol, 0, 5);
sprintf(topic, "%s%s", MQTT_ROOT_TOPIC, "volume");
sprintf(vol, "%d", config.store.volume);
@@ -76,75 +77,64 @@ void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
if (len == 0) return;
- memset(buf, 0, 20);
- strlcpy(buf, payload, len+1);
- if (strcmp(buf, "prev") == 0) {
- player.prev();
+ if(len<20){
+ char buf[len+1];
+ strncpy(buf, payload, len);
+ buf[len]='\0';
+ if (strcmp(buf, "prev") == 0) { player.sendCommand({PR_PREV, 0}); return; }
+ if (strcmp(buf, "next") == 0) { player.sendCommand({PR_NEXT, 0}); return; }
+ if (strcmp(buf, "toggle") == 0) { player.sendCommand({PR_TOGGLE, 0}); return; }
+ if (strcmp(buf, "stop") == 0) { player.sendCommand({PR_STOP, 0}); return; }
+ if (strcmp(buf, "start") == 0 || strcmp(buf, "play") == 0) { player.sendCommand({PR_PLAY, config.lastStation()}); return; }
+ if (strcmp(buf, "boot") == 0 || strcmp(buf, "reboot") == 0) { ESP.restart(); return; }
+ if (strcmp(buf, "volm") == 0) {
+ player.stepVol(false);
+ return;
+ }
+ if (strcmp(buf, "volp") == 0) {
+ player.stepVol(true);
+ return;
+ }
+ if (strcmp(buf, "turnoff") == 0) {
+ uint8_t sst = config.store.smartstart;
+ config.setDspOn(0);
+ player.sendCommand({PR_STOP, 0});
+ delay(100);
+ config.saveValue(&config.store.smartstart, sst);
+ return;
+ }
+ if (strcmp(buf, "turnon") == 0) {
+ config.setDspOn(1);
+ if (config.store.smartstart == 1) player.sendCommand({PR_PLAY, config.lastStation()});
+ return;
+ }
+ int volume;
+ if ( sscanf(buf, "vol %d", &volume) == 1) {
+ if (volume < 0) volume = 0;
+ if (volume > 254) volume = 254;
+ player.setVol(volume);
+ return;
+ }
+ int sb;
+ if (sscanf(buf, "play %d", &sb) == 1 ) {
+ if (sb < 1) sb = 1;
+ uint16_t cs = config.playlistLength();
+ if (sb >= cs) sb = cs;
+ player.sendCommand({PR_PLAY, (uint16_t)sb});
+ return;
+ }
+ }else{
+ if(len>MQTT_BURL_SIZE) return;
+ strncpy(player.burl, payload, len);
+ player.burl[len]='\0';
+ player.sendCommand({PR_BURL, 0});
return;
}
- if (strcmp(buf, "next") == 0) {
- player.next();
- return;
- }
- if (strcmp(buf, "toggle") == 0) {
- player.toggle();
- return;
- }
- if (strcmp(buf, "stop") == 0) {
- player.sendCommand({PR_STOP, 0});
- //telnet.info();
- return;
- }
- if (strcmp(buf, "start") == 0 || strcmp(buf, "play") == 0) {
- player.sendCommand({PR_PLAY, config.lastStation()});
- return;
- }
- if (strcmp(buf, "boot") == 0 || strcmp(buf, "reboot") == 0) {
- ESP.restart();
- return;
- }
- if (strcmp(buf, "volm") == 0) {
- player.stepVol(false);
- return;
- }
- if (strcmp(buf, "volp") == 0) {
- player.stepVol(true);
- return;
- }
- if (strcmp(buf, "turnoff") == 0) {
- uint8_t sst = config.store.smartstart;
- config.setDspOn(0);
- player.sendCommand({PR_STOP, 0});
- //telnet.info();
- delay(100);
- config.saveValue(&config.store.smartstart, sst);
- return;
- }
- if (strcmp(buf, "turnon") == 0) {
- config.setDspOn(1);
- if (config.store.smartstart == 1) player.sendCommand({PR_PLAY, config.lastStation()});
- return;
- }
- int volume;
- if ( sscanf(buf, "vol %d", &volume) == 1) {
- if (volume < 0) volume = 0;
- if (volume > 254) volume = 254;
- player.setVol(volume);
- return;
- }
- int sb;
- if (sscanf(buf, "play %d", &sb) == 1 ) {
- if (sb < 1) sb = 1;
- uint16_t cs = config.playlistLength();
- if (sb >= cs) sb = cs;
- player.sendCommand({PR_PLAY, (uint16_t)sb});
- return;
- }
- if (strstr(buf, "http")==buf){
+ /*if (strstr(buf, "http")==0){
if(len+1>sizeof(player.burl)) return;
strlcpy(player.burl, payload, len+1);
return;
- }
+ }*/
}
#endif // #ifdef MQTT_ROOT_TOPIC
diff --git a/yoRadio/src/core/mqtt.h b/yoRadio/src/core/mqtt.h
index 074da59..bc76960 100644
--- a/yoRadio/src/core/mqtt.h
+++ b/yoRadio/src/core/mqtt.h
@@ -1,11 +1,9 @@
#ifndef mqtt_h
#define mqtt_h
#include "options.h"
-#ifdef MQTT_ROOT_TOPIC
-//#if __has_include("../../mqttoptions.h")
-//#include "../../mqttoptions.h"
-#include "../async-mqtt-client/AsyncMqttClient.h"
+#ifdef MQTT_ROOT_TOPIC
+#include "../async-mqtt-client/AsyncMqttClient.h"
void mqttInit();
void connectToMqtt();
@@ -15,6 +13,7 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties
void mqttPublishStatus();
void mqttPublishPlaylist();
void mqttPublishVolume();
+void zeroBuffer();
#endif // #ifdef MQTT_ROOT_TOPIC
diff --git a/yoRadio/src/core/netserver.cpp b/yoRadio/src/core/netserver.cpp
index e58366e..e808ca3 100644
--- a/yoRadio/src/core/netserver.cpp
+++ b/yoRadio/src/core/netserver.cpp
@@ -1,7 +1,6 @@
#include "netserver.h"
#include
-#include "config.h"
#include "player.h"
#include "telnet.h"
#include "display.h"
@@ -10,17 +9,10 @@
#include "mqtt.h"
#include "controls.h"
#include "commandhandler.h"
+#include "timekeeper.h"
#include
#include
-
-#if USE_OTA
-#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
-#include
-#else
-#include
-#endif
-#include
-#endif
+//#include
#ifdef USE_SD
#include "sdmanager.h"
@@ -29,7 +21,7 @@
#define MIN_MALLOC 24112
#endif
#ifndef NSQ_SEND_DELAY
- #define NSQ_SEND_DELAY (TickType_t)100 //portMAX_DELAY?
+ #define NSQ_SEND_DELAY pdMS_TO_TICKS(100) //portMAX_DELAY?
#endif
//#define CORS_DEBUG //Enable CORS policy: 'Access-Control-Allow-Origin' (for testing)
@@ -46,20 +38,19 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
bool shouldReboot = false;
#ifdef MQTT_ROOT_TOPIC
-Ticker mqttplaylistticker;
+//Ticker mqttplaylistticker;
bool mqttplaylistblock = false;
void mqttplaylistSend() {
mqttplaylistblock = true;
- mqttplaylistticker.detach();
+// mqttplaylistticker.detach();
mqttPublishPlaylist();
mqttplaylistblock = false;
}
#endif
char* updateError() {
- static char ret[140] = {0};
- sprintf(ret, "Update failed with error (%d)
%s", (int)Update.getError(), Update.errorString());
- return ret;
+ sprintf(netserver.nsBuf, "Update failed with error (%d)
%s", (int)Update.getError(), Update.errorString());
+ return netserver.nsBuf;
}
bool NetServer::begin(bool quiet) {
@@ -67,6 +58,7 @@ bool NetServer::begin(bool quiet) {
if(!quiet) Serial.print("##[BOOT]#\tnetserver.begin\t");
importRequest = IMDONE;
irRecordEnable = false;
+ playerBufMax = psramInit()?300000:1600 * config.store.abuff;
nsQueue = xQueueCreate( 20, sizeof( nsRequestParams_t ) );
while(nsQueue==NULL){;}
@@ -82,43 +74,8 @@ bool NetServer::begin(bool quiet) {
webserver.begin();
if(strlen(config.store.mdnsname)>0)
MDNS.begin(config.store.mdnsname);
-
websocket.onEvent(onWsEvent);
webserver.addHandler(&websocket);
-#if USE_OTA
- if(strlen(config.store.mdnsname)>0)
- ArduinoOTA.setHostname(config.store.mdnsname);
-#ifdef OTA_PASS
- ArduinoOTA.setPassword(OTA_PASS);
-#endif
- ArduinoOTA
- .onStart([]() {
- display.putRequest(NEWMODE, UPDATING);
- telnet.printf("Start OTA updating %s\n", ArduinoOTA.getCommand() == U_FLASH?"firmware":"filesystem");
- })
- .onEnd([]() {
- telnet.printf("\nEnd OTA update, Rebooting...\n");
- })
- .onProgress([](unsigned int progress, unsigned int total) {
- telnet.printf("Progress OTA: %u%%\r", (progress / (total / 100)));
- })
- .onError([](ota_error_t error) {
- telnet.printf("Error[%u]: ", error);
- if (error == OTA_AUTH_ERROR) {
- telnet.printf("Auth Failed\n");
- } else if (error == OTA_BEGIN_ERROR) {
- telnet.printf("Begin Failed\n");
- } else if (error == OTA_CONNECT_ERROR) {
- telnet.printf("Connect Failed\n");
- } else if (error == OTA_RECEIVE_ERROR) {
- telnet.printf("Receive Failed\n");
- } else if (error == OTA_END_ERROR) {
- telnet.printf("End Failed\n");
- }
- });
- ArduinoOTA.begin();
-#endif
-
if(!quiet) Serial.println("done");
return true;
}
@@ -152,6 +109,7 @@ void NetServer::chunkedHtmlPage(const String& contentType, AsyncWebServerRequest
strlcpy(chunkedPathBuffer, path, sizeof(chunkedPathBuffer)-1);
AsyncWebServerResponse *response;
response = request->beginChunkedResponse(contentType, chunkedHtmlPageCallback);
+ response->addHeader("Cache-Control","max-age=31536000");
request->send(response);
}
@@ -181,13 +139,12 @@ const char *getFormat(BitrateFormat _format) {
}
}
-char wsbuf[BUFLEN * 2];
void NetServer::processQueue(){
if(nsQueue==NULL) return;
nsRequestParams_t request;
if(xQueueReceive(nsQueue, &request, NS_QUEUE_TICKS)){
- memset(wsbuf, 0, BUFLEN * 2);
uint8_t clientId = request.clientId;
+ wsBuf[0]='\0';
switch (request.type) {
case PLAYLIST: getPlaylist(clientId); break;
case PLAYLISTSAVED: {
@@ -205,36 +162,46 @@ void NetServer::processQueue(){
}
case GETACTIVE: {
bool dbgact = false, nxtn=false;
- String act = F("\"group_wifi\",");
+ //String act = F("\"group_wifi\",");
+ nsBuf[0]='\0';
+ APPEND_GROUP("group_wifi");
if (network.status == CONNECTED) {
- act += F("\"group_system\",");
- if (BRIGHTNESS_PIN != 255 || DSP_CAN_FLIPPED || DSP_MODEL == DSP_NOKIA5110 || dbgact) act += F("\"group_display\",");
+ //act += F("\"group_system\",");
+ APPEND_GROUP("group_system");
+ if (BRIGHTNESS_PIN != 255 || DSP_CAN_FLIPPED || DSP_MODEL == DSP_NOKIA5110 || dbgact) APPEND_GROUP("group_display");
#ifdef USE_NEXTION
- act += F("\"group_nextion\",");
- if (!SHOW_WEATHER || dbgact) act += F("\"group_weather\",");
+ APPEND_GROUP("group_nextion");
+ if (!SHOW_WEATHER || dbgact) APPEND_GROUP("group_weather");
nxtn=true;
#endif
#if defined(LCD_I2C) || defined(DSP_OLED)
- act += F("\"group_oled\",");
+ APPEND_GROUP("group_oled");
#endif
- #ifndef HIDE_VU
- act += F("\"group_vu\",");
+ #if !defined(HIDE_VU) && !defined(DUMMYDISPLAY)
+ APPEND_GROUP("group_vu");
+ #endif
+ if (BRIGHTNESS_PIN != 255 || nxtn || dbgact) APPEND_GROUP("group_brightness");
+ if (DSP_CAN_FLIPPED || dbgact) APPEND_GROUP("group_tft");
+ if (TS_MODEL != TS_MODEL_UNDEFINED || dbgact) APPEND_GROUP("group_touch");
+ if (DSP_MODEL == DSP_NOKIA5110) APPEND_GROUP("group_nokia");
+ APPEND_GROUP("group_timezone");
+ if (SHOW_WEATHER || dbgact) APPEND_GROUP("group_weather");
+ APPEND_GROUP("group_controls");
+ if (ENC_BTNL != 255 || ENC2_BTNL != 255 || dbgact) APPEND_GROUP("group_encoder");
+ if (IR_PIN != 255 || dbgact) APPEND_GROUP("group_ir");
+ if (!psramInit()) APPEND_GROUP("group_buffer");
+ #if RTCSUPPORTED
+ APPEND_GROUP("group_rtc");
+ #else
+ APPEND_GROUP("group_wortc");
#endif
- if (BRIGHTNESS_PIN != 255 || nxtn || dbgact) act += F("\"group_brightness\",");
- if (DSP_CAN_FLIPPED || dbgact) act += F("\"group_tft\",");
- if (TS_MODEL != TS_MODEL_UNDEFINED || dbgact) act += F("\"group_touch\",");
- if (DSP_MODEL == DSP_NOKIA5110) act += F("\"group_nokia\",");
- act += F("\"group_timezone\",");
- if (SHOW_WEATHER || dbgact) act += F("\"group_weather\",");
- act += F("\"group_controls\",");
- if (ENC_BTNL != 255 || ENC2_BTNL != 255 || dbgact) act += F("\"group_encoder\",");
- if (IR_PIN != 255 || dbgact) act += F("\"group_ir\",");
}
- act = act.substring(0, act.length() - 1);
- sprintf (wsbuf, "{\"act\":[%s]}", act.c_str());
+ size_t len = strlen(nsBuf);
+ if (len > 0 && nsBuf[len - 1] == ',') nsBuf[len - 1] = '\0';
+
+ snprintf(wsBuf, sizeof(wsBuf), "{\"act\":[%s]}", nsBuf);
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);
@@ -249,15 +216,19 @@ void NetServer::processQueue(){
return;
break;
}
- case GETSYSTEM: sprintf (wsbuf, "{\"sst\":%d,\"aif\":%d,\"vu\":%d,\"softr\":%d,\"vut\":%d,\"mdns\":\"%s\"}",
+ case GETSYSTEM: sprintf (wsBuf, "{\"sst\":%d,\"aif\":%d,\"vu\":%d,\"softr\":%d,\"vut\":%d,\"mdns\":\"%s\",\"ipaddr\":\"%s\", \"abuff\": %d, \"telnet\": %d, \"watchdog\": %d }",
config.store.smartstart != 2,
config.store.audioinfo,
config.store.vumeter,
config.store.softapdelay,
config.vuThreshold,
- config.store.mdnsname);
+ config.store.mdnsname,
+ config.ipToStr(WiFi.localIP()),
+ config.store.abuff,
+ config.store.telnet,
+ config.store.watchdog);
break;
- case GETSCREEN: sprintf (wsbuf, "{\"flip\":%d,\"inv\":%d,\"nump\":%d,\"tsf\":%d,\"tsd\":%d,\"dspon\":%d,\"br\":%d,\"con\":%d,\"scre\":%d,\"scrt\":%d,\"scrb\":%d,\"scrpe\":%d,\"scrpt\":%d,\"scrpb\":%d}",
+ case GETSCREEN: sprintf (wsBuf, "{\"flip\":%d,\"inv\":%d,\"nump\":%d,\"tsf\":%d,\"tsd\":%d,\"dspon\":%d,\"br\":%d,\"con\":%d,\"scre\":%d,\"scrt\":%d,\"scrb\":%d,\"scrpe\":%d,\"scrpt\":%d,\"scrpb\":%d}",
config.store.flipscreen,
config.store.invertdisplay,
config.store.numplaylist,
@@ -273,52 +244,55 @@ void NetServer::processQueue(){
config.store.screensaverPlayingTimeout,
config.store.screensaverPlayingBlank);
break;
- case GETTIMEZONE: sprintf (wsbuf, "{\"tzh\":%d,\"tzm\":%d,\"sntp1\":\"%s\",\"sntp2\":\"%s\"}",
+ case GETTIMEZONE: sprintf (wsBuf, "{\"tzh\":%d,\"tzm\":%d,\"sntp1\":\"%s\",\"sntp2\":\"%s\", \"timeint\":%d,\"timeintrtc\":%d}",
config.store.tzHour,
config.store.tzMin,
config.store.sntp1,
- config.store.sntp2);
+ config.store.sntp2,
+ config.store.timeSyncInterval,
+ config.store.timeSyncIntervalRTC);
break;
- case GETWEATHER: sprintf (wsbuf, "{\"wen\":%d,\"wlat\":\"%s\",\"wlon\":\"%s\",\"wkey\":\"%s\"}",
+ case GETWEATHER: sprintf (wsBuf, "{\"wen\":%d,\"wlat\":\"%s\",\"wlon\":\"%s\",\"wkey\":\"%s\",\"wint\":%d}",
config.store.showweather,
config.store.weatherlat,
config.store.weatherlon,
- config.store.weatherkey);
+ config.store.weatherkey,
+ config.store.weatherSyncInterval);
break;
- case GETCONTROLS: sprintf (wsbuf, "{\"vols\":%d,\"enca\":%d,\"irtl\":%d,\"skipup\":%d}",
+ case GETCONTROLS: sprintf (wsBuf, "{\"vols\":%d,\"enca\":%d,\"irtl\":%d,\"skipup\":%d}",
config.store.volsteps,
config.store.encacc,
config.store.irtlp,
config.store.skipPlaylistUpDown);
break;
- case DSPON: sprintf (wsbuf, "{\"dspontrue\":%d}", 1); break;
+ case DSPON: sprintf (wsBuf, "{\"dspontrue\":%d}", 1); break;
case STATION: requestOnChange(STATIONNAME, clientId); requestOnChange(ITEM, clientId); 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, "{\"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}",
+ 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, "{\"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}, {\"id\":\"heap\", \"value\": %d}]}", rssi, (player.isRunning() && config.store.audioinfo)?(int)(100*player.inBufferFilled()/playerBufMax):0); /*rssi = 255;*/ break;
+ case SDPOS: sprintf (wsBuf, "{\"sdpos\": %lu,\"sdend\": %lu,\"sdtpos\": %lu,\"sdtend\": %lu}",
player.getFilePos(),
player.getFileSize(),
player.getAudioCurrentTime(),
player.getAudioFileDuration());
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, "{\"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;
+ case SDLEN: sprintf (wsBuf, "{\"sdmin\": %lu,\"sdmax\": %lu}", player.sd_min, player.sd_max); break;
+ case SDSNUFFLE: sprintf (wsBuf, "{\"snuffle\": %d}", config.store.sdsnuffle); 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
case CHANGEMODE: config.changeMode(config.newConfigMode); return; break;
#endif
default: break;
}
- if (strlen(wsbuf) > 0) {
- if (clientId == 0) { websocket.textAll(wsbuf); }else{ websocket.text(clientId, wsbuf); }
+ if (strlen(wsBuf) > 0) {
+ if (clientId == 0) { websocket.textAll(wsBuf); }else{ websocket.text(clientId, wsBuf); }
#ifdef MQTT_ROOT_TOPIC
if (clientId == 0 && (request.type == STATION || request.type == ITEM || request.type == TITLE || request.type == MODE)) mqttPublishStatus();
if (clientId == 0 && request.type == VOLUME) mqttPublishVolume();
@@ -341,22 +315,19 @@ void NetServer::loop() {
default: break;
}
processQueue();
-#if USE_OTA
- ArduinoOTA.handle();
-#endif
}
#if IR_PIN!=255
void NetServer::irToWs(const char* protocol, uint64_t irvalue) {
- char buf[BUFLEN] = { 0 };
- sprintf (buf, "{\"ircode\": %llu, \"protocol\": \"%s\"}", irvalue, protocol);
- websocket.textAll(buf);
+ wsBuf[0]='\0';
+ sprintf (wsBuf, "{\"ircode\": %llu, \"protocol\": \"%s\"}", irvalue, protocol);
+ websocket.textAll(wsBuf);
}
void NetServer::irValsToWs() {
if (!irRecordEnable) return;
- char buf[BUFLEN] = { 0 };
- sprintf (buf, "{\"irvals\": [%llu, %llu, %llu]}", config.ircodes.irVals[config.irindex][0], config.ircodes.irVals[config.irindex][1], config.ircodes.irVals[config.irindex][2]);
- websocket.textAll(buf);
+ wsBuf[0]='\0';
+ sprintf (wsBuf, "{\"irvals\": [%llu, %llu, %llu]}", config.ircodes.irVals[config.irindex][0], config.ircodes.irVals[config.irindex][1], config.ircodes.irVals[config.irindex][2]);
+ websocket.textAll(wsBuf);
}
#endif
@@ -364,32 +335,36 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client
AwsFrameInfo *info = (AwsFrameInfo*)arg;
if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
data[len] = 0;
- char comnd[65], val[65];
- if (config.parseWsCommand((const char*)data, comnd, val, 65)) {
- if (strcmp(comnd, "trebble") == 0) {
- int8_t valb = atoi(val);
+ if (config.parseWsCommand((const char*)data, _wscmd, _wsval, 65)) {
+ if (strcmp(_wscmd, "ping") == 0) {
+ websocket.text(clientId, "{\"pong\": 1}");
+ return;
+ }
+ if (strcmp(_wscmd, "trebble") == 0) {
+ int8_t valb = atoi(_wsval);
config.setTone(config.store.bass, config.store.middle, valb);
return;
}
- if (strcmp(comnd, "middle") == 0) {
- int8_t valb = atoi(val);
+ if (strcmp(_wscmd, "middle") == 0) {
+ int8_t valb = atoi(_wsval);
config.setTone(config.store.bass, valb, config.store.trebble);
return;
}
- if (strcmp(comnd, "bass") == 0) {
- int8_t valb = atoi(val);
+ if (strcmp(_wscmd, "bass") == 0) {
+ int8_t valb = atoi(_wsval);
config.setTone(valb, config.store.middle, config.store.trebble);
return;
}
- if (strcmp(comnd, "submitplaylistdone") == 0) {
+ if (strcmp(_wscmd, "submitplaylistdone") == 0) {
#ifdef MQTT_ROOT_TOPIC
- mqttplaylistticker.attach(5, mqttplaylistSend);
+ //mqttplaylistticker.attach(5, mqttplaylistSend);
+ timekeeper.waitAndDo(5, mqttplaylistSend);
#endif
if (player.isRunning()) player.sendCommand({PR_PLAY, -config.lastStation()});
return;
}
- if(cmd.exec(comnd, val, clientId)){
+ if(cmd.exec(_wscmd, _wsval, clientId)){
return;
}
}
@@ -397,9 +372,8 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client
}
void NetServer::getPlaylist(uint8_t clientId) {
- char buf[160] = {0};
- sprintf(buf, "{\"file\": \"http://%s%s\"}", WiFi.localIP().toString().c_str(), PLAYLIST_PATH);
- if (clientId == 0) { websocket.textAll(buf); } else { websocket.text(clientId, buf); }
+ sprintf(nsBuf, "{\"file\": \"http://%s%s\"}", config.ipToStr(WiFi.localIP()), PLAYLIST_PATH);
+ if (clientId == 0) { websocket.textAll(nsBuf); } else { websocket.text(clientId, nsBuf); }
}
int NetServer::_readPlaylistLine(File &file, char * line, size_t size){
@@ -413,27 +387,28 @@ int NetServer::_readPlaylistLine(File &file, char * line, size_t size){
bool NetServer::importPlaylist() {
if(config.getMode()==PM_SDCARD) return false;
+ //player.sendCommand({PR_STOP, 0});
File tempfile = SPIFFS.open(TMP_PATH, "r");
if (!tempfile) {
return false;
}
- char sName[BUFLEN], sUrl[BUFLEN], linePl[BUFLEN*3];;
+ char linePl[BUFLEN*3];
int sOvol;
_readPlaylistLine(tempfile, linePl, sizeof(linePl)-1);
- if (config.parseCSV(linePl, sName, sUrl, sOvol)) {
+ if (config.parseCSV(linePl, nsBuf, nsBuf2, sOvol)) {
tempfile.close();
SPIFFS.rename(TMP_PATH, PLAYLIST_PATH);
requestOnChange(PLAYLISTSAVED, 0);
return true;
}
- if (config.parseJSON(linePl, sName, sUrl, sOvol)) {
+ if (config.parseJSON(linePl, nsBuf, nsBuf2, sOvol)) {
File playlistfile = SPIFFS.open(PLAYLIST_PATH, "w");
- snprintf(linePl, sizeof(linePl)-1, "%s\t%s\t%d", sName, sUrl, 0);
+ snprintf(linePl, sizeof(linePl)-1, "%s\t%s\t%d", nsBuf, nsBuf2, 0);
playlistfile.println(linePl);
while (tempfile.available()) {
_readPlaylistLine(tempfile, linePl, sizeof(linePl)-1);
- if (config.parseJSON(linePl, sName, sUrl, sOvol)) {
- snprintf(linePl, sizeof(linePl)-1, "%s\t%s\t%d", sName, sUrl, 0);
+ if (config.parseJSON(linePl, nsBuf, nsBuf2, sOvol)) {
+ snprintf(linePl, sizeof(linePl)-1, "%s\t%s\t%d", nsBuf, nsBuf2, 0);
playlistfile.println(linePl);
}
}
@@ -461,11 +436,12 @@ void NetServer::resetQueue(){
if(nsQueue!=NULL) xQueueReset(nsQueue);
}
-int freeSpace;
void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
+ static int freeSpace = 0;
if(request->url()=="/upload"){
if (!index) {
if(filename!="tempwifi.csv"){
+ //player.sendCommand({PR_STOP, 0});
if(SPIFFS.exists(PLAYLIST_PATH)) SPIFFS.remove(PLAYLIST_PATH);
if(SPIFFS.exists(INDEX_PATH)) SPIFFS.remove(INDEX_PATH);
if(SPIFFS.exists(PLAYLIST_SD_PATH)) SPIFFS.remove(PLAYLIST_SD_PATH);
@@ -473,6 +449,8 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index,
}
freeSpace = (float)SPIFFS.totalBytes()/100*68-SPIFFS.usedBytes();
request->_tempFile = SPIFFS.open(TMP_PATH , "w");
+ }else{
+
}
if (len) {
if(freeSpace>index+len){
@@ -481,6 +459,7 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index,
}
if (final) {
request->_tempFile.close();
+ freeSpace = 0;
}
}else if(request->url()=="/update"){
if (!index) {
@@ -510,6 +489,7 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index,
}else{ // "/webboard"
DBGVB("File: %s, size:%u bytes, index: %u, final: %s\n", filename.c_str(), len, index, final?"true":"false");
if (!index) {
+ player.sendCommand({PR_STOP, 0});
String spath = "/www/";
if(filename=="playlist.csv" || filename=="wifi.csv") spath = "/data/";
request->_tempFile = SPIFFS.open(spath + filename , "w");
@@ -526,8 +506,8 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index,
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
switch (type) {
- 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_CONNECT: /*netserver.requestOnChange(STARTUP, client->id()); */if (config.store.audioinfo) Serial.printf("[WEBSOCKET] client #%lu connected from %s\n", client->id(), config.ipToStr(client->remoteIP())); break;
+ case WS_EVT_DISCONNECT: if (config.store.audioinfo) Serial.printf("[WEBSOCKET] client #%lu disconnected\n", client->id()); break;
case WS_EVT_DATA: netserver.onWsMessage(arg, data, len, client->id()); break;
case WS_EVT_PONG:
case WS_EVT_ERROR:
@@ -548,7 +528,7 @@ void handleNotFound(AsyncWebServerRequest * request) {
if(request->url()=="/emergency") { request->send_P(200, "text/html", emergency_form); return; }
if(request->method() == HTTP_POST && request->url()=="/webboard" && config.emptyFS) { request->redirect("/"); ESP.restart(); return; }
if (request->method() == HTTP_GET) {
- DBGVB("[%s] client ip=%s request of %s", __func__, request->client()->remoteIP().toString().c_str(), request->url().c_str());
+ DBGVB("[%s] client ip=%s request of %s", __func__, config.ipToStr(request->client()->remoteIP()), request->url().c_str());
if (strcmp(request->url().c_str(), PLAYLIST_PATH) == 0 ||
strcmp(request->url().c_str(), SSIDS_PATH) == 0 ||
strcmp(request->url().c_str(), INDEX_PATH) == 0 ||
@@ -595,13 +575,15 @@ void handleNotFound(AsyncWebServerRequest * request) {
return;
}
if (request->url() == "/variables.js") {
- 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);
+ sprintf (netserver.nsBuf, "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", netserver.nsBuf);
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);
+ //request->send_P(200, "text/html", index_html);
+ AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", index_html);
+ response->addHeader("Cache-Control","max-age=31536000");
+ request->send(response);
return;
}
if (request->method() == HTTP_GET && request->url() == "/webboard") {
@@ -618,11 +600,10 @@ void handleIndex(AsyncWebServerRequest * request) {
if(request->url()=="/" && request->method() == HTTP_GET ) { request->send_P(200, "text/html", emptyfs_html); return; }
if(request->url()=="/" && request->method() == HTTP_POST) {
if(request->arg("ssid")!="" && request->arg("pass")!=""){
- char buf[BUFLEN];
- memset(buf, 0, BUFLEN);
- snprintf(buf, BUFLEN, "%s\t%s", request->arg("ssid").c_str(), request->arg("pass").c_str());
+ netserver.nsBuf[0]='\0';
+ snprintf(netserver.nsBuf, sizeof(netserver.nsBuf), "%s\t%s", request->arg("ssid").c_str(), request->arg("pass").c_str());
request->redirect("/");
- config.saveWifiFromNextion(buf);
+ config.saveWifiFromNextion(netserver.nsBuf);
return;
}
request->redirect("/");
@@ -641,7 +622,12 @@ void handleIndex(AsyncWebServerRequest * request) {
}
#endif
if (strcmp(request->url().c_str(), "/") == 0 && request->params() == 0) {
- if(network.status == CONNECTED) request->send_P(200, "text/html", index_html); else request->redirect("/settings.html");
+ if(network.status == CONNECTED) {
+ AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", index_html);
+ response->addHeader("Cache-Control","max-age=31536000");
+ request->send(response);
+ //request->send_P(200, "text/html", index_html);
+ } else request->redirect("/settings.html");
return;
}
if(network.status == CONNECTED){
diff --git a/yoRadio/src/core/netserver.h b/yoRadio/src/core/netserver.h
index a6bbba0..1ab21a6 100644
--- a/yoRadio/src/core/netserver.h
+++ b/yoRadio/src/core/netserver.h
@@ -1,9 +1,11 @@
#ifndef netserver_h
#define netserver_h
#include "Arduino.h"
-
+#include "config.h"
#include "../AsyncWebServer/ESPAsyncWebServer.h"
+#define APPEND_GROUP(name) strcat(nsBuf, "\"" name "\",")
+
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"(
@@ -74,6 +76,7 @@ const char index_html[] PROGMEM = R"(
+