This commit is contained in:
e2002
2025-07-27 18:00:01 +03:00
parent bddc5bdf17
commit 507414da7e
35 changed files with 1257 additions and 819 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -13,4 +13,5 @@
--playlist-hover: #323232; --playlist-hover: #323232;
--section-gradient: #111111; --section-gradient: #111111;
--section-border: #555555; --section-border: #555555;
--heapbar-color: #3ea220;
} }

View File

@@ -40,20 +40,23 @@ extern "C" {
#endif #endif
#ifndef XTASK_MEM_SIZE #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 #endif
#ifndef XTASK_PRIOTITY #ifndef XTASK_PRIOTITY
#define XTASK_PRIOTITY 5 //3 #define XTASK_PRIOTITY 3 //3
#endif #endif
#ifndef ATCP_TASK_DELAY #ifndef ATCP_TASK_DELAY
#define ATCP_TASK_DELAY 2 #define ATCP_TASK_DELAY 2
#endif #endif
#ifndef XQUEUE_SIZE #ifndef XQUEUE_SIZE
#define XQUEUE_SIZE 128 // (32) //#define XQUEUE_SIZE 128 // (32)
#define XQUEUE_SIZE 32
#endif #endif
#ifndef SEND_ASYNC_EVENT_DELAY #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 #endif
class AsyncClient; class AsyncClient;

View File

@@ -61,9 +61,9 @@ size_t AudioBuffer::init() {
if(m_buffer == NULL) { if(m_buffer == NULL) {
// PSRAM not found, not configured or not enough available // PSRAM not found, not configured or not enough available
m_f_psram = false; 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_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) if(!m_buffer)
return 0; return 0;
@@ -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; if(timeout_ms_ssl) m_timeout_ms_ssl = timeout_ms_ssl;
} }
void Audio::connectTask(void* pvParams) {
ConnectParams* params = static_cast<ConnectParams*>(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) { bool Audio::connecttohost(const char* host, const char* user, const char* pwd) {
// user and pwd for authentification only, can be empty // 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(); uint32_t t = millis();
if(m_f_Log) AUDIO_INFO("connect to %s on port %d path %s", hostwoext, port, extension); if(m_f_Log) AUDIO_INFO("connect to %s on port %d path %s", hostwoext, port, extension);
if(!config.store.watchdog){
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);
}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){ if(res){
uint32_t dt = millis() - t; uint32_t dt = millis() - t;
strcpy(m_lastHost, l_host); strcpy(m_lastHost, l_host);

View File

@@ -32,10 +32,6 @@
#include <FFat.h> #include <FFat.h>
#endif // SDFATFS_USED #endif // SDFATFS_USED
#ifndef AUDIOBUFFER_MULTIPLIER2
#define AUDIOBUFFER_MULTIPLIER2 8
#endif
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) #if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
#include "hal/gpio_ll.h" #include "hal/gpio_ll.h"
#endif #endif
@@ -149,7 +145,7 @@ public:
protected: protected:
size_t m_buffSizePSRAM = 300000; // most webstreams limit the advance to 100...300Kbytes size_t m_buffSizePSRAM = 300000; // most webstreams limit the advance to 100...300Kbytes
//size_t m_buffSizeRAM = 1600 * 5; //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_buffSize = 0;
size_t m_freeSpace = 0; size_t m_freeSpace = 0;
size_t m_writeSpace = 0; size_t m_writeSpace = 0;
@@ -287,6 +283,7 @@ private:
void IIR_calculateCoefficients(int8_t G1, int8_t G2, int8_t G3); void IIR_calculateCoefficients(int8_t G1, int8_t G2, int8_t G3);
bool ts_parsePacket(uint8_t* packet, uint8_t* packetStart, uint8_t* packetLength); bool ts_parsePacket(uint8_t* packet, uint8_t* packetStart, uint8_t* packetLength);
void _computeVUlevel(int16_t sample[2]); void _computeVUlevel(int16_t sample[2]);
static void connectTask(void* pvParams);
// implement several function with respect to the index of string // implement several function with respect to the index of string
void trim(char *s) { void trim(char *s) {
//fb trim in place //fb trim in place
@@ -475,6 +472,14 @@ private:
std::vector<char*> m_playlistURL; // m3u8 streamURLs buffer std::vector<char*> m_playlistURL; // m3u8 streamURLs buffer
std::vector<uint32_t> m_hashQueue; std::vector<uint32_t> 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_frameSizeWav = 1600;
const size_t m_frameSizeMP3 = 1600; const size_t m_frameSizeMP3 = 1600;
const size_t m_frameSizeAAC = 1600; const size_t m_frameSizeAAC = 1600;

View File

@@ -42,9 +42,9 @@ size_t AudioBuffer::init() {
} }
} }
} else { // no PSRAM available, use ESP32 Flash Memory" } 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_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) if(!m_buffer)
return 0; 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) m_timeout_ms = timeout_ms;
if(timeout_ms_ssl) m_timeout_ms_ssl = timeout_ms_ssl; if(timeout_ms_ssl) m_timeout_ms_ssl = timeout_ms_ssl;
} }
void Audio::connectTask(void* pvParams) {
ConnectParams* params = static_cast<ConnectParams*>(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){ bool Audio::connecttohost(String host){
return connecttohost(host.c_str()); return connecttohost(host.c_str());
@@ -1827,7 +1843,23 @@ bool Audio::connecttohost(const char* host, const char* user, const char* pwd) {
uint32_t t = millis(); uint32_t t = millis();
if(m_f_Log) AUDIO_INFO("connect to %s on port %d path %s", hostwoext, port, extension); 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); 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){ if(res){
uint32_t dt = millis() - t; uint32_t dt = millis() - t;

View File

@@ -9,10 +9,6 @@
#ifndef _vs1053_ext #ifndef _vs1053_ext
#define _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) #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: protected:
const size_t m_buffSizePSRAM = 300000; // most webstreams limit the advance to 100...300Kbytes 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 * 10;
const size_t m_buffSizeRAM = 1600 * AUDIOBUFFER_MULTIPLIER2; const size_t m_buffSizeRAM = 1600;
size_t m_buffSize = 0; size_t m_buffSize = 0;
size_t m_freeSpace = 0; size_t m_freeSpace = 0;
size_t m_writeSpace = 0; size_t m_writeSpace = 0;
@@ -136,6 +132,15 @@ private:
std::vector<char*> m_playlistURL; // m3u8 streamURLs buffer std::vector<char*> m_playlistURL; // m3u8 streamURLs buffer
std::vector<uint32_t> m_hashQueue; std::vector<uint32_t> 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: private:
enum : int { AUDIO_NONE, HTTP_RESPONSE_HEADER , AUDIO_DATA, AUDIO_LOCALFILE, AUDIO_METADATA, AUDIO_PLAYLISTINIT, enum : int { AUDIO_NONE, HTTP_RESPONSE_HEADER , AUDIO_DATA, AUDIO_LOCALFILE, AUDIO_METADATA, AUDIO_PLAYLISTINIT,
AUDIO_PLAYLISTHEADER, AUDIO_PLAYLISTDATA, VS1053_SWM, VS1053_OGG}; AUDIO_PLAYLISTHEADER, AUDIO_PLAYLISTDATA, VS1053_SWM, VS1053_OGG};

View File

@@ -61,7 +61,7 @@ void audio_showstation(const char *info) {
void audio_showstreamtitle(const char *info) { void audio_showstreamtitle(const char *info) {
DBGH(); 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); bool p = printable(info) && (strlen(info) > 0);
#ifdef DEBUG_TITLES #ifdef DEBUG_TITLES
config.setTitle(DEBUG_TITLES); config.setTitle(DEBUG_TITLES);
@@ -73,7 +73,7 @@ void audio_showstreamtitle(const char *info) {
void audio_error(const char *info) { void audio_error(const char *info) {
//config.setTitle(info); //config.setTitle(info);
player.setError(info); player.setError(info);
telnet.printf("##ERROR#:\t%s\n", info); //telnet.printf("##ERROR#:\t%s\n", info);
} }
void audio_id3artist(const char *info){ void audio_id3artist(const char *info){
@@ -88,11 +88,11 @@ void audio_id3album(const char *info){
if(strlen(config.station.title)==0){ if(strlen(config.station.title)==0){
config.setTitle(info); config.setTitle(info);
}else{ }else{
char out[BUFLEN]= {0}; size_t tbs = sizeof(config.tmpBuf);
strlcat(out, config.station.title, BUFLEN); strlcat(config.tmpBuf, config.station.title, tbs);
strlcat(out, " - ", BUFLEN); strlcat(config.tmpBuf, " - ", tbs);
strlcat(out, info, BUFLEN); strlcat(config.tmpBuf, info, tbs);
config.setTitle(out); config.setTitle(config.tmpBuf);
} }
} }
} }

View File

@@ -5,6 +5,7 @@
#include "config.h" #include "config.h"
#include "controls.h" #include "controls.h"
#include "options.h" #include "options.h"
#include "telnet.h"
CommandHandler cmd; 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<bool>(atoi(value))); return true; } if (strEquals(command, "screensaverplayingenabled")){ config.setScreensaverPlayingEnabled(static_cast<bool>(atoi(value))); return true; }
if (strEquals(command, "screensaverplayingtimeout")){ config.setScreensaverPlayingTimeout(static_cast<uint16_t>(atoi(value))); return true; } if (strEquals(command, "screensaverplayingtimeout")){ config.setScreensaverPlayingTimeout(static_cast<uint16_t>(atoi(value))); return true; }
if (strEquals(command, "screensaverplayingblank")) { config.setScreensaverPlayingBlank(static_cast<bool>(atoi(value))); return true; } if (strEquals(command, "screensaverplayingblank")) { config.setScreensaverPlayingBlank(static_cast<bool>(atoi(value))); return true; }
if (strEquals(command, "abuff")){ config.saveValue(&config.store.abuff, static_cast<uint16_t>(atoi(value))); return true; }
if (strEquals(command, "telnet")){ config.saveValue(&config.store.telnet, static_cast<bool>(atoi(value))); telnet.toggle(); return true; }
if (strEquals(command, "watchdog")){ config.saveValue(&config.store.watchdog, static_cast<bool>(atoi(value))); return true; }
if (strEquals(command, "tzh")) { config.saveValue(&config.store.tzHour, static_cast<int8_t>(atoi(value))); return true; } if (strEquals(command, "tzh")) { config.saveValue(&config.store.tzHour, static_cast<int8_t>(atoi(value))); return true; }
if (strEquals(command, "tzm")) { config.saveValue(&config.store.tzMin, static_cast<int8_t>(atoi(value))); return true; } if (strEquals(command, "tzm")) { config.saveValue(&config.store.tzMin, static_cast<int8_t>(atoi(value))); return true; }
if (strEquals(command, "sntp2")) { config.saveValue(config.store.sntp2, value, 35, false); 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, "sntp1")) { config.setSntpOne(value); return true; }
if (strEquals(command, "timeint")) { config.saveValue(&config.store.timeSyncInterval, static_cast<uint16_t>(atoi(value))); return true; }
if (strEquals(command, "timeintrtc")) { config.saveValue(&config.store.timeSyncIntervalRTC, static_cast<uint16_t>(atoi(value))); return true; }
if (strEquals(command, "volsteps")) { config.saveValue(&config.store.volsteps, static_cast<uint8_t>(atoi(value))); return true; } if (strEquals(command, "volsteps")) { config.saveValue(&config.store.volsteps, static_cast<uint8_t>(atoi(value))); return true; }
if (strEquals(command, "encacc")) { setEncAcceleration(static_cast<uint16_t>(atoi(value))); return true; } if (strEquals(command, "encacc")) { setEncAcceleration(static_cast<uint16_t>(atoi(value))); return true; }
@@ -79,14 +85,16 @@ 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, "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, "lon")) { config.saveValue(config.store.weatherlon, value, 10, false); return true; }
if (strEquals(command, "key")) { config.setWeatherKey(value); return true; } if (strEquals(command, "key")) { config.setWeatherKey(value); return true; }
//<-----TODO if (strEquals(command, "wint")) { config.saveValue(&config.store.weatherSyncInterval, static_cast<uint16_t>(atoi(value))); return true; }
if (strEquals(command, "volume")) { player.setVol(static_cast<uint8_t>(atoi(value))); return true; } if (strEquals(command, "volume")) { player.setVol(static_cast<uint8_t>(atoi(value))); return true; }
if (strEquals(command, "sdpos")) { config.setSDpos(static_cast<uint32_t>(atoi(value))); return true; } if (strEquals(command, "sdpos")) { config.setSDpos(static_cast<uint32_t>(atoi(value))); return true; }
if (strEquals(command, "snuffle")) { config.setSnuffle(strcmp(value, "true") == 0); return true; } if (strEquals(command, "snuffle")) { config.setSnuffle(strcmp(value, "true") == 0); return true; }
if (strEquals(command, "balance")) { config.setBalance(static_cast<uint8_t>(atoi(value))); return true; } if (strEquals(command, "balance")) { config.setBalance(static_cast<uint8_t>(atoi(value))); return true; }
if (strEquals(command, "reboot")) { ESP.restart(); 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, "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 IR_PIN!=255
if (strEquals(command, "irbtn")) { config.setIrBtn(atoi(value)); return true; } if (strEquals(command, "irbtn")) { config.setIrBtn(atoi(value)); return true; }
@@ -101,10 +109,9 @@ bool CommandHandler::exec(const char *command, const char *value, uint8_t cid) {
if (strEquals(command, "softap")) { config.saveValue(&config.store.softapdelay, static_cast<uint8_t>(atoi(value))); return true; } if (strEquals(command, "softap")) { config.saveValue(&config.store.softapdelay, static_cast<uint8_t>(atoi(value))); return true; }
if (strEquals(command, "mdnsname")) { config.saveValue(config.store.mdnsname, value, MDNS_LENGTH); return true; } if (strEquals(command, "mdnsname")) { config.saveValue(config.store.mdnsname, value, MDNS_LENGTH); return true; }
if (strEquals(command, "rebootmdns")){ if (strEquals(command, "rebootmdns")){
char buf[MDNS_LENGTH*2]; if(strlen(config.store.mdnsname)>0) snprintf(config.tmpBuf, sizeof(config.tmpBuf), "{\"redirect\": \"http://%s.local/settings.html\"}", config.store.mdnsname);
if(strlen(config.store.mdnsname)>0) snprintf(buf, MDNS_LENGTH*2, "{\"redirect\": \"http://%s.local\"}", config.store.mdnsname); else snprintf(config.tmpBuf, sizeof(config.tmpBuf), "{\"redirect\": \"http://%s/settings.html\"}", config.ipToStr(WiFi.localIP()));
else snprintf(buf, MDNS_LENGTH*2, "{\"redirect\": \"http://%s/\"}", WiFi.localIP().toString().c_str()); websocket.text(cid, config.tmpBuf); delay(500); ESP.restart();
websocket.text(cid, buf); delay(500); ESP.restart();
return true; return true;
} }

View File

@@ -6,6 +6,7 @@
#include "network.h" #include "network.h"
#include "netserver.h" #include "netserver.h"
#include "controls.h" #include "controls.h"
#include "timekeeper.h"
#ifdef USE_SD #ifdef USE_SD
#include "sdmanager.h" #include "sdmanager.h"
#endif #endif
@@ -13,6 +14,19 @@
Config config; 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){ void u8fix(char *src){
char last = src[strlen(src)-1]; char last = src[strlen(src)-1];
if ((uint8_t)last >= 0xC2) src[strlen(src)-1]='\0'; if ((uint8_t)last >= 0xC2) src[strlen(src)-1]='\0';
@@ -46,7 +60,8 @@ void Config::init() {
screensaverPlayingTicks = 0; screensaverPlayingTicks = 0;
newConfigMode = 0; newConfigMode = 0;
isScreensaver = false; isScreensaver = false;
bootInfo(); memset(tmpBuf, 0, BUFLEN);
//bootInfo();
#if RTCSUPPORTED #if RTCSUPPORTED
_rtcFound = false; _rtcFound = false;
BOOTLOG("RTC begin(SDA=%d,SCL=%d)", RTC_SDA, RTC_SCL); BOOTLOG("RTC begin(SDA=%d,SCL=%d)", RTC_SDA, RTC_SCL);
@@ -69,7 +84,7 @@ void Config::init() {
#endif #endif
#endif #endif
eepromRead(EEPROM_START, store); eepromRead(EEPROM_START, store);
bootInfo(); // https://github.com/e2002/yoradio/pull/149
if (store.config_set != 4262) { if (store.config_set != 4262) {
setDefaults(); setDefaults();
} }
@@ -93,6 +108,7 @@ void Config::init() {
_SDplaylistFS = &SPIFFS; _SDplaylistFS = &SPIFFS;
#endif #endif
_bootDone=false; _bootDone=false;
setTimeConf();
} }
void Config::_setupVersion(){ void Config::_setupVersion(){
@@ -103,9 +119,8 @@ void Config::_setupVersion(){
saveValue(&store.screensaverTimeout, (uint16_t)20); saveValue(&store.screensaverTimeout, (uint16_t)20);
break; break;
case 2: case 2:
char buf[MDNS_LENGTH]; snprintf(tmpBuf, MDNS_LENGTH, "yoradio-%x", (unsigned int)getChipId());
snprintf(buf, MDNS_LENGTH, "yoradio-%x", getChipId()); saveValue(store.mdnsname, tmpBuf, MDNS_LENGTH);
saveValue(store.mdnsname, buf, MDNS_LENGTH);
saveValue(&store.skipPlaylistUpDown, false); saveValue(&store.skipPlaylistUpDown, false);
break; break;
case 3: case 3:
@@ -114,6 +129,13 @@ void Config::_setupVersion(){
saveValue(&store.screensaverPlayingTimeout, (uint16_t)5); saveValue(&store.screensaverPlayingTimeout, (uint16_t)5);
saveValue(&store.screensaverPlayingBlank, false); saveValue(&store.screensaverPlayingBlank, false);
break; 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: default:
break; break;
} }
@@ -200,6 +222,44 @@ bool Config::spiffsCleanup(){
return ret; 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(){ void Config::initPlaylistMode(){
uint16_t _lastStation = 0; uint16_t _lastStation = 0;
uint16_t cs = playlistLength(); uint16_t cs = playlistLength();
@@ -369,19 +429,17 @@ void Config::setSntpOne(const char *val){
tzdone = true; tzdone = true;
} }
if (tzdone) { if (tzdone) {
network.forceTimeSync = true; timekeeper.forceTimeSync = true;
saveValue(config.store.sntp1, val, 35); saveValue(config.store.sntp1, val, 35);
} }
} }
void Config::setShowweather(bool val){ void Config::setShowweather(bool val){
config.saveValue(&config.store.showweather, val); config.saveValue(&config.store.showweather, val);
network.trueWeather=false; timekeeper.forceWeather = true;
network.forceWeather = true;
display.putRequest(SHOWWEATHER); display.putRequest(SHOWWEATHER);
} }
void Config::setWeatherKey(const char *val){ void Config::setWeatherKey(const char *val){
saveValue(store.weatherkey, val, WEATHERKEY_LENGTH); saveValue(store.weatherkey, val, WEATHERKEY_LENGTH);
network.trueWeather=false;
display.putRequest(NEWMODE, CLEAR); display.putRequest(NEWMODE, CLEAR);
display.putRequest(NEWMODE, PLAYER); display.putRequest(NEWMODE, PLAYER);
} }
@@ -411,7 +469,10 @@ void Config::resetSystem(const char *val, uint8_t clientId){
saveValue(&store.audioinfo, false, false); saveValue(&store.audioinfo, false, false);
saveValue(&store.vumeter, false, false); saveValue(&store.vumeter, false, false);
saveValue(&store.softapdelay, (uint8_t)0, 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); saveValue(store.mdnsname, store.mdnsname, MDNS_LENGTH, true, true);
display.putRequest(NEWMODE, CLEAR); display.putRequest(NEWMODE, PLAYER); display.putRequest(NEWMODE, CLEAR); display.putRequest(NEWMODE, PLAYER);
netserver.requestOnChange(GETSYSTEM, clientId); 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.tzMin, (int8_t)0, false);
saveValue(store.sntp1, "pool.ntp.org", 35, false); saveValue(store.sntp1, "pool.ntp.org", 35, false);
saveValue(store.sntp2, "0.ru.pool.ntp.org", 35); 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); configTime(store.tzHour * 3600 + store.tzMin * 60, getTimezoneOffset(), store.sntp1, store.sntp2);
network.forceTimeSync = true; timekeeper.forceTimeSync = true;
netserver.requestOnChange(GETTIMEZONE, clientId); netserver.requestOnChange(GETTIMEZONE, clientId);
return; return;
} }
@@ -453,7 +516,8 @@ void Config::resetSystem(const char *val, uint8_t clientId){
saveValue(store.weatherlat, "55.7512", 10, false); saveValue(store.weatherlat, "55.7512", 10, false);
saveValue(store.weatherlon, "37.6184", 10, false); saveValue(store.weatherlon, "37.6184", 10, false);
saveValue(store.weatherkey, "", WEATHERKEY_LENGTH); 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); display.putRequest(NEWMODE, CLEAR); display.putRequest(NEWMODE, PLAYER);
netserver.requestOnChange(GETWEATHER, clientId); netserver.requestOnChange(GETWEATHER, clientId);
return; return;
@@ -530,11 +594,17 @@ void Config::setDefaults() {
store.screensaverEnabled = false; store.screensaverEnabled = false;
store.screensaverTimeout = 20; store.screensaverTimeout = 20;
store.screensaverBlank = false; 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.skipPlaylistUpDown = false;
store.screensaverPlayingEnabled = false; store.screensaverPlayingEnabled = false;
store.screensaverPlayingTimeout = 5; store.screensaverPlayingTimeout = 5;
store.screensaverPlayingBlank = false; 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); eepromWrite(EEPROM_START, store);
} }
@@ -627,12 +697,11 @@ void Config::indexPlaylist() {
if (!playlist) { if (!playlist) {
return; return;
} }
char sName[BUFLEN], sUrl[BUFLEN];
int sOvol; int sOvol;
File index = SPIFFS.open(INDEX_PATH, "w"); File index = SPIFFS.open(INDEX_PATH, "w");
while (playlist.available()) { while (playlist.available()) {
uint32_t pos = playlist.position(); 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); index.write((uint8_t *) &pos, 4);
} }
} }
@@ -661,7 +730,6 @@ uint16_t Config::playlistLength(){
return out; return out;
} }
bool Config::loadStation(uint16_t ls) { bool Config::loadStation(uint16_t ls) {
char sName[BUFLEN], sUrl[BUFLEN];
int sOvol; int sOvol;
uint16_t cs = playlistLength(); uint16_t cs = playlistLength();
if (cs == 0) { if (cs == 0) {
@@ -681,11 +749,11 @@ bool Config::loadStation(uint16_t ls) {
index.readBytes((char *) &pos, 4); index.readBytes((char *) &pos, 4);
index.close(); index.close();
playlist.seek(pos, SeekSet); 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.url, 0, BUFLEN);
memset(station.name, 0, BUFLEN); memset(station.name, 0, BUFLEN);
strncpy(station.name, sName, BUFLEN); strncpy(station.name, tmpBuf, BUFLEN);
strncpy(station.url, sUrl, BUFLEN); strncpy(station.url, tmpBuf2, BUFLEN);
station.ovol = sOvol; station.ovol = sOvol;
setLastStation(ls); setLastStation(ls);
} }
@@ -698,11 +766,11 @@ char * Config::stationByNum(uint16_t num){
File index = SDPLFS()->open(REAL_INDEX, "r"); File index = SDPLFS()->open(REAL_INDEX, "r");
index.seek((num - 1) * 4, SeekSet); index.seek((num - 1) * 4, SeekSet);
uint32_t pos; uint32_t pos;
memset(_stationBuf, 0, BUFLEN/2); memset(_stationBuf, 0, sizeof(_stationBuf));
index.readBytes((char *) &pos, 4); index.readBytes((char *) &pos, 4);
index.close(); index.close();
playlist.seek(pos, SeekSet); playlist.seek(pos, SeekSet);
strncpy(_stationBuf, playlist.readStringUntil('\t').c_str(), BUFLEN/2); strncpy(_stationBuf, playlist.readStringUntil('\t').c_str(), sizeof(_stationBuf));
playlist.close(); playlist.close();
return _stationBuf; return _stationBuf;
} }
@@ -880,6 +948,14 @@ bool Config::saveWifi() {
return true; 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() { bool Config::initNetwork() {
File file = SPIFFS.open(SSIDS_PATH, "r"); File file = SPIFFS.open(SSIDS_PATH, "r");
if (!file || file.isDirectory()) { if (!file || file.isDirectory()) {
@@ -973,7 +1049,7 @@ void Config::doSleepW(){
void Config::sleepForAfter(uint16_t sf, uint16_t sa){ void Config::sleepForAfter(uint16_t sf, uint16_t sa){
sleepfor = sf; sleepfor = sf;
if(sa > 0) _sleepTimer.attach(sa * 60, doSleep); if(sa > 0) timekeeper.waitAndDo(sa * 60, doSleep);
else doSleep(); else doSleep();
} }

View File

@@ -1,7 +1,6 @@
#ifndef config_h #ifndef config_h
#define config_h #define config_h
#include "Arduino.h" #include "Arduino.h"
#include <Ticker.h>
#include <SPI.h> #include <SPI.h>
#include <SPIFFS.h> #include <SPIFFS.h>
#include <EEPROM.h> #include <EEPROM.h>
@@ -47,13 +46,23 @@
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) #if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
#define ESP_ARDUINO_3 1 #define ESP_ARDUINO_3 1
#endif #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 playMode_e : uint8_t { PM_WEB=0, PM_SDCARD=1 };
enum BitrateFormat { BF_UNCNOWN, BF_MP3, BF_AAC, BF_FLAC, BF_OGG, BF_WAV }; enum BitrateFormat { BF_UNCNOWN, BF_MP3, BF_AAC, BF_FLAC, BF_OGG, BF_WAV };
void u8fix(char *src); void u8fix(char *src);
void checkAllTasksStack();
struct theme_t { struct theme_t {
uint16_t background; uint16_t background;
uint16_t meta; uint16_t meta;
@@ -143,6 +152,12 @@ struct config_t
bool screensaverPlayingBlank; bool screensaverPlayingBlank;
char mdnsname[24]; char mdnsname[24];
bool skipPlaylistUpDown; bool skipPlaylistUpDown;
uint16_t abuff;
bool telnet;
bool watchdog;
uint16_t timeSyncInterval;
uint16_t timeSyncIntervalRTC;
uint16_t weatherSyncInterval;
}; };
#if IR_PIN!=255 #if IR_PIN!=255
@@ -189,6 +204,10 @@ class Config {
uint16_t screensaverPlayingTicks; uint16_t screensaverPlayingTicks;
bool isScreensaver; bool isScreensaver;
int newConfigMode; int newConfigMode;
char tmpBuf[BUFLEN];
char tmpBuf2[BUFLEN];
char ipBuf[16];
char _stationBuf[BUFLEN/2];
public: public:
Config() {}; Config() {};
//void save(); //void save();
@@ -214,6 +233,7 @@ class Config {
bool loadStation(uint16_t station); bool loadStation(uint16_t station);
bool initNetwork(); bool initNetwork();
bool saveWifi(); bool saveWifi();
void setTimeConf();
bool saveWifiFromNextion(const char* post); bool saveWifiFromNextion(const char* post);
void setSmartStart(uint8_t ss); void setSmartStart(uint8_t ss);
void setBitrateFormat(BitrateFormat fmt) { configFmt = fmt; } void setBitrateFormat(BitrateFormat fmt) { configFmt = fmt; }
@@ -259,8 +279,10 @@ class Config {
void setIrBtn(int val); void setIrBtn(int val);
#endif #endif
void resetSystem(const char *val, uint8_t clientId); void resetSystem(const char *val, uint8_t clientId);
bool spiffsCleanup(); bool spiffsCleanup();
char * ipToStr(IPAddress ip);
bool prepareForPlaying(uint16_t stationId);
void configPostPlaying(uint16_t stationId);
FS* SDPLFS(){ return _SDplaylistFS; } FS* SDPLFS(){ return _SDplaylistFS; }
#if RTCSUPPORTED #if RTCSUPPORTED
bool isRTCFound(){ return _rtcFound; }; bool isRTCFound(){ return _rtcFound; };
@@ -303,7 +325,6 @@ class Config {
#endif #endif
FS* _SDplaylistFS; FS* _SDplaylistFS;
void setDefaults(); void setDefaults();
Ticker _sleepTimer;
static void doSleep(); static void doSleep();
uint16_t color565(uint8_t r, uint8_t g, uint8_t b); uint16_t color565(uint8_t r, uint8_t g, uint8_t b);
void _setupVersion(); void _setupVersion();
@@ -314,7 +335,6 @@ class Config {
uint16_t station = random(1, store.countStation); uint16_t station = random(1, store.countStation);
return station; return station;
} }
char _stationBuf[BUFLEN/2];
}; };
extern Config config; extern Config config;

View File

@@ -56,12 +56,11 @@ constexpr uint8_t nrOfButtons = sizeof(button) / sizeof(button[0]);
#include "../IRremoteESP8266/IRtext.h" #include "../IRremoteESP8266/IRtext.h"
#include "../IRremoteESP8266/IRutils.h" #include "../IRremoteESP8266/IRutils.h"
uint8_t irVolRepeat = 0; uint8_t irVolRepeat = 0;
const uint16_t kCaptureBufferSize = 1024; //const uint16_t kCaptureBufferSize = 1024;
const uint8_t kTimeout = IR_TIMEOUT;
const uint16_t kMinUnknownSize = 12; const uint16_t kMinUnknownSize = 12;
#define LEGACY_TIMING_INFO false #define LEGACY_TIMING_INFO false
IRrecv irrecv(IR_PIN, kCaptureBufferSize, kTimeout, true); IRrecv irrecv(IR_PIN, IR_BUFSIZE, IR_TIMEOUT, true);
decode_results irResults; decode_results irResults;
#endif #endif

View File

@@ -5,13 +5,48 @@
#include "display.h" #include "display.h"
#include "player.h" #include "player.h"
#include "network.h" #include "network.h"
#include "netserver.h"
#include "timekeeper.h"
Display display; Display display;
#ifdef USE_NEXTION #ifdef USE_NEXTION
Nextion nextion; Nextion nextion;
#endif #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 #ifndef DUMMYDISPLAY
//============================================================================================================================ //============================================================================================================================
DspCore dsp; DspCore dsp;
@@ -19,40 +54,27 @@ DspCore dsp;
Page *pages[] = { new Page(), new Page(), new Page(), new Page() }; Page *pages[] = { new Page(), new Page(), new Page(), new Page() };
#ifndef DSQ_SEND_DELAY #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 #endif
#ifndef CORE_STACK_SIZE
#define CORE_STACK_SIZE 1024*3
#endif
#ifndef DSP_TASK_DELAY #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 #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) #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 #undef BITRATE_FULL
#define BITRATE_FULL false #define BITRATE_FULL false
#endif #endif
TaskHandle_t DspTask;
QueueHandle_t displayQueue;
void returnPlayer(){ void returnPlayer(){
display.putRequest(NEWMODE, PLAYER); 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() { void Display::init() {
Serial.print("##[BOOT]#\tdisplay.init\t"); Serial.print("##[BOOT]#\tdisplay.init\t");
#ifdef USE_NEXTION #ifdef USE_NEXTION
@@ -117,7 +139,7 @@ void Display::_buildPager(){
_volbar = new SliderWidget(volbarConf, config.theme.volbarin, config.theme.background, 254, config.theme.volbarout); _volbar = new SliderWidget(volbarConf, config.theme.volbarin, config.theme.background, 254, config.theme.volbarout);
#endif #endif
#ifndef HIDE_HEAPBAR #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 #endif
#ifndef HIDE_VOL #ifndef HIDE_VOL
_voltxt = new TextWidget(voltxtConf, 10, false, config.theme.vol, config.theme.background); _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)); TextWidget *appass2 = (TextWidget*) &_boot->addWidget(new TextWidget(apPass2Conf, 30, false, config.theme.clock, config.theme.background));
appass2->setText(apPassword); appass2->setText(apPassword);
ScrollWidget *bootSett = (ScrollWidget*) &_boot->addWidget(new ScrollWidget("*", apSettConf, config.theme.title2, config.theme.background)); 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.addPage(_boot);
_pager.setPage(_boot); _pager.setPage(_boot);
#else #else
@@ -234,7 +256,7 @@ void Display::_start() {
if(_vuwidget) _vuwidget->lock(); if(_vuwidget) _vuwidget->lock();
if(_rssi) _setRSSI(WiFi.RSSI()); if(_rssi) _setRSSI(WiFi.RSSI());
#ifndef HIDE_IP #ifndef HIDE_IP
if(_volip) _volip->setText(WiFi.localIP().toString().c_str(), iptxtFmt); if(_volip) _volip->setText(config.ipToStr(WiFi.localIP()), iptxtFmt);
#endif #endif
_pager.setPage( pages[PG_PLAYER]); _pager.setPage( pages[PG_PLAYER]);
_volume(); _volume();
@@ -254,11 +276,6 @@ void Display::_showDialog(const char *title){
_meta.setText(title); _meta.setText(title);
} }
void Display::_setReturnTicker(uint8_t time_s){
_returnTicker.detach();
_returnTicker.once(time_s, returnPlayer);
}
void Display::_swichMode(displayMode_e newmode) { void Display::_swichMode(displayMode_e newmode) {
#ifdef USE_NEXTION #ifdef USE_NEXTION
//nextion.swichMode(newmode); //nextion.swichMode(newmode);
@@ -276,7 +293,6 @@ void Display::_swichMode(displayMode_e newmode) {
dsp.clearDsp(); dsp.clearDsp();
#endif #endif
numOfNextStation = 0; numOfNextStation = 0;
_returnTicker.detach();
#ifdef META_MOVE #ifdef META_MOVE
_meta.moveBack(); _meta.moveBack();
#endif #endif
@@ -304,7 +320,7 @@ void Display::_swichMode(displayMode_e newmode) {
#ifndef HIDE_IP #ifndef HIDE_IP
_showDialog(const_DlgVolume); _showDialog(const_DlgVolume);
#else #else
_showDialog(WiFi.localIP().toString().c_str()); _showDialog(config.ipToStr(WiFi.localIP()));
#endif #endif
_nums.setText(config.store.volume, numtxtFmt); _nums.setText(config.store.volume, numtxtFmt);
} }
@@ -329,11 +345,11 @@ void Display::resetQueue(){
void Display::_drawPlaylist() { void Display::_drawPlaylist() {
dsp.drawPlaylist(currentPlItem); dsp.drawPlaylist(currentPlItem);
_setReturnTicker(30); timekeeper.waitAndReturnPlayer(30);
} }
void Display::_drawNextStationNum(uint16_t num) { void Display::_drawNextStationNum(uint16_t num) {
_setReturnTicker(30); timekeeper.waitAndReturnPlayer(30);
_meta.setText(config.stationByNum(num)); _meta.setText(config.stationByNum(num));
_nums.setText(num, "%d"); _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() { void Display::loop() {
if(_bootStep==0) { if(_bootStep==0) {
_pager.begin(); _pager.begin();
@@ -429,7 +443,7 @@ void Display::loop() {
if(_weather) _weather->lock(!config.store.showweather); if(_weather) _weather->lock(!config.store.showweather);
if(!config.store.showweather){ if(!config.store.showweather){
#ifndef HIDE_IP #ifndef HIDE_IP
if(_volip) _volip->setText(WiFi.localIP().toString().c_str(), iptxtFmt); if(_volip) _volip->setText(config.ipToStr(WiFi.localIP()), iptxtFmt);
#endif #endif
}else{ }else{
if(_weather) _weather->setText(const_getWeather); if(_weather) _weather->setText(const_getWeather);
@@ -463,13 +477,19 @@ void Display::loop() {
case DSP_START: _start(); break; case DSP_START: _start(); break;
case NEWIP: { case NEWIP: {
#ifndef HIDE_IP #ifndef HIDE_IP
if(_volip) _volip->setText(WiFi.localIP().toString().c_str(), iptxtFmt); if(_volip) _volip->setText(config.ipToStr(WiFi.localIP()), iptxtFmt);
#endif #endif
break; break;
} }
default: 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(); dsp.loop();
#if I2S_DOUT==255 #if I2S_DOUT==255
player.computeVUlevel(); player.computeVUlevel();
@@ -561,7 +581,7 @@ void Display::_volume() {
if(_voltxt) _voltxt->setText(config.store.volume, voltxtFmt); if(_voltxt) _voltxt->setText(config.store.volume, voltxtFmt);
#endif #endif
if(_mode==VOL) { if(_mode==VOL) {
_setReturnTicker(3); timekeeper.waitAndReturnPlayer(3);
_nums.setText(config.store.volume, numtxtFmt); _nums.setText(config.store.volume, numtxtFmt);
} }
/*#ifdef USE_NEXTION /*#ifdef USE_NEXTION

View File

@@ -3,20 +3,18 @@
#include "options.h" #include "options.h"
#include "Arduino.h" #include "Arduino.h"
#include <Ticker.h>
#include "config.h" #include "config.h"
#include "common.h" #include "common.h"
#include "../displays/dspcore.h" #include "../displays/dspcore.h"
#if NEXTION_RX!=255 && NEXTION_TX!=255 #if NEXTION_RX!=255 && NEXTION_TX!=255
#define USE_NEXTION #define USE_NEXTION
#include "../displays/nextion.h" #include "../displays/nextion.h"
#endif #endif
//static void loopDspTask(void * pvParameters);
#ifndef DUMMYDISPLAY #ifndef DUMMYDISPLAY
void loopDspTask(void * pvParameters);
class Display { class Display {
public: public:
@@ -54,7 +52,6 @@ class Display {
ClockWidget _clock; ClockWidget _clock;
Page *_boot; Page *_boot;
TextWidget *_bootstring, *_volip, *_voltxt, *_rssi, *_bitrate; TextWidget *_bootstring, *_volip, *_voltxt, *_rssi, *_bitrate;
Ticker _returnTicker;
uint8_t _bootStep; uint8_t _bootStep;
void _time(bool redraw = false); void _time(bool redraw = false);
void _apScreen(); void _apScreen();
@@ -68,7 +65,6 @@ class Display {
void _showDialog(const char *title); void _showDialog(const char *title);
void _buildPager(); void _buildPager();
void _bootScreen(); void _bootScreen();
void _setReturnTicker(uint8_t time_s);
void _layoutChange(bool played); void _layoutChange(bool played);
void _setRSSI(int rssi); void _setRSSI(int rssi);
}; };
@@ -98,6 +94,8 @@ class Display {
bool deepsleep(){return true;} bool deepsleep(){return true;}
void wakeup(){} void wakeup(){}
void printPLitem(uint8_t pos, const char* item){} void printPLitem(uint8_t pos, const char* item){}
private:
void _createDspTask();
}; };
#endif #endif

View File

@@ -9,7 +9,7 @@
AsyncMqttClient mqttClient; AsyncMqttClient mqttClient;
TimerHandle_t mqttReconnectTimer; TimerHandle_t mqttReconnectTimer;
char topic[140], status[BUFLEN*3], vol[5], buf[20]; char topic[100], status[BUFLEN+50];
void connectToMqtt() { void connectToMqtt() {
mqttClient.connect(); mqttClient.connect();
@@ -25,8 +25,10 @@ void mqttInit() {
connectToMqtt(); connectToMqtt();
} }
void zeroBuffer(){ memset(topic, 0, sizeof(topic)); memset(status, 0, sizeof(status)); }
void onMqttConnect(bool sessionPresent) { void onMqttConnect(bool sessionPresent) {
memset(topic, 0, 140); zeroBuffer();
sprintf(topic, "%s%s", MQTT_ROOT_TOPIC, "command"); sprintf(topic, "%s%s", MQTT_ROOT_TOPIC, "command");
mqttClient.subscribe(topic, 2); mqttClient.subscribe(topic, 2);
mqttPublishStatus(); mqttPublishStatus();
@@ -36,13 +38,12 @@ void onMqttConnect(bool sessionPresent) {
void mqttPublishStatus() { void mqttPublishStatus() {
if(mqttClient.connected()){ if(mqttClient.connected()){
memset(topic, 0, 140); zeroBuffer();
memset(status, 0, BUFLEN*3);
sprintf(topic, "%s%s", MQTT_ROOT_TOPIC, "status"); sprintf(topic, "%s%s", MQTT_ROOT_TOPIC, "status");
char name[BUFLEN*2]; char name[BUFLEN/2];
char title[BUFLEN*2]; char title[BUFLEN/2];
config.escapeQuotes(config.station.name, name, sizeof(name)); config.escapeQuotes(config.station.name, name, sizeof(name)-10);
config.escapeQuotes(config.station.title, title, sizeof(name)); 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); 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); mqttClient.publish(topic, 0, true, status);
} }
@@ -50,17 +51,17 @@ void mqttPublishStatus() {
void mqttPublishPlaylist() { void mqttPublishPlaylist() {
if(mqttClient.connected()){ if(mqttClient.connected()){
memset(topic, 0, 140); zeroBuffer();
memset(status, 0, BUFLEN*3);
sprintf(topic, "%s%s", MQTT_ROOT_TOPIC, "playlist"); 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); mqttClient.publish(topic, 0, true, status);
} }
} }
void mqttPublishVolume(){ void mqttPublishVolume(){
if(mqttClient.connected()){ if(mqttClient.connected()){
memset(topic, 0, 140); zeroBuffer();
char vol[5];
memset(vol, 0, 5); memset(vol, 0, 5);
sprintf(topic, "%s%s", MQTT_ROOT_TOPIC, "volume"); sprintf(topic, "%s%s", MQTT_ROOT_TOPIC, "volume");
sprintf(vol, "%d", config.store.volume); sprintf(vol, "%d", config.store.volume);
@@ -76,33 +77,16 @@ void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) { void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
if (len == 0) return; if (len == 0) return;
memset(buf, 0, 20); if(len<20){
strlcpy(buf, payload, len+1); char buf[len+1];
if (strcmp(buf, "prev") == 0) { strncpy(buf, payload, len);
player.prev(); buf[len]='\0';
return; 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, "next") == 0) { if (strcmp(buf, "toggle") == 0) { player.sendCommand({PR_TOGGLE, 0}); return; }
player.next(); if (strcmp(buf, "stop") == 0) { player.sendCommand({PR_STOP, 0}); return; }
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, "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) { if (strcmp(buf, "volm") == 0) {
player.stepVol(false); player.stepVol(false);
return; return;
@@ -115,7 +99,6 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties
uint8_t sst = config.store.smartstart; uint8_t sst = config.store.smartstart;
config.setDspOn(0); config.setDspOn(0);
player.sendCommand({PR_STOP, 0}); player.sendCommand({PR_STOP, 0});
//telnet.info();
delay(100); delay(100);
config.saveValue(&config.store.smartstart, sst); config.saveValue(&config.store.smartstart, sst);
return; return;
@@ -140,11 +123,18 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties
player.sendCommand({PR_PLAY, (uint16_t)sb}); player.sendCommand({PR_PLAY, (uint16_t)sb});
return; return;
} }
if (strstr(buf, "http")==buf){ }else{
if(len>MQTT_BURL_SIZE) return;
strncpy(player.burl, payload, len);
player.burl[len]='\0';
player.sendCommand({PR_BURL, 0});
return;
}
/*if (strstr(buf, "http")==0){
if(len+1>sizeof(player.burl)) return; if(len+1>sizeof(player.burl)) return;
strlcpy(player.burl, payload, len+1); strlcpy(player.burl, payload, len+1);
return; return;
} }*/
} }
#endif // #ifdef MQTT_ROOT_TOPIC #endif // #ifdef MQTT_ROOT_TOPIC

View File

@@ -1,11 +1,9 @@
#ifndef mqtt_h #ifndef mqtt_h
#define mqtt_h #define mqtt_h
#include "options.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 mqttInit();
void connectToMqtt(); void connectToMqtt();
@@ -15,6 +13,7 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties
void mqttPublishStatus(); void mqttPublishStatus();
void mqttPublishPlaylist(); void mqttPublishPlaylist();
void mqttPublishVolume(); void mqttPublishVolume();
void zeroBuffer();
#endif // #ifdef MQTT_ROOT_TOPIC #endif // #ifdef MQTT_ROOT_TOPIC

View File

@@ -1,7 +1,6 @@
#include "netserver.h" #include "netserver.h"
#include <SPIFFS.h> #include <SPIFFS.h>
#include "config.h"
#include "player.h" #include "player.h"
#include "telnet.h" #include "telnet.h"
#include "display.h" #include "display.h"
@@ -10,17 +9,10 @@
#include "mqtt.h" #include "mqtt.h"
#include "controls.h" #include "controls.h"
#include "commandhandler.h" #include "commandhandler.h"
#include "timekeeper.h"
#include <Update.h> #include <Update.h>
#include <ESPmDNS.h> #include <ESPmDNS.h>
//#include <Ticker.h>
#if USE_OTA
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
#include <NetworkUdp.h>
#else
#include <WiFiUdp.h>
#endif
#include <ArduinoOTA.h>
#endif
#ifdef USE_SD #ifdef USE_SD
#include "sdmanager.h" #include "sdmanager.h"
@@ -29,7 +21,7 @@
#define MIN_MALLOC 24112 #define MIN_MALLOC 24112
#endif #endif
#ifndef NSQ_SEND_DELAY #ifndef NSQ_SEND_DELAY
#define NSQ_SEND_DELAY (TickType_t)100 //portMAX_DELAY? #define NSQ_SEND_DELAY pdMS_TO_TICKS(100) //portMAX_DELAY?
#endif #endif
//#define CORS_DEBUG //Enable CORS policy: 'Access-Control-Allow-Origin' (for testing) //#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; bool shouldReboot = false;
#ifdef MQTT_ROOT_TOPIC #ifdef MQTT_ROOT_TOPIC
Ticker mqttplaylistticker; //Ticker mqttplaylistticker;
bool mqttplaylistblock = false; bool mqttplaylistblock = false;
void mqttplaylistSend() { void mqttplaylistSend() {
mqttplaylistblock = true; mqttplaylistblock = true;
mqttplaylistticker.detach(); // mqttplaylistticker.detach();
mqttPublishPlaylist(); mqttPublishPlaylist();
mqttplaylistblock = false; mqttplaylistblock = false;
} }
#endif #endif
char* updateError() { char* updateError() {
static char ret[140] = {0}; sprintf(netserver.nsBuf, "Update failed with error (%d)<br /> %s", (int)Update.getError(), Update.errorString());
sprintf(ret, "Update failed with error (%d)<br /> %s", (int)Update.getError(), Update.errorString()); return netserver.nsBuf;
return ret;
} }
bool NetServer::begin(bool quiet) { bool NetServer::begin(bool quiet) {
@@ -67,6 +58,7 @@ bool NetServer::begin(bool quiet) {
if(!quiet) Serial.print("##[BOOT]#\tnetserver.begin\t"); if(!quiet) Serial.print("##[BOOT]#\tnetserver.begin\t");
importRequest = IMDONE; importRequest = IMDONE;
irRecordEnable = false; irRecordEnable = false;
playerBufMax = psramInit()?300000:1600 * config.store.abuff;
nsQueue = xQueueCreate( 20, sizeof( nsRequestParams_t ) ); nsQueue = xQueueCreate( 20, sizeof( nsRequestParams_t ) );
while(nsQueue==NULL){;} while(nsQueue==NULL){;}
@@ -82,43 +74,8 @@ bool NetServer::begin(bool quiet) {
webserver.begin(); webserver.begin();
if(strlen(config.store.mdnsname)>0) if(strlen(config.store.mdnsname)>0)
MDNS.begin(config.store.mdnsname); MDNS.begin(config.store.mdnsname);
websocket.onEvent(onWsEvent); websocket.onEvent(onWsEvent);
webserver.addHandler(&websocket); 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"); if(!quiet) Serial.println("done");
return true; return true;
} }
@@ -152,6 +109,7 @@ void NetServer::chunkedHtmlPage(const String& contentType, AsyncWebServerRequest
strlcpy(chunkedPathBuffer, path, sizeof(chunkedPathBuffer)-1); strlcpy(chunkedPathBuffer, path, sizeof(chunkedPathBuffer)-1);
AsyncWebServerResponse *response; AsyncWebServerResponse *response;
response = request->beginChunkedResponse(contentType, chunkedHtmlPageCallback); response = request->beginChunkedResponse(contentType, chunkedHtmlPageCallback);
response->addHeader("Cache-Control","max-age=31536000");
request->send(response); request->send(response);
} }
@@ -181,13 +139,12 @@ const char *getFormat(BitrateFormat _format) {
} }
} }
char wsbuf[BUFLEN * 2];
void NetServer::processQueue(){ void NetServer::processQueue(){
if(nsQueue==NULL) return; if(nsQueue==NULL) return;
nsRequestParams_t request; nsRequestParams_t request;
if(xQueueReceive(nsQueue, &request, NS_QUEUE_TICKS)){ if(xQueueReceive(nsQueue, &request, NS_QUEUE_TICKS)){
memset(wsbuf, 0, BUFLEN * 2);
uint8_t clientId = request.clientId; uint8_t clientId = request.clientId;
wsBuf[0]='\0';
switch (request.type) { switch (request.type) {
case PLAYLIST: getPlaylist(clientId); break; case PLAYLIST: getPlaylist(clientId); break;
case PLAYLISTSAVED: { case PLAYLISTSAVED: {
@@ -205,36 +162,46 @@ void NetServer::processQueue(){
} }
case GETACTIVE: { case GETACTIVE: {
bool dbgact = false, nxtn=false; 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) { if (network.status == CONNECTED) {
act += F("\"group_system\","); //act += F("\"group_system\",");
if (BRIGHTNESS_PIN != 255 || DSP_CAN_FLIPPED || DSP_MODEL == DSP_NOKIA5110 || dbgact) act += F("\"group_display\","); APPEND_GROUP("group_system");
if (BRIGHTNESS_PIN != 255 || DSP_CAN_FLIPPED || DSP_MODEL == DSP_NOKIA5110 || dbgact) APPEND_GROUP("group_display");
#ifdef USE_NEXTION #ifdef USE_NEXTION
act += F("\"group_nextion\","); APPEND_GROUP("group_nextion");
if (!SHOW_WEATHER || dbgact) act += F("\"group_weather\","); if (!SHOW_WEATHER || dbgact) APPEND_GROUP("group_weather");
nxtn=true; nxtn=true;
#endif #endif
#if defined(LCD_I2C) || defined(DSP_OLED) #if defined(LCD_I2C) || defined(DSP_OLED)
act += F("\"group_oled\","); APPEND_GROUP("group_oled");
#endif #endif
#ifndef HIDE_VU #if !defined(HIDE_VU) && !defined(DUMMYDISPLAY)
act += F("\"group_vu\","); 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 #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); size_t len = strlen(nsBuf);
sprintf (wsbuf, "{\"act\":[%s]}", act.c_str()); if (len > 0 && nsBuf[len - 1] == ',') nsBuf[len - 1] = '\0';
snprintf(wsBuf, sizeof(wsBuf), "{\"act\":[%s]}", nsBuf);
break; break;
} }
//case STARTUP: sprintf (wsbuf, "{\"command\":\"startup\", \"payload\": {\"mode\":\"%s\", \"version\":\"%s\"}}", network.status == CONNECTED ? "player" : "ap", YOVERSION); break;
case GETINDEX: { case GETINDEX: {
requestOnChange(STATION, clientId); requestOnChange(STATION, clientId);
requestOnChange(TITLE, clientId); requestOnChange(TITLE, clientId);
@@ -249,15 +216,19 @@ void NetServer::processQueue(){
return; return;
break; 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.smartstart != 2,
config.store.audioinfo, config.store.audioinfo,
config.store.vumeter, config.store.vumeter,
config.store.softapdelay, config.store.softapdelay,
config.vuThreshold, config.vuThreshold,
config.store.mdnsname); config.store.mdnsname,
config.ipToStr(WiFi.localIP()),
config.store.abuff,
config.store.telnet,
config.store.watchdog);
break; 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.flipscreen,
config.store.invertdisplay, config.store.invertdisplay,
config.store.numplaylist, config.store.numplaylist,
@@ -273,52 +244,55 @@ void NetServer::processQueue(){
config.store.screensaverPlayingTimeout, config.store.screensaverPlayingTimeout,
config.store.screensaverPlayingBlank); config.store.screensaverPlayingBlank);
break; 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.tzHour,
config.store.tzMin, config.store.tzMin,
config.store.sntp1, config.store.sntp1,
config.store.sntp2); config.store.sntp2,
config.store.timeSyncInterval,
config.store.timeSyncIntervalRTC);
break; 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.showweather,
config.store.weatherlat, config.store.weatherlat,
config.store.weatherlon, config.store.weatherlon,
config.store.weatherkey); config.store.weatherkey,
config.store.weatherSyncInterval);
break; 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.volsteps,
config.store.encacc, config.store.encacc,
config.store.irtlp, config.store.irtlp,
config.store.skipPlaylistUpDown); config.store.skipPlaylistUpDown);
break; 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 STATION: requestOnChange(STATIONNAME, clientId); requestOnChange(ITEM, clientId); break;
case STATIONNAME: sprintf (wsbuf, "{\"payload\":[{\"id\":\"nameset\", \"value\": \"%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 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 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 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 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\": %d,\"sdend\": %d,\"sdtpos\": %d,\"sdtend\": %d}", case SDPOS: sprintf (wsBuf, "{\"sdpos\": %lu,\"sdend\": %lu,\"sdtpos\": %lu,\"sdtend\": %lu}",
player.getFilePos(), player.getFilePos(),
player.getFileSize(), player.getFileSize(),
player.getAudioCurrentTime(), player.getAudioCurrentTime(),
player.getAudioFileDuration()); player.getAudioFileDuration());
break; break;
case SDLEN: sprintf (wsbuf, "{\"sdmin\": %d,\"sdmax\": %d}", player.sd_min, player.sd_max); 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 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 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 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 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 BALANCE: sprintf (wsBuf, "{\"payload\":[{\"id\": \"balance\", \"value\": %d}]}", config.store.balance); break;
case SDINIT: sprintf (wsbuf, "{\"sdinit\": %d}", SDC_CS!=255); break; case SDINIT: sprintf (wsBuf, "{\"sdinit\": %d}", SDC_CS!=255); break;
case GETPLAYERMODE: sprintf (wsbuf, "{\"playermode\": \"%s\"}", config.getMode()==PM_SDCARD?"modesd":"modeweb"); break; case GETPLAYERMODE: sprintf (wsBuf, "{\"playermode\": \"%s\"}", config.getMode()==PM_SDCARD?"modesd":"modeweb"); break;
#ifdef USE_SD #ifdef USE_SD
case CHANGEMODE: config.changeMode(config.newConfigMode); return; break; case CHANGEMODE: config.changeMode(config.newConfigMode); return; break;
#endif #endif
default: break; default: break;
} }
if (strlen(wsbuf) > 0) { if (strlen(wsBuf) > 0) {
if (clientId == 0) { websocket.textAll(wsbuf); }else{ websocket.text(clientId, wsbuf); } if (clientId == 0) { websocket.textAll(wsBuf); }else{ websocket.text(clientId, wsBuf); }
#ifdef MQTT_ROOT_TOPIC #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 == STATION || request.type == ITEM || request.type == TITLE || request.type == MODE)) mqttPublishStatus();
if (clientId == 0 && request.type == VOLUME) mqttPublishVolume(); if (clientId == 0 && request.type == VOLUME) mqttPublishVolume();
@@ -341,22 +315,19 @@ void NetServer::loop() {
default: break; default: break;
} }
processQueue(); processQueue();
#if USE_OTA
ArduinoOTA.handle();
#endif
} }
#if IR_PIN!=255 #if IR_PIN!=255
void NetServer::irToWs(const char* protocol, uint64_t irvalue) { void NetServer::irToWs(const char* protocol, uint64_t irvalue) {
char buf[BUFLEN] = { 0 }; wsBuf[0]='\0';
sprintf (buf, "{\"ircode\": %llu, \"protocol\": \"%s\"}", irvalue, protocol); sprintf (wsBuf, "{\"ircode\": %llu, \"protocol\": \"%s\"}", irvalue, protocol);
websocket.textAll(buf); websocket.textAll(wsBuf);
} }
void NetServer::irValsToWs() { void NetServer::irValsToWs() {
if (!irRecordEnable) return; if (!irRecordEnable) return;
char buf[BUFLEN] = { 0 }; wsBuf[0]='\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]); 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(buf); websocket.textAll(wsBuf);
} }
#endif #endif
@@ -364,32 +335,36 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client
AwsFrameInfo *info = (AwsFrameInfo*)arg; AwsFrameInfo *info = (AwsFrameInfo*)arg;
if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) { if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
data[len] = 0; data[len] = 0;
char comnd[65], val[65]; if (config.parseWsCommand((const char*)data, _wscmd, _wsval, 65)) {
if (config.parseWsCommand((const char*)data, comnd, val, 65)) { if (strcmp(_wscmd, "ping") == 0) {
if (strcmp(comnd, "trebble") == 0) { websocket.text(clientId, "{\"pong\": 1}");
int8_t valb = atoi(val); return;
}
if (strcmp(_wscmd, "trebble") == 0) {
int8_t valb = atoi(_wsval);
config.setTone(config.store.bass, config.store.middle, valb); config.setTone(config.store.bass, config.store.middle, valb);
return; return;
} }
if (strcmp(comnd, "middle") == 0) { if (strcmp(_wscmd, "middle") == 0) {
int8_t valb = atoi(val); int8_t valb = atoi(_wsval);
config.setTone(config.store.bass, valb, config.store.trebble); config.setTone(config.store.bass, valb, config.store.trebble);
return; return;
} }
if (strcmp(comnd, "bass") == 0) { if (strcmp(_wscmd, "bass") == 0) {
int8_t valb = atoi(val); int8_t valb = atoi(_wsval);
config.setTone(valb, config.store.middle, config.store.trebble); config.setTone(valb, config.store.middle, config.store.trebble);
return; return;
} }
if (strcmp(comnd, "submitplaylistdone") == 0) { if (strcmp(_wscmd, "submitplaylistdone") == 0) {
#ifdef MQTT_ROOT_TOPIC #ifdef MQTT_ROOT_TOPIC
mqttplaylistticker.attach(5, mqttplaylistSend); //mqttplaylistticker.attach(5, mqttplaylistSend);
timekeeper.waitAndDo(5, mqttplaylistSend);
#endif #endif
if (player.isRunning()) player.sendCommand({PR_PLAY, -config.lastStation()}); if (player.isRunning()) player.sendCommand({PR_PLAY, -config.lastStation()});
return; return;
} }
if(cmd.exec(comnd, val, clientId)){ if(cmd.exec(_wscmd, _wsval, clientId)){
return; 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) { void NetServer::getPlaylist(uint8_t clientId) {
char buf[160] = {0}; sprintf(nsBuf, "{\"file\": \"http://%s%s\"}", config.ipToStr(WiFi.localIP()), PLAYLIST_PATH);
sprintf(buf, "{\"file\": \"http://%s%s\"}", WiFi.localIP().toString().c_str(), PLAYLIST_PATH); if (clientId == 0) { websocket.textAll(nsBuf); } else { websocket.text(clientId, nsBuf); }
if (clientId == 0) { websocket.textAll(buf); } else { websocket.text(clientId, buf); }
} }
int NetServer::_readPlaylistLine(File &file, char * line, size_t size){ 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() { bool NetServer::importPlaylist() {
if(config.getMode()==PM_SDCARD) return false; if(config.getMode()==PM_SDCARD) return false;
//player.sendCommand({PR_STOP, 0});
File tempfile = SPIFFS.open(TMP_PATH, "r"); File tempfile = SPIFFS.open(TMP_PATH, "r");
if (!tempfile) { if (!tempfile) {
return false; return false;
} }
char sName[BUFLEN], sUrl[BUFLEN], linePl[BUFLEN*3];; char linePl[BUFLEN*3];
int sOvol; int sOvol;
_readPlaylistLine(tempfile, linePl, sizeof(linePl)-1); _readPlaylistLine(tempfile, linePl, sizeof(linePl)-1);
if (config.parseCSV(linePl, sName, sUrl, sOvol)) { if (config.parseCSV(linePl, nsBuf, nsBuf2, sOvol)) {
tempfile.close(); tempfile.close();
SPIFFS.rename(TMP_PATH, PLAYLIST_PATH); SPIFFS.rename(TMP_PATH, PLAYLIST_PATH);
requestOnChange(PLAYLISTSAVED, 0); requestOnChange(PLAYLISTSAVED, 0);
return true; return true;
} }
if (config.parseJSON(linePl, sName, sUrl, sOvol)) { if (config.parseJSON(linePl, nsBuf, nsBuf2, sOvol)) {
File playlistfile = SPIFFS.open(PLAYLIST_PATH, "w"); 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); playlistfile.println(linePl);
while (tempfile.available()) { while (tempfile.available()) {
_readPlaylistLine(tempfile, linePl, sizeof(linePl)-1); _readPlaylistLine(tempfile, linePl, sizeof(linePl)-1);
if (config.parseJSON(linePl, sName, sUrl, sOvol)) { if (config.parseJSON(linePl, nsBuf, nsBuf2, sOvol)) {
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); playlistfile.println(linePl);
} }
} }
@@ -461,11 +436,12 @@ void NetServer::resetQueue(){
if(nsQueue!=NULL) xQueueReset(nsQueue); if(nsQueue!=NULL) xQueueReset(nsQueue);
} }
int freeSpace;
void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) { 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(request->url()=="/upload"){
if (!index) { if (!index) {
if(filename!="tempwifi.csv"){ if(filename!="tempwifi.csv"){
//player.sendCommand({PR_STOP, 0});
if(SPIFFS.exists(PLAYLIST_PATH)) SPIFFS.remove(PLAYLIST_PATH); if(SPIFFS.exists(PLAYLIST_PATH)) SPIFFS.remove(PLAYLIST_PATH);
if(SPIFFS.exists(INDEX_PATH)) SPIFFS.remove(INDEX_PATH); if(SPIFFS.exists(INDEX_PATH)) SPIFFS.remove(INDEX_PATH);
if(SPIFFS.exists(PLAYLIST_SD_PATH)) SPIFFS.remove(PLAYLIST_SD_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(); freeSpace = (float)SPIFFS.totalBytes()/100*68-SPIFFS.usedBytes();
request->_tempFile = SPIFFS.open(TMP_PATH , "w"); request->_tempFile = SPIFFS.open(TMP_PATH , "w");
}else{
} }
if (len) { if (len) {
if(freeSpace>index+len){ if(freeSpace>index+len){
@@ -481,6 +459,7 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index,
} }
if (final) { if (final) {
request->_tempFile.close(); request->_tempFile.close();
freeSpace = 0;
} }
}else if(request->url()=="/update"){ }else if(request->url()=="/update"){
if (!index) { if (!index) {
@@ -510,6 +489,7 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index,
}else{ // "/webboard" }else{ // "/webboard"
DBGVB("File: %s, size:%u bytes, index: %u, final: %s\n", filename.c_str(), len, index, final?"true":"false"); DBGVB("File: %s, size:%u bytes, index: %u, final: %s\n", filename.c_str(), len, index, final?"true":"false");
if (!index) { if (!index) {
player.sendCommand({PR_STOP, 0});
String spath = "/www/"; String spath = "/www/";
if(filename=="playlist.csv" || filename=="wifi.csv") spath = "/data/"; if(filename=="playlist.csv" || filename=="wifi.csv") spath = "/data/";
request->_tempFile = SPIFFS.open(spath + filename , "w"); 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) { void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
switch (type) { 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_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 #%u disconnected\n", client->id()); 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_DATA: netserver.onWsMessage(arg, data, len, client->id()); break;
case WS_EVT_PONG: case WS_EVT_PONG:
case WS_EVT_ERROR: 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->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_POST && request->url()=="/webboard" && config.emptyFS) { request->redirect("/"); ESP.restart(); return; }
if (request->method() == HTTP_GET) { 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 || if (strcmp(request->url().c_str(), PLAYLIST_PATH) == 0 ||
strcmp(request->url().c_str(), SSIDS_PATH) == 0 || strcmp(request->url().c_str(), SSIDS_PATH) == 0 ||
strcmp(request->url().c_str(), INDEX_PATH) == 0 || strcmp(request->url().c_str(), INDEX_PATH) == 0 ||
@@ -595,13 +575,15 @@ void handleNotFound(AsyncWebServerRequest * request) {
return; return;
} }
if (request->url() == "/variables.js") { if (request->url() == "/variables.js") {
char varjsbuf[BUFLEN]; 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");
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", netserver.nsBuf);
request->send(200, "text/html", varjsbuf);
return; 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){ 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; return;
} }
if (request->method() == HTTP_GET && request->url() == "/webboard") { 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_GET ) { request->send_P(200, "text/html", emptyfs_html); return; }
if(request->url()=="/" && request->method() == HTTP_POST) { if(request->url()=="/" && request->method() == HTTP_POST) {
if(request->arg("ssid")!="" && request->arg("pass")!=""){ if(request->arg("ssid")!="" && request->arg("pass")!=""){
char buf[BUFLEN]; netserver.nsBuf[0]='\0';
memset(buf, 0, BUFLEN); snprintf(netserver.nsBuf, sizeof(netserver.nsBuf), "%s\t%s", request->arg("ssid").c_str(), request->arg("pass").c_str());
snprintf(buf, BUFLEN, "%s\t%s", request->arg("ssid").c_str(), request->arg("pass").c_str());
request->redirect("/"); request->redirect("/");
config.saveWifiFromNextion(buf); config.saveWifiFromNextion(netserver.nsBuf);
return; return;
} }
request->redirect("/"); request->redirect("/");
@@ -641,7 +622,12 @@ void handleIndex(AsyncWebServerRequest * request) {
} }
#endif #endif
if (strcmp(request->url().c_str(), "/") == 0 && request->params() == 0) { 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; return;
} }
if(network.status == CONNECTED){ if(network.status == CONNECTED){

View File

@@ -1,9 +1,11 @@
#ifndef netserver_h #ifndef netserver_h
#define netserver_h #define netserver_h
#include "Arduino.h" #include "Arduino.h"
#include "config.h"
#include "../AsyncWebServer/ESPAsyncWebServer.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 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 }; enum import_e : uint8_t { IMDONE=0, IMPL=1, IMWIFI=2 };
const char emptyfs_html[] PROGMEM = R"( const char emptyfs_html[] PROGMEM = R"(
@@ -74,6 +76,7 @@ const char index_html[] PROGMEM = R"(
<div id="content" class="hidden progmem"> <div id="content" class="hidden progmem">
</div><!--content--> </div><!--content-->
<div id="progress"><span id="loader"></span></div> <div id="progress"><span id="loader"></span></div>
<div id="heap"></div>
</body> </body>
</html> </html>
)"; )";
@@ -96,6 +99,7 @@ class NetServer {
import_e importRequest; import_e importRequest;
bool resumePlay; bool resumePlay;
char chunkedPathBuffer[40]; char chunkedPathBuffer[40];
char nsBuf[BUFLEN], nsBuf2[BUFLEN];
public: public:
NetServer() {}; NetServer() {};
bool begin(bool quiet=false); bool begin(bool quiet=false);
@@ -114,7 +118,10 @@ class NetServer {
private: private:
requestType_e request; requestType_e request;
QueueHandle_t nsQueue; QueueHandle_t nsQueue;
char _wscmd[65], _wsval[65];
char wsBuf[BUFLEN*2];
int rssi; int rssi;
uint32_t playerBufMax;
void getPlaylist(uint8_t clientId); void getPlaylist(uint8_t clientId);
bool importPlaylist(); bool importPlaylist();
static size_t chunkedHtmlPageCallback(uint8_t* buffer, size_t maxLen, size_t index); static size_t chunkedHtmlPageCallback(uint8_t* buffer, size_t maxLen, size_t index);

View File

@@ -6,98 +6,17 @@
#include "netserver.h" #include "netserver.h"
#include "player.h" #include "player.h"
#include "mqtt.h" #include "mqtt.h"
#include "timekeeper.h"
#ifndef WIFI_ATTEMPTS #ifndef WIFI_ATTEMPTS
#define WIFI_ATTEMPTS 16 #define WIFI_ATTEMPTS 16
#endif #endif
#ifndef SEARCH_WIFI_CORE_ID
#define SEARCH_WIFI_CORE_ID 0
#endif
MyNetwork network; MyNetwork network;
TaskHandle_t syncTaskHandle;
//TaskHandle_t reconnectTaskHandle;
bool getWeather(char *wstr);
void doSync(void * pvParameters);
void ticks() {
if(!display.ready()) return; //waiting for SD is ready
pm.on_ticker();
static const uint16_t weatherSyncInterval=1800;
//static const uint16_t weatherSyncIntervalFail=10;
#if RTCSUPPORTED
static const uint32_t timeSyncInterval=86400;
static uint32_t timeSyncTicks = 0;
#else
static const uint16_t timeSyncInterval=3600;
static uint16_t timeSyncTicks = 0;
#endif
static uint16_t weatherSyncTicks = 0;
static bool divrssi;
timeSyncTicks++;
weatherSyncTicks++;
divrssi = !divrssi;
if(network.status == CONNECTED){
if(network.forceTimeSync || network.forceWeather){
xTaskCreatePinnedToCore(doSync, "doSync", 1024 * 4, NULL, 0, &syncTaskHandle, 0);
}
if(timeSyncTicks >= timeSyncInterval){
timeSyncTicks=0;
network.forceTimeSync = true;
}
if(weatherSyncTicks >= weatherSyncInterval){
weatherSyncTicks=0;
network.forceWeather = true;
}
}
#ifndef DSP_LCD
if(config.store.screensaverEnabled && display.mode()==PLAYER && !player.isRunning()){
config.screensaverTicks++;
if(config.screensaverTicks > config.store.screensaverTimeout+SCREENSAVERSTARTUPDELAY){
if(config.store.screensaverBlank){
display.putRequest(NEWMODE, SCREENBLANK);
}else{
display.putRequest(NEWMODE, SCREENSAVER);
}
}
}
if(config.store.screensaverPlayingEnabled && display.mode()==PLAYER && player.isRunning()){
config.screensaverPlayingTicks++;
if(config.screensaverPlayingTicks > config.store.screensaverPlayingTimeout*60+SCREENSAVERSTARTUPDELAY){
if(config.store.screensaverPlayingBlank){
display.putRequest(NEWMODE, SCREENBLANK);
}else{
display.putRequest(NEWMODE, SCREENSAVER);
}
}
}
#endif
#if RTCSUPPORTED
if(config.isRTCFound()){
rtc.getTime(&network.timeinfo);
mktime(&network.timeinfo);
display.putRequest(CLOCK);
}
#else
if(network.timeinfo.tm_year>100 || network.status == SDREADY) {
network.timeinfo.tm_sec++;
mktime(&network.timeinfo);
display.putRequest(CLOCK);
}
#endif
if(player.isRunning() && config.getMode()==PM_SDCARD) netserver.requestOnChange(SDPOS, 0);
if(divrssi) {
if(network.status == CONNECTED){
netserver.setRSSI(WiFi.RSSI());
netserver.requestOnChange(NRSSI, 0);
display.putRequest(DSPRSSI, netserver.getRSSI());
}
#ifdef USE_SD
if(display.mode()!=SDCHANGE) player.sendCommand({PR_CHECKSD, 0});
#endif
player.sendCommand({PR_VUTONUS, 0});
}
}
void MyNetwork::WiFiReconnected(WiFiEvent_t event, WiFiEventInfo_t info){ void MyNetwork::WiFiReconnected(WiFiEvent_t event, WiFiEventInfo_t info){
network.beginReconnect = false; network.beginReconnect = false;
player.lockOutput = false; player.lockOutput = false;
@@ -152,6 +71,8 @@ bool MyNetwork::wifiBegin(bool silent){
Serial.print("##[BOOT]#\t"); Serial.print("##[BOOT]#\t");
display.putRequest(BOOTSTRING, ls); display.putRequest(BOOTSTRING, ls);
} }
WiFi.disconnect(true, true); //disconnect & erase internal credentials https://github.com/e2002/yoradio/pull/164/commits/89d8b4450dde99cd7930b84bb14d81dab920b879
delay(100);
WiFi.begin(config.ssids[ls].ssid, config.ssids[ls].password); WiFi.begin(config.ssids[ls].ssid, config.ssids[ls].password);
while (WiFi.status() != WL_CONNECTED) { while (WiFi.status() != WL_CONNECTED) {
if(!silent) Serial.print("."); if(!silent) Serial.print(".");
@@ -180,7 +101,7 @@ bool MyNetwork::wifiBegin(bool silent){
void searchWiFi(void * pvParameters){ void searchWiFi(void * pvParameters){
if(!network.wifiBegin(true)){ if(!network.wifiBegin(true)){
delay(10000); delay(10000);
xTaskCreatePinnedToCore(searchWiFi, "searchWiFi", 1024 * 4, NULL, 0, NULL, 0); xTaskCreatePinnedToCore(searchWiFi, "searchWiFi", 1024 * 4, NULL, 0, NULL, SEARCH_WIFI_CORE_ID);
}else{ }else{
network.status = CONNECTED; network.status = CONNECTED;
netserver.begin(true); netserver.begin(true);
@@ -196,8 +117,6 @@ void searchWiFi(void * pvParameters){
void MyNetwork::begin() { void MyNetwork::begin() {
BOOTLOG("network.begin"); BOOTLOG("network.begin");
config.initNetwork(); config.initNetwork();
ctimer.detach();
forceTimeSync = forceWeather = true;
if (config.ssidsCount == 0 || DBGAP) { if (config.ssidsCount == 0 || DBGAP) {
raiseSoftAP(); raiseSoftAP();
return; return;
@@ -213,7 +132,7 @@ void MyNetwork::begin() {
setWifiParams(); setWifiParams();
}else{ }else{
status = SDREADY; status = SDREADY;
xTaskCreatePinnedToCore(searchWiFi, "searchWiFi", 1024 * 4, NULL, 0, NULL, 0); xTaskCreatePinnedToCore(searchWiFi, "searchWiFi", 1024 * 4, NULL, 0, NULL, SEARCH_WIFI_CORE_ID);
} }
Serial.println("##[BOOT]#\tdone"); Serial.println("##[BOOT]#\tdone");
@@ -226,7 +145,6 @@ void MyNetwork::begin() {
display.putRequest(CLOCK); display.putRequest(CLOCK);
} }
#endif #endif
ctimer.attach(1, ticks);
if (network_on_connect) network_on_connect(); if (network_on_connect) network_on_connect();
pm.on_connect(); pm.on_connect();
} }
@@ -236,16 +154,11 @@ void MyNetwork::setWifiParams(){
WiFi.onEvent(WiFiReconnected, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP); WiFi.onEvent(WiFiReconnected, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP);
WiFi.onEvent(WiFiLostConnection, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED); WiFi.onEvent(WiFiLostConnection, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED);
weatherBuf=NULL; weatherBuf=NULL;
trueWeather = false;
#if (DSP_MODEL!=DSP_DUMMY || defined(USE_NEXTION)) && !defined(HIDE_WEATHER) #if (DSP_MODEL!=DSP_DUMMY || defined(USE_NEXTION)) && !defined(HIDE_WEATHER)
weatherBuf = (char *) malloc(sizeof(char) * WEATHER_STRING_L); weatherBuf = (char *) malloc(sizeof(char) * WEATHER_STRING_L);
memset(weatherBuf, 0, WEATHER_STRING_L); memset(weatherBuf, 0, WEATHER_STRING_L);
#endif #endif
if(strlen(config.store.sntp1)>0 && strlen(config.store.sntp2)>0){ //config.setTimeConf(); //??
configTime(config.store.tzHour * 3600 + config.store.tzMin * 60, config.getTimezoneOffset(), config.store.sntp1, config.store.sntp2);
}else if(strlen(config.store.sntp1)>0){
configTime(config.store.tzHour * 3600 + config.store.tzMin * 60, config.getTimezoneOffset(), config.store.sntp1);
}
} }
void MyNetwork::requestTimeSync(bool withTelnetOutput, uint8_t clientId) { void MyNetwork::requestTimeSync(bool withTelnetOutput, uint8_t clientId) {
@@ -275,209 +188,9 @@ void MyNetwork::raiseSoftAP() {
BOOTLOG("************************************************"); BOOTLOG("************************************************");
status = SOFT_AP; status = SOFT_AP;
if(config.store.softapdelay>0) if(config.store.softapdelay>0)
rtimer.once(config.store.softapdelay*60, rebootTime); timekeeper.waitAndDo(config.store.softapdelay*60, rebootTime);
} }
void MyNetwork::requestWeatherSync(){ void MyNetwork::requestWeatherSync(){
display.putRequest(NEWWEATHER); display.putRequest(NEWWEATHER);
} }
void doSync( void * pvParameters ) {
static uint8_t tsFailCnt = 0;
//static uint8_t wsFailCnt = 0;
if(network.forceTimeSync){
network.forceTimeSync = false;
if(getLocalTime(&network.timeinfo)){
tsFailCnt = 0;
network.forceTimeSync = false;
mktime(&network.timeinfo);
display.putRequest(CLOCK);
network.requestTimeSync(true);
#if RTCSUPPORTED
if (config.isRTCFound()) rtc.setTime(&network.timeinfo);
#endif
}else{
if(tsFailCnt<4){
network.forceTimeSync = true;
tsFailCnt++;
}else{
network.forceTimeSync = false;
tsFailCnt=0;
}
}
}
if(network.weatherBuf && (strlen(config.store.weatherkey)!=0 && config.store.showweather) && network.forceWeather){
network.forceWeather = false;
network.trueWeather=getWeather(network.weatherBuf);
}
vTaskDelete( NULL );
}
bool getWeather(char *wstr) {
#if (DSP_MODEL!=DSP_DUMMY || defined(USE_NEXTION)) && !defined(HIDE_WEATHER)
WiFiClient client;
const char* host = "api.openweathermap.org";
if (!client.connect(host, 80)) {
Serial.println("##WEATHER###: connection failed");
return false;
}
char httpget[250] = {0};
sprintf(httpget, "GET /data/2.5/weather?lat=%s&lon=%s&units=%s&lang=%s&appid=%s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", config.store.weatherlat, config.store.weatherlon, weatherUnits, weatherLang, config.store.weatherkey, host);
client.print(httpget);
unsigned long timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 2000UL) {
Serial.println("##WEATHER###: client available timeout !");
client.stop();
return false;
}
}
timeout = millis();
String line = "";
if (client.connected()) {
while (client.available())
{
line = client.readStringUntil('\n');
if (strstr(line.c_str(), "\"temp\"") != NULL) {
client.stop();
break;
}
if ((millis() - timeout) > 500)
{
client.stop();
Serial.println("##WEATHER###: client read timeout !");
return false;
}
}
}
if (strstr(line.c_str(), "\"temp\"") == NULL) {
Serial.println("##WEATHER###: weather not found !");
return false;
}
char *tmpe;
char *tmps;
char *tmpc;
const char* cursor = line.c_str();
char desc[120], temp[20], hum[20], press[20], icon[5];
tmps = strstr(cursor, "\"description\":\"");
if (tmps == NULL) { Serial.println("##WEATHER###: description not found !"); return false;}
tmps += 15;
tmpe = strstr(tmps, "\",\"");
if (tmpe == NULL) { Serial.println("##WEATHER###: description not found !"); return false;}
strlcpy(desc, tmps, tmpe - tmps + 1);
cursor = tmpe + 2;
// "ясно","icon":"01d"}],
tmps = strstr(cursor, "\"icon\":\"");
if (tmps == NULL) { Serial.println("##WEATHER###: icon not found !"); return false;}
tmps += 8;
tmpe = strstr(tmps, "\"}");
if (tmpe == NULL) { Serial.println("##WEATHER###: icon not found !"); return false;}
strlcpy(icon, tmps, tmpe - tmps + 1);
cursor = tmpe + 2;
tmps = strstr(cursor, "\"temp\":");
if (tmps == NULL) { Serial.println("##WEATHER###: temp not found !"); return false;}
tmps += 7;
tmpe = strstr(tmps, ",\"");
if (tmpe == NULL) { Serial.println("##WEATHER###: temp not found !"); return false;}
strlcpy(temp, tmps, tmpe - tmps + 1);
cursor = tmpe + 1;
float tempf = atof(temp);
tmps = strstr(cursor, "\"feels_like\":");
if (tmps == NULL) { Serial.println("##WEATHER###: feels_like not found !"); return false;}
tmps += 13;
tmpe = strstr(tmps, ",\"");
if (tmpe == NULL) { Serial.println("##WEATHER###: feels_like not found !"); return false;}
strlcpy(temp, tmps, tmpe - tmps + 1);
cursor = tmpe + 2;
float tempfl = atof(temp); (void)tempfl;
tmps = strstr(cursor, "\"pressure\":");
if (tmps == NULL) { Serial.println("##WEATHER###: pressure not found !"); return false;}
tmps += 11;
tmpe = strstr(tmps, ",\"");
if (tmpe == NULL) { Serial.println("##WEATHER###: pressure not found !"); return false;}
strlcpy(press, tmps, tmpe - tmps + 1);
cursor = tmpe + 2;
int pressi = (float)atoi(press) / 1.333;
tmps = strstr(cursor, "humidity\":");
if (tmps == NULL) { Serial.println("##WEATHER###: humidity not found !"); return false;}
tmps += 10;
tmpe = strstr(tmps, ",\"");
tmpc = strstr(tmps, "}");
if (tmpe == NULL) { Serial.println("##WEATHER###: humidity not found !"); return false;}
strlcpy(hum, tmps, tmpe - tmps + (tmpc>tmpe?1:0));
tmps = strstr(cursor, "\"grnd_level\":");
bool grnd_level_pr = (tmps != NULL);
if(grnd_level_pr){
tmps += 13;
tmpe = strstr(tmps, ",\"");
if (tmpe == NULL) { Serial.println("##WEATHER###: grnd_level not found !"); return false;}
strlcpy(press, tmps, tmpe - tmps + 1);
cursor = tmpe + 2;
pressi = (float)atoi(press) / 1.333;
}
tmps = strstr(cursor, "\"speed\":");
if (tmps == NULL) { Serial.println("##WEATHER###: wind speed not found !"); return false;}
tmps += 8;
tmpe = strstr(tmps, ",\"");
if (tmpe == NULL) { Serial.println("##WEATHER###: wind speed not found !"); return false;}
strlcpy(temp, tmps, tmpe - tmps + 1);
cursor = tmpe + 1;
float wind_speed = atof(temp); (void)wind_speed;
tmps = strstr(cursor, "\"deg\":");
if (tmps == NULL) { Serial.println("##WEATHER###: wind deg not found !"); return false;}
tmps += 6;
tmpe = strstr(tmps, ",\"");
if (tmpe == NULL) { Serial.println("##WEATHER###: wind deg not found !"); return false;}
strlcpy(temp, tmps, tmpe - tmps + 1);
cursor = tmpe + 1;
int wind_deg = atof(temp)/22.5;
if(wind_deg<0) wind_deg = 16+wind_deg;
#ifdef USE_NEXTION
nextion.putcmdf("press_txt.txt=\"%dmm\"", pressi);
nextion.putcmdf("hum_txt.txt=\"%d%%\"", atoi(hum));
char cmd[30];
snprintf(cmd, sizeof(cmd)-1,"temp_txt.txt=\"%.1f\"", tempf);
nextion.putcmd(cmd);
int iconofset;
if(strstr(icon,"01")!=NULL) iconofset = 0;
else if(strstr(icon,"02")!=NULL) iconofset = 1;
else if(strstr(icon,"03")!=NULL) iconofset = 2;
else if(strstr(icon,"04")!=NULL) iconofset = 3;
else if(strstr(icon,"09")!=NULL) iconofset = 4;
else if(strstr(icon,"10")!=NULL) iconofset = 5;
else if(strstr(icon,"11")!=NULL) iconofset = 6;
else if(strstr(icon,"13")!=NULL) iconofset = 7;
else if(strstr(icon,"50")!=NULL) iconofset = 8;
else iconofset = 9;
nextion.putcmd("cond_img.pic", 50+iconofset);
nextion.weatherVisible(1);
#endif
Serial.printf("##WEATHER###: description: %s, temp:%.1f C, pressure:%dmmHg, humidity:%s%%\n", desc, tempf, pressi, hum);
#ifdef WEATHER_FMT_SHORT
sprintf(wstr, weatherFmt, tempf, pressi, hum);
#else
#if EXT_WEATHER
sprintf(wstr, weatherFmt, desc, tempf, tempfl, pressi, hum, wind_speed, wind[wind_deg]);
#else
sprintf(wstr, weatherFmt, desc, tempf, pressi, hum);
#endif
#endif
network.requestWeatherSync();
return true;
#endif // if (DSP_MODEL!=DSP_DUMMY || defined(USE_NEXTION)) && !defined(HIDE_WEATHER)
return false;
}

View File

@@ -1,6 +1,5 @@
#ifndef network_h #ifndef network_h
#define network_h #define network_h
#include <Ticker.h>
#include "time.h" #include "time.h"
#include "WiFi.h" #include "WiFi.h"
#include "rtcsupport.h" #include "rtcsupport.h"
@@ -17,12 +16,10 @@ class MyNetwork {
public: public:
n_Status_e status; n_Status_e status;
struct tm timeinfo; struct tm timeinfo;
bool firstRun, forceTimeSync, forceWeather;
bool lostPlaying = false, beginReconnect = false; bool lostPlaying = false, beginReconnect = false;
//uint8_t tsFailCnt, wsFailCnt; //uint8_t tsFailCnt, wsFailCnt;
Ticker ctimer;
char *weatherBuf; char *weatherBuf;
bool trueWeather; //bool trueWeather;
public: public:
MyNetwork() {}; MyNetwork() {};
void begin(); void begin();
@@ -31,7 +28,6 @@ class MyNetwork {
void setWifiParams(); void setWifiParams();
bool wifiBegin(bool silent=false); bool wifiBegin(bool silent=false);
private: private:
Ticker rtimer;
void raiseSoftAP(); void raiseSoftAP();
static void WiFiLostConnection(WiFiEvent_t event, WiFiEventInfo_t info); static void WiFiLostConnection(WiFiEvent_t event, WiFiEventInfo_t info);
static void WiFiReconnected(WiFiEvent_t event, WiFiEventInfo_t info); static void WiFiReconnected(WiFiEvent_t event, WiFiEventInfo_t info);

View File

@@ -1,7 +1,7 @@
#ifndef options_h #ifndef options_h
#define options_h #define options_h
#define YOVERSION "0.9.533" #define YOVERSION "0.9.550"
/******************************************************* /*******************************************************
DO NOT EDIT THIS FILE. DO NOT EDIT THIS FILE.
@@ -361,6 +361,9 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti
#ifndef IR_TIMEOUT #ifndef IR_TIMEOUT
#define IR_TIMEOUT 80 // kTimeout, see IRremoteESP8266 documentation #define IR_TIMEOUT 80 // kTimeout, see IRremoteESP8266 documentation
#endif #endif
#ifndef IR_BUFSIZE
#define IR_BUFSIZE 128
#endif
/* THEMES */ /* THEMES */
/* color name R G B */ /* color name R G B */
@@ -488,10 +491,26 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti
#ifndef USE_OTA #ifndef USE_OTA
#define USE_OTA false #define USE_OTA false
#endif #endif
#ifndef WATCHDOG_INTERVAL
#define WATCHDOG_INTERVAL 3 //sec.
#endif
//#define OTA_PASS "myotapassword12345" //#define OTA_PASS "myotapassword12345"
//#define HTTP_USER "user" //#define HTTP_USER "user"
//#define HTTP_PASS "password" //#define HTTP_PASS "password"
#ifndef WATCHDOG_TASK_SIZE
#define WATCHDOG_TASK_SIZE 1024*6
#endif
#ifndef WATCHDOG_TASK_PRIORITY
#define WATCHDOG_TASK_PRIORITY 3
#endif
#ifndef WATCHDOG_TASK_CORE_ID
#define WATCHDOG_TASK_CORE_ID 1
#endif
#ifndef CONNECTION_TIMEOUT
#define CONNECTION_TIMEOUT 5700
#endif
#ifndef CONNECTION_TIMEOUT_SSL
#define CONNECTION_TIMEOUT_SSL 5700
#endif
#endif #endif

View File

@@ -5,6 +5,7 @@
#include "display.h" #include "display.h"
#include "sdmanager.h" #include "sdmanager.h"
#include "netserver.h" #include "netserver.h"
#include "timekeeper.h"
Player player; Player player;
QueueHandle_t playerQueue; QueueHandle_t playerQueue;
@@ -35,10 +36,10 @@ void Player::init() {
Serial.print("##[BOOT]#\tplayer.init\t"); Serial.print("##[BOOT]#\tplayer.init\t");
playerQueue=NULL; playerQueue=NULL;
_resumeFilePos = 0; _resumeFilePos = 0;
_hasError=false;
playerQueue = xQueueCreate( 5, sizeof( playerRequestParams_t ) ); playerQueue = xQueueCreate( 5, sizeof( playerRequestParams_t ) );
setOutputPins(false); setOutputPins(false);
delay(50); delay(50);
memset(_plError, 0, PLERR_LN);
#ifdef MQTT_ROOT_TOPIC #ifdef MQTT_ROOT_TOPIC
memset(burl, 0, MQTT_BURL_SIZE); memset(burl, 0, MQTT_BURL_SIZE);
#endif #endif
@@ -56,14 +57,13 @@ void Player::init() {
setTone(config.store.bass, config.store.middle, config.store.trebble); setTone(config.store.bass, config.store.middle, config.store.trebble);
setVolume(0); setVolume(0);
_status = STOPPED; _status = STOPPED;
//setOutputPins(false);
_volTimer=false; _volTimer=false;
//randomSeed(analogRead(0)); //randomSeed(analogRead(0));
#if PLAYER_FORCE_MONO #if PLAYER_FORCE_MONO
forceMono(true); forceMono(true);
#endif #endif
_loadVol(config.store.volume); _loadVol(config.store.volume);
setConnectionTimeout(1700, 3700); setConnectionTimeout(CONNECTION_TIMEOUT, CONNECTION_TIMEOUT_SSL);
Serial.println("done"); Serial.println("done");
} }
@@ -78,16 +78,18 @@ void Player::resetQueue(){
void Player::stopInfo() { void Player::stopInfo() {
config.setSmartStart(0); config.setSmartStart(0);
//telnet.info();
netserver.requestOnChange(MODE, 0); netserver.requestOnChange(MODE, 0);
} }
void Player::setError(){
_hasError=true;
config.setTitle(config.tmpBuf);
telnet.printf("##ERROR#:\t%s\n", config.tmpBuf);
}
void Player::setError(const char *e){ void Player::setError(const char *e){
strlcpy(_plError, e, PLERR_LN); strlcpy(config.tmpBuf, e, sizeof(config.tmpBuf));
if(hasError()) { setError();
config.setTitle(_plError);
telnet.printf("##ERROR#:\t%s\n", e);
}
} }
void Player::_stop(bool alreadyStopped){ void Player::_stop(bool alreadyStopped){
@@ -95,17 +97,19 @@ void Player::_stop(bool alreadyStopped){
if(config.getMode()==PM_SDCARD && !alreadyStopped) config.sdResumePos = player.getFilePos(); if(config.getMode()==PM_SDCARD && !alreadyStopped) config.sdResumePos = player.getFilePos();
_status = STOPPED; _status = STOPPED;
setOutputPins(false); setOutputPins(false);
if(!hasError()) config.setTitle((display.mode()==LOST || display.mode()==UPDATING)?"":const_PlStopped); if(!_hasError) config.setTitle((display.mode()==LOST || display.mode()==UPDATING)?"":const_PlStopped);
config.station.bitrate = 0; config.station.bitrate = 0;
config.setBitrateFormat(BF_UNCNOWN); config.setBitrateFormat(BF_UNCNOWN);
#ifdef USE_NEXTION #ifdef USE_NEXTION
nextion.bitrate(config.station.bitrate); nextion.bitrate(config.station.bitrate);
#endif #endif
setDefaults();
if(!alreadyStopped) stopSong();
netserver.requestOnChange(BITRATE, 0); netserver.requestOnChange(BITRATE, 0);
display.putRequest(DBITRATE); display.putRequest(DBITRATE);
display.putRequest(PSTOP); display.putRequest(PSTOP);
setDefaults(); //setDefaults();
if(!alreadyStopped) stopSong(); //if(!alreadyStopped) stopSong();
if(!lockOutput) stopInfo(); if(!lockOutput) stopInfo();
if (player_on_stop_play) player_on_stop_play(); if (player_on_stop_play) player_on_stop_play();
pm.on_stop_play(); pm.on_stop_play();
@@ -119,6 +123,12 @@ void Player::initHeaders(const char *file) {
//netserver.requestOnChange(SDPOS, 0); //netserver.requestOnChange(SDPOS, 0);
setDefaults(); setDefaults();
} }
void resetPlayer(){
if(!config.store.watchdog) return;
player.resetQueue();
player.sendCommand({PR_STOP, 0});
player.loop();
}
#ifndef PL_QUEUE_TICKS #ifndef PL_QUEUE_TICKS
#define PL_QUEUE_TICKS 0 #define PL_QUEUE_TICKS 0
@@ -141,6 +151,10 @@ void Player::loop() {
pm.on_station_change(); pm.on_station_change();
break; break;
} }
case PR_TOGGLE: {
toggle();
break;
}
case PR_VOL: { case PR_VOL: {
config.setVolume(requestP.payload); config.setVolume(requestP.payload);
Audio::setVolume(volToI2S(requestP.payload)); Audio::setVolume(volToI2S(requestP.payload));
@@ -157,8 +171,19 @@ void Player::loop() {
break; break;
} }
#endif #endif
case PR_VUTONUS: case PR_VUTONUS: {
if(config.vuThreshold>10) config.vuThreshold -=10; if(config.vuThreshold>10) config.vuThreshold -=10;
break;
}
case PR_BURL: {
#ifdef MQTT_ROOT_TOPIC
if(strlen(burl)>0){
browseUrl();
}
#endif
break;
}
default: break; default: break;
} }
} }
@@ -170,11 +195,12 @@ void Player::loop() {
_volTimer=false; _volTimer=false;
} }
} }
/*
#ifdef MQTT_ROOT_TOPIC #ifdef MQTT_ROOT_TOPIC
if(strlen(burl)>0){ if(strlen(burl)>0){
browseUrl(); browseUrl();
} }
#endif #endif*/
} }
void Player::setOutputPins(bool isPlaying) { void Player::setOutputPins(bool isPlaying) {
@@ -185,32 +211,15 @@ void Player::setOutputPins(bool isPlaying) {
void Player::_play(uint16_t stationId) { void Player::_play(uint16_t stationId) {
log_i("%s called, stationId=%d", __func__, stationId); log_i("%s called, stationId=%d", __func__, stationId);
setError(""); _hasError=false;
setDefaults(); setDefaults();
remoteStationName = false; _status = STOPPED;
config.setDspOn(1);
config.vuThreshold = 0;
//display.putRequest(PSTOP);
config.screensaverTicks=SCREENSAVERSTARTUPDELAY;
config.screensaverPlayingTicks=SCREENSAVERSTARTUPDELAY;
if(config.getMode()!=PM_SDCARD) {
display.putRequest(PSTOP);
}
setOutputPins(false); setOutputPins(false);
//config.setTitle(config.getMode()==PM_WEB?const_PlConnect:""); remoteStationName = false;
if(!config.loadStation(stationId)) return;
config.setTitle(config.getMode()==PM_WEB?const_PlConnect:"[next track]");
config.station.bitrate=0;
config.setBitrateFormat(BF_UNCNOWN);
if(!config.prepareForPlaying(stationId)) return;
_loadVol(config.store.volume); _loadVol(config.store.volume);
display.putRequest(DBITRATE);
display.putRequest(NEWSTATION);
netserver.requestOnChange(STATION, 0);
netserver.loop();
netserver.loop();
if(config.store.smartstart!=2)
config.setSmartStart(0);
bool isConnected = false; bool isConnected = false;
if(config.getMode()==PM_SDCARD && SDC_CS!=255){ if(config.getMode()==PM_SDCARD && SDC_CS!=255){
isConnected=connecttoFS(sdman,config.station.url,config.sdResumePos==0?_resumeFilePos:config.sdResumePos-player.sd_min); isConnected=connecttoFS(sdman,config.station.url,config.sdResumePos==0?_resumeFilePos:config.sdResumePos-player.sd_min);
@@ -219,36 +228,25 @@ void Player::_play(uint16_t stationId) {
} }
if(config.getMode()==PM_WEB) isConnected=connecttohost(config.station.url); if(config.getMode()==PM_WEB) isConnected=connecttohost(config.station.url);
if(isConnected){ if(isConnected){
//if (config.store.play_mode==PM_WEB?connecttohost(config.station.url):connecttoFS(SD,config.station.url,config.sdResumePos==0?_resumeFilePos:config.sdResumePos-player.sd_min)) {
_status = PLAYING; _status = PLAYING;
if(config.getMode()==PM_SDCARD) { config.configPostPlaying(stationId);
config.sdResumePos = 0;
config.saveValue(&config.store.lastSdStation, stationId);
}
//config.setTitle("");
if(config.store.smartstart!=2)
config.setSmartStart(1);
netserver.requestOnChange(MODE, 0);
setOutputPins(true); setOutputPins(true);
display.putRequest(NEWMODE, PLAYER);
display.putRequest(PSTART);
if (player_on_start_play) player_on_start_play(); if (player_on_start_play) player_on_start_play();
pm.on_start_play(); pm.on_start_play();
}else{ }else{
telnet.printf("##ERROR#:\tError connecting to %s\n", config.station.url); telnet.printf("##ERROR#:\tError connecting to %.128s\n", config.station.url);
SET_PLAY_ERROR("Error connecting to %s", config.station.url); snprintf(config.tmpBuf, sizeof(config.tmpBuf), "Error connecting to %.128s", config.station.url); setError();
_stop(true); _stop(true);
}; };
} }
#ifdef MQTT_ROOT_TOPIC #ifdef MQTT_ROOT_TOPIC
void Player::browseUrl(){ void Player::browseUrl(){
setError(""); _hasError=false;
remoteStationName = true; remoteStationName = true;
config.setDspOn(1); config.setDspOn(1);
resumeAfterUrl = _status==PLAYING; resumeAfterUrl = _status==PLAYING;
display.putRequest(PSTOP); display.putRequest(PSTOP);
// setDefaults();
setOutputPins(false); setOutputPins(false);
config.setTitle(const_PlConnect); config.setTitle(const_PlConnect);
if (connecttohost(burl)){ if (connecttohost(burl)){
@@ -260,16 +258,15 @@ void Player::browseUrl(){
if (player_on_start_play) player_on_start_play(); if (player_on_start_play) player_on_start_play();
pm.on_start_play(); pm.on_start_play();
}else{ }else{
telnet.printf("##ERROR#:\tError connecting to %s\n", burl); telnet.printf("##ERROR#:\tError connecting to %.128s\n", burl);
SET_PLAY_ERROR("Error connecting to %s", burl); snprintf(config.tmpBuf, sizeof(config.tmpBuf), "Error connecting to %.128s", burl); setError();
_stop(true); _stop(true);
} }
memset(burl, 0, MQTT_BURL_SIZE); //memset(burl, 0, MQTT_BURL_SIZE);
} }
#endif #endif
void Player::prev() { void Player::prev() {
uint16_t lastStation = config.lastStation(); uint16_t lastStation = config.lastStation();
if(config.getMode()==PM_WEB || !config.store.sdsnuffle){ if(config.getMode()==PM_WEB || !config.store.sdsnuffle){
if (lastStation == 1) config.lastStation(config.playlistLength()); else config.lastStation(lastStation-1); if (lastStation == 1) config.lastStation(config.playlistLength()); else config.lastStation(lastStation-1);

View File

@@ -13,13 +13,14 @@
#endif #endif
#ifndef PLQ_SEND_DELAY #ifndef PLQ_SEND_DELAY
#define PLQ_SEND_DELAY portMAX_DELAY //#define PLQ_SEND_DELAY portMAX_DELAY
#define PLQ_SEND_DELAY pdMS_TO_TICKS(1000)
#endif #endif
#define PLERR_LN 64 //#define PLERR_LN 64
#define SET_PLAY_ERROR(...) {char buff[512 + 64]; sprintf(buff,__VA_ARGS__); setError(buff);} //#define SET_PLAY_ERROR(...) {char buff[512 + 64]; sprintf(buff,__VA_ARGS__); setError(buff);}
enum playerRequestType_e : uint8_t { PR_PLAY = 1, PR_STOP = 2, PR_PREV = 3, PR_NEXT = 4, PR_VOL = 5, PR_CHECKSD = 6, PR_VUTONUS = 7 }; enum playerRequestType_e : uint8_t { PR_PLAY = 1, PR_STOP = 2, PR_PREV = 3, PR_NEXT = 4, PR_VOL = 5, PR_CHECKSD = 6, PR_VUTONUS = 7, PR_BURL = 8, PR_TOGGLE = 9 };
struct playerRequestParams_t struct playerRequestParams_t
{ {
playerRequestType_e type; playerRequestType_e type;
@@ -34,11 +35,12 @@ class Player: public Audio {
bool _volTimer; /* delayed volume save */ bool _volTimer; /* delayed volume save */
uint32_t _resumeFilePos; uint32_t _resumeFilePos;
plStatus_e _status; plStatus_e _status;
char _plError[PLERR_LN]; //char _plError[PLERR_LN];
private: private:
void _stop(bool alreadyStopped = false); void _stop(bool alreadyStopped = false);
void _play(uint16_t stationId); void _play(uint16_t stationId);
void _loadVol(uint8_t volume); void _loadVol(uint8_t volume);
bool _hasError;
public: public:
bool lockOutput = true; bool lockOutput = true;
bool resumeAfterUrl = false; bool resumeAfterUrl = false;
@@ -51,8 +53,9 @@ class Player: public Audio {
void init(); void init();
void loop(); void loop();
void initHeaders(const char *file); void initHeaders(const char *file);
void setError();
void setError(const char *e); void setError(const char *e);
bool hasError() { return strlen(_plError)>0; } //bool hasError() { return strlen(_plError)>0; }
void sendCommand(playerRequestParams_t request); void sendCommand(playerRequestParams_t request);
void resetQueue(); void resetQueue();
#ifdef MQTT_ROOT_TOPIC #ifdef MQTT_ROOT_TOPIC

View File

@@ -45,10 +45,11 @@ bool SDManager::cardPresent() {
} }
bool SDManager::_checkNoMedia(const char* path){ bool SDManager::_checkNoMedia(const char* path){
char nomedia[BUFLEN]= {0}; if (path[strlen(path) - 1] == '/')
strlcat(nomedia, path, BUFLEN); snprintf(config.tmpBuf, sizeof(config.tmpBuf), "%s%s", path, ".nomedia");
strlcat(nomedia, "/.nomedia", BUFLEN); else
bool nm = exists(nomedia); snprintf(config.tmpBuf, sizeof(config.tmpBuf), "%s/%s", path, ".nomedia");
bool nm = exists(config.tmpBuf);
return nm; return nm;
} }

View File

@@ -5,11 +5,12 @@
#include "player.h" #include "player.h"
#include "network.h" #include "network.h"
#include "telnet.h" #include "telnet.h"
#include "esp_heap_caps.h"
Telnet telnet; Telnet telnet;
bool Telnet::_isIPSet(IPAddress ip) { bool Telnet::_isIPSet(IPAddress ip) {
return ip.toString() == "0.0.0.0"; return strcmp(config.ipToStr(ip), "0.0.0.0") == 0;
} }
bool Telnet::begin(bool quiet) { bool Telnet::begin(bool quiet) {
@@ -21,12 +22,11 @@ bool Telnet::begin(bool quiet) {
} }
if(!quiet) Serial.print("##[BOOT]#\ttelnet.begin\t"); if(!quiet) Serial.print("##[BOOT]#\ttelnet.begin\t");
if (WiFi.status() == WL_CONNECTED || _isIPSet(WiFi.softAPIP())) { if (WiFi.status() == WL_CONNECTED || _isIPSet(WiFi.softAPIP())) {
server.begin(); toggle();
server.setNoDelay(true);
if(!quiet){ if(!quiet){
Serial.println("done"); Serial.println("done");
Serial.println("##[BOOT]#"); Serial.println("##[BOOT]#");
BOOTLOG("Ready! Go to http:/%s/ to configure", WiFi.localIP().toString().c_str()); BOOTLOG("Ready! Go to http:/%s/ to configure", config.ipToStr(WiFi.localIP()));
BOOTLOG("------------------------------------------------"); BOOTLOG("------------------------------------------------");
Serial.println("##[BOOT]#"); Serial.println("##[BOOT]#");
} }
@@ -36,10 +36,19 @@ bool Telnet::begin(bool quiet) {
} }
} }
void Telnet::start() {
server.begin();
server.setNoDelay(true);
}
void Telnet::stop() { void Telnet::stop() {
server.stop(); server.stop();
} }
void Telnet::toggle() {
if(config.store.telnet) { start(); }else{ stop(); }
}
void Telnet::emptyClientStream(WiFiClient client) { void Telnet::emptyClientStream(WiFiClient client) {
client.flush(); client.flush();
delay(50); delay(50);
@@ -72,6 +81,7 @@ void Telnet::loop() {
return; return;
} }
uint8_t i; uint8_t i;
if(config.store.telnet)
if (WiFi.status() == WL_CONNECTED) { if (WiFi.status() == WL_CONNECTED) {
if (server.hasClient()) { if (server.hasClient()) {
for (i = 0; i < MAX_TLN_CLIENTS; i++) { for (i = 0; i < MAX_TLN_CLIENTS; i++) {
@@ -81,7 +91,7 @@ void Telnet::loop() {
} }
clients[i] = server.available(); clients[i] = server.available();
if (!clients[i]) Serial.println("available broken"); if (!clients[i]) Serial.println("available broken");
on_connect(clients[i].remoteIP().toString().c_str(), i); on_connect(config.ipToStr(clients[i].remoteIP()), i);
clients[i].setNoDelay(true); clients[i].setNoDelay(true);
emptyClientStream(clients[i]); emptyClientStream(clients[i]);
break; break;
@@ -125,35 +135,33 @@ void Telnet::print(uint8_t id, const char *buf) {
} }
void Telnet::printf(const char *format, ...) { void Telnet::printf(const char *format, ...) {
char buf[MAX_PRINTF_LEN];
va_list args; va_list args;
va_start (args, format ); va_start (args, format );
vsnprintf(buf, MAX_PRINTF_LEN, format, args); vsnprintf(cmBuf, sizeof(cmBuf), format, args);
va_end (args); va_end (args);
for (int id = 0; id < MAX_TLN_CLIENTS; id++) { for (int id = 0; id < MAX_TLN_CLIENTS; id++) {
if (clients[id] && clients[id].connected()) { if (clients[id] && clients[id].connected()) {
clients[id].print(buf); clients[id].print(cmBuf);
} }
} }
if (strcmp(buf, "> ") == 0) return; if (strcmp(cmBuf, "> ") == 0) return;
//if(strstr(buf,"\n> ")==NULL) Serial.print(buf); //if(strstr(buf,"\n> ")==NULL) Serial.print(buf);
char *nl = strstr(buf, "\n> "); char *nl = strstr(cmBuf, "\n> ");
if (nl != NULL) { buf[nl-buf+1] = '\0'; } if (nl != NULL) { cmBuf[nl-cmBuf+1] = '\0'; }
Serial.print(buf); Serial.print(cmBuf);
} }
void Telnet::printf(uint8_t id, const char *format, ...) { void Telnet::printf(uint8_t id, const char *format, ...) {
char buf[MAX_PRINTF_LEN];
va_list argptr; va_list argptr;
va_start(argptr, format); va_start(argptr, format);
vsnprintf(buf, MAX_PRINTF_LEN, format, argptr); vsnprintf(cmBuf, sizeof(cmBuf), format, argptr);
va_end(argptr); va_end(argptr);
if(id>MAX_TLN_CLIENTS){ if(id>MAX_TLN_CLIENTS){
Serial.print(buf); Serial.print(cmBuf);
return; return;
} }
if (clients[id] && clients[id].connected()) { if (clients[id] && clients[id].connected()) {
clients[id].print(buf); clients[id].print(cmBuf);
} }
} }
@@ -164,9 +172,8 @@ void Telnet::on_connect(const char* str, uint8_t clientId) {
void Telnet::info() { void Telnet::info() {
telnet.printf("##CLI.INFO#\n"); telnet.printf("##CLI.INFO#\n");
char timeStringBuff[50]; strftime(config.tmpBuf, sizeof(config.tmpBuf), "%Y-%m-%dT%H:%M:%S+03:00", &network.timeinfo);
strftime(timeStringBuff, sizeof(timeStringBuff), "%Y-%m-%dT%H:%M:%S+03:00", &network.timeinfo); telnet.printf("##SYS.DATE#: %s\n", config.tmpBuf); //TODO timezone offset
telnet.printf("##SYS.DATE#: %s\n", timeStringBuff); //TODO timezone offset
telnet.printf("##CLI.NAMESET#: %d %s\n", config.lastStation(), config.station.name); telnet.printf("##CLI.NAMESET#: %d %s\n", config.lastStation(), config.station.name);
if (player.status() == PLAYING) { if (player.status() == PLAYING) {
telnet.printf("##CLI.META#: %s\n", config.station.title); telnet.printf("##CLI.META#: %s\n", config.station.title);
@@ -180,6 +187,16 @@ void Telnet::info() {
telnet.printf("> "); telnet.printf("> ");
} }
void Telnet::printHeapFragmentationInfo(uint8_t id){
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));
printf(id, "\n*************************************\n");
printf(id, "* Free heap: %u bytes\n", freeHeap);
printf(id, "* Largest free block: %u bytes\n", largestBlock);
printf(id, "* Fragmentation: %.2f%%\n", fragmentation);
printf(id, "*************************************\n\n");
}
void Telnet::on_input(const char* str, uint8_t clientId) { void Telnet::on_input(const char* str, uint8_t clientId) {
if (strlen(str) == 0) return; if (strlen(str) == 0) return;
if(network.status == CONNECTED){ if(network.status == CONNECTED){
@@ -253,12 +270,11 @@ void Telnet::on_input(const char* str, uint8_t clientId) {
if (!file || file.isDirectory()) { if (!file || file.isDirectory()) {
return; return;
} }
char sName[BUFLEN], sUrl[BUFLEN];
int sOvol; int sOvol;
uint8_t c = 1; uint8_t c = 1;
while (file.available()) { while (file.available()) {
if (config.parseCSV(file.readStringUntil('\n').c_str(), sName, sUrl, sOvol)) { if (config.parseCSV(file.readStringUntil('\n').c_str(), config.tmpBuf, config.tmpBuf2, sOvol)) {
printf(clientId, "#CLI.LISTNUM#: %*d: %s, %s\n", 3, c, sName, sUrl); printf(clientId, "#CLI.LISTNUM#: %*d: %s, %s\n", 3, c, config.tmpBuf, config.tmpBuf2);
c++; c++;
} }
} }
@@ -268,12 +284,11 @@ void Telnet::on_input(const char* str, uint8_t clientId) {
} }
if (strcmp(str, "cli.info") == 0 || strcmp(str, "info") == 0) { if (strcmp(str, "cli.info") == 0 || strcmp(str, "info") == 0) {
printf(clientId, "##CLI.INFO#\n"); printf(clientId, "##CLI.INFO#\n");
char timeStringBuff[50]; strftime(config.tmpBuf, sizeof(config.tmpBuf), "%Y-%m-%dT%H:%M:%S", &network.timeinfo);
strftime(timeStringBuff, sizeof(timeStringBuff), "%Y-%m-%dT%H:%M:%S", &network.timeinfo);
if (config.store.tzHour < 0) { if (config.store.tzHour < 0) {
printf(clientId, "##SYS.DATE#: %s%03d:%02d\n", timeStringBuff, config.store.tzHour, config.store.tzMin); printf(clientId, "##SYS.DATE#: %s%03d:%02d\n", config.tmpBuf, config.store.tzHour, config.store.tzMin);
} else { } else {
printf(clientId, "##SYS.DATE#: %s+%02d:%02d\n", timeStringBuff, config.store.tzHour, config.store.tzMin); printf(clientId, "##SYS.DATE#: %s+%02d:%02d\n", config.tmpBuf, config.store.tzHour, config.store.tzMin);
} }
printf(clientId, "##CLI.NAMESET#: %d %s\n", config.lastStation(), config.station.name); printf(clientId, "##CLI.NAMESET#: %d %s\n", config.lastStation(), config.station.name);
if (player.status() == PLAYING) { if (player.status() == PLAYING) {
@@ -406,11 +421,10 @@ void Telnet::on_input(const char* str, uint8_t clientId) {
printf(clientId, "#WIFI.CON#\n"); printf(clientId, "#WIFI.CON#\n");
File file = SPIFFS.open(SSIDS_PATH, "r"); File file = SPIFFS.open(SSIDS_PATH, "r");
if (file && !file.isDirectory()) { if (file && !file.isDirectory()) {
char sSid[BUFLEN], sPas[BUFLEN];
uint8_t c = 1; uint8_t c = 1;
while (file.available()) { while (file.available()) {
if (config.parseSsid(file.readStringUntil('\n').c_str(), sSid, sPas)) { if (config.parseSsid(file.readStringUntil('\n').c_str(), config.tmpBuf, config.tmpBuf2)) {
printf(clientId, "%d: %s, %s\n", c, sSid, sPas); printf(clientId, "%d: %s, %s\n", c, config.tmpBuf, config.tmpBuf2);
c++; c++;
} }
} }
@@ -422,11 +436,10 @@ void Telnet::on_input(const char* str, uint8_t clientId) {
printf(clientId, "#WIFI.STATION#\n"); printf(clientId, "#WIFI.STATION#\n");
File file = SPIFFS.open(SSIDS_PATH, "r"); File file = SPIFFS.open(SSIDS_PATH, "r");
if (file && !file.isDirectory()) { if (file && !file.isDirectory()) {
char sSid[BUFLEN], sPas[BUFLEN];
uint8_t c = 1; uint8_t c = 1;
while (file.available()) { while (file.available()) {
if (config.parseSsid(file.readStringUntil('\n').c_str(), sSid, sPas)) { if (config.parseSsid(file.readStringUntil('\n').c_str(), config.tmpBuf, config.tmpBuf2)) {
if(c==config.store.lastSSID) printf(clientId, "%d: %s, %s\n", c, sSid, sPas); if(c==config.store.lastSSID) printf(clientId, "%d: %s, %s\n", c, config.tmpBuf, config.tmpBuf2);
c++; c++;
} }
} }
@@ -434,23 +447,21 @@ void Telnet::on_input(const char* str, uint8_t clientId) {
printf(clientId, "##WIFI.STATION#\n> "); printf(clientId, "##WIFI.STATION#\n> ");
return; return;
} }
char newssid[30], newpass[40]; if (sscanf(str, "wifi.con(\"%[^\"]\",\"%[^\"]\")", config.tmpBuf, config.tmpBuf2) == 2 || sscanf(str, "wifi.con(%[^,],%[^)])", config.tmpBuf, config.tmpBuf2) == 2 || sscanf(str, "wifi.con(%[^ ] %[^)])", config.tmpBuf, config.tmpBuf2) == 2 || sscanf(str, "wifi %[^ ] %s", config.tmpBuf, config.tmpBuf2) == 2) {
if (sscanf(str, "wifi.con(\"%[^\"]\",\"%[^\"]\")", newssid, newpass) == 2 || sscanf(str, "wifi.con(%[^,],%[^)])", newssid, newpass) == 2 || sscanf(str, "wifi.con(%[^ ] %[^)])", newssid, newpass) == 2 || sscanf(str, "wifi %[^ ] %s", newssid, newpass) == 2) { snprintf(cmBuf, sizeof(cmBuf), "New SSID: \"%s\" with PASS: \"%s\" for next boot\n> ", config.tmpBuf, config.tmpBuf2);
char buf[BUFLEN]; printf(clientId, cmBuf);
snprintf(buf, BUFLEN, "New SSID: \"%s\" with PASS: \"%s\" for next boot\n> ", newssid, newpass);
printf(clientId, buf);
printf(clientId, "...REBOOTING...\n> "); printf(clientId, "...REBOOTING...\n> ");
memset(buf, 0, BUFLEN); memset(cmBuf, 0, sizeof(cmBuf));
snprintf(buf, BUFLEN, "%s\t%s", newssid, newpass); snprintf(cmBuf, sizeof(cmBuf), "%s\t%s", config.tmpBuf, config.tmpBuf2);
config.saveWifiFromNextion(buf); config.saveWifiFromNextion(cmBuf);
return; return;
} }
if (strcmp(str, "wifi.status") == 0 || strcmp(str, "status") == 0) { if (strcmp(str, "wifi.status") == 0 || strcmp(str, "status") == 0) {
printf(clientId, "#WIFI.STATUS#\nStatus:\t\t%d\nMode:\t\t%s\nIP:\t\t%s\nMask:\t\t%s\nGateway:\t%s\nRSSI:\t\t%d dBm\n##WIFI.STATUS#\n> ", printf(clientId, "#WIFI.STATUS#\nStatus:\t\t%d\nMode:\t\t%s\nIP:\t\t%s\nMask:\t\t%s\nGateway:\t%s\nRSSI:\t\t%d dBm\n##WIFI.STATUS#\n> ",
WiFi.status(), WiFi.getMode()==WIFI_STA?"WIFI_STA":"WIFI_AP", WiFi.status(), WiFi.getMode()==WIFI_STA?"WIFI_STA":"WIFI_AP",
WiFi.getMode()==WIFI_STA?WiFi.localIP().toString():WiFi.softAPIP().toString(), WiFi.getMode()==WIFI_STA?config.ipToStr(WiFi.localIP()):config.ipToStr(WiFi.softAPIP()),
WiFi.getMode()==WIFI_STA?WiFi.subnetMask().toString():"255.255.255.0", WiFi.getMode()==WIFI_STA?config.ipToStr(WiFi.subnetMask()):"255.255.255.0",
WiFi.getMode()==WIFI_STA?WiFi.gatewayIP().toString():WiFi.softAPIP().toString(), WiFi.getMode()==WIFI_STA?config.ipToStr(WiFi.gatewayIP()):config.ipToStr(WiFi.softAPIP()),
WiFi.RSSI() WiFi.RSSI()
); );
return; return;
@@ -460,12 +471,14 @@ void Telnet::on_input(const char* str, uint8_t clientId) {
return; return;
} }
if (strcmp(str, "sys.heap") == 0 || strcmp(str, "heap") == 0) { if (strcmp(str, "sys.heap") == 0 || strcmp(str, "heap") == 0) {
printf(clientId, "Free heap:\t%d bytes\n> ", xPortGetFreeHeapSize()); //printf(clientId, "Free heap:\t%d bytes\n> ", xPortGetFreeHeapSize());
printHeapFragmentationInfo(clientId);
return; return;
} }
if (strcmp(str, "sys.config") == 0 || strcmp(str, "config") == 0) { if (strcmp(str, "sys.config") == 0 || strcmp(str, "config") == 0) {
config.bootInfo(); config.bootInfo();
printf(clientId, "Free heap:\t%d bytes\n> ", xPortGetFreeHeapSize()); //printf(clientId, "Free heap:\t%d bytes\n> ", xPortGetFreeHeapSize());
printHeapFragmentationInfo(clientId);
return; return;
} }
if (strcmp(str, "wifi.discon") == 0 || strcmp(str, "discon") == 0 || strcmp(str, "disconnect") == 0) { if (strcmp(str, "wifi.discon") == 0 || strcmp(str, "discon") == 0 || strcmp(str, "disconnect") == 0) {

View File

@@ -4,14 +4,15 @@
#include <WiFi.h> #include <WiFi.h>
#define MAX_TLN_CLIENTS 5 #define MAX_TLN_CLIENTS 5
#define MAX_PRINTF_LEN BUFLEN+50
class Telnet { class Telnet {
public: public:
Telnet() {}; Telnet() {};
bool begin(bool quiet=false); bool begin(bool quiet=false);
void loop(); void loop();
void start();
void stop(); void stop();
void toggle();
void print(uint8_t id, const char *buf); void print(uint8_t id, const char *buf);
void print(const char *buf); void print(const char *buf);
void printf(uint8_t id, const char *format, ...); void printf(uint8_t id, const char *format, ...);
@@ -25,8 +26,10 @@ class Telnet {
void on_connect(const char* str, uint8_t clientId); void on_connect(const char* str, uint8_t clientId);
void on_input(const char* str, uint8_t clientId); void on_input(const char* str, uint8_t clientId);
private: private:
char cmBuf[220];
bool _isIPSet(IPAddress ip); bool _isIPSet(IPAddress ip);
void handleSerial(); void handleSerial();
void printHeapFragmentationInfo(uint8_t id);
}; };
extern Telnet telnet; extern Telnet telnet;

View File

@@ -0,0 +1,386 @@
#include "timekeeper.h"
#include "options.h"
#include "config.h"
#include "network.h"
#include "display.h"
#include "player.h"
#include "netserver.h"
#include "rtcsupport.h"
#if RTCSUPPORTED
//#define TIME_SYNC_INTERVAL 24*60*60*1000
#define TIME_SYNC_INTERVAL config.store.timeSyncIntervalRTC*60*60*1000
#else
#define TIME_SYNC_INTERVAL config.store.timeSyncInterval*60*1000
#endif
#define WEATHER_SYNC_INTERVAL config.store.weatherSyncInterval*60*1000
#define SYNC_STACK_SIZE 1024 * 4
#define SYNC_TASK_CORE 0
#define SYNC_TASK_PRIORITY 1
TimeKeeper timekeeper;
void _syncTask(void *pvParameters) {
if (timekeeper.forceWeather && timekeeper.forceTimeSync) {
timekeeper.weatherTask();
timekeeper.timeTask();
}
else if (timekeeper.forceWeather) {
timekeeper.weatherTask();
}
else if (timekeeper.forceTimeSync) {
timekeeper.timeTask();
}
timekeeper.busy = false;
vTaskDelete(NULL);
}
bool TimeKeeper::loop0(){ // core0 (display)
uint32_t currentTime = millis();
static uint32_t _last1s = 0;
static uint32_t _last2s = 0;
static uint32_t _last5s = 0;
if (currentTime - _last1s >= 1000) { // 1sec
_last1s = currentTime;
#ifndef DUMMYDISPLAY
#ifndef UPCLOCK_CORE1
_upClock();
#endif
#endif
}
if (currentTime - _last2s >= 2000) { // 2sec
_last2s = currentTime;
_upRSSI();
}
if (currentTime - _last5s >= 5000) { // 2sec
_last5s = currentTime;
//HEAP_INFO();
}
#ifdef DUMMYDISPLAY
return true;
#endif
static uint32_t lastWeatherTime = 0;
if (currentTime - lastWeatherTime >= WEATHER_SYNC_INTERVAL) {
lastWeatherTime = currentTime;
forceWeather = true;
}
static uint32_t lastTimeTime = 0;
if (currentTime - lastTimeTime >= TIME_SYNC_INTERVAL) {
lastTimeTime = currentTime;
forceTimeSync = true;
}
if (!busy && (forceWeather || forceTimeSync) && network.status == CONNECTED) {
busy = true;
//config.setTimeConf();
xTaskCreatePinnedToCore(
_syncTask,
"syncTask",
SYNC_STACK_SIZE,
NULL, // Params
SYNC_TASK_PRIORITY,
NULL, // Descriptor
SYNC_TASK_CORE
);
}
return true; // just in case
}
bool TimeKeeper::loop1(){ // core1 (player)
uint32_t currentTime = millis();
static uint32_t _last1s = 0;
static uint32_t _last2s = 0;
if (currentTime - _last1s >= 1000) { // 1sec
pm.on_ticker();
_last1s = currentTime;
#ifndef DUMMYDISPLAY
#ifdef UPCLOCK_CORE1
_upClock();
#endif
#endif
_upScreensaver();
_upSDPos();
_returnPlayer();
_doAfterWait();
}
if (currentTime - _last2s >= 2000) { // 2sec
_last2s = currentTime;
}
return true; // just in case
}
void TimeKeeper::waitAndReturnPlayer(uint8_t time_s){
_returnPlayerTime = millis()+time_s*1000;
}
void TimeKeeper::_returnPlayer(){
if(_returnPlayerTime>0 && millis()>=_returnPlayerTime){
_returnPlayerTime = 0;
display.putRequest(NEWMODE, PLAYER);
}
}
void TimeKeeper::waitAndDo(uint8_t time_s, void (*callback)()){
_doAfterTime = millis()+time_s*1000;
_aftercallback = callback;
}
void TimeKeeper::_doAfterWait(){
if(_doAfterTime>0 && millis()>=_doAfterTime){
_doAfterTime = 0;
_aftercallback();
}
}
void TimeKeeper::_upClock(){
#if RTCSUPPORTED
if(config.isRTCFound()){
rtc.getTime(&network.timeinfo);
mktime(&network.timeinfo);
if(display.ready()) display.putRequest(CLOCK);
}
#else
if(network.timeinfo.tm_year>100 || network.status == SDREADY) {
network.timeinfo.tm_sec++;
mktime(&network.timeinfo);
if(display.ready()) display.putRequest(CLOCK);
}
#endif
}
void TimeKeeper::_upScreensaver(){
#ifndef DSP_LCD
if(!display.ready()) return;
if(config.store.screensaverEnabled && display.mode()==PLAYER && !player.isRunning()){
config.screensaverTicks++;
if(config.screensaverTicks > config.store.screensaverTimeout+SCREENSAVERSTARTUPDELAY){
if(config.store.screensaverBlank){
display.putRequest(NEWMODE, SCREENBLANK);
}else{
display.putRequest(NEWMODE, SCREENSAVER);
}
}
}
if(config.store.screensaverPlayingEnabled && display.mode()==PLAYER && player.isRunning()){
config.screensaverPlayingTicks++;
if(config.screensaverPlayingTicks > config.store.screensaverPlayingTimeout*60+SCREENSAVERSTARTUPDELAY){
if(config.store.screensaverPlayingBlank){
display.putRequest(NEWMODE, SCREENBLANK);
}else{
display.putRequest(NEWMODE, SCREENSAVER);
}
}
}
#endif
}
void TimeKeeper::_upRSSI(){
if(network.status == CONNECTED){
netserver.setRSSI(WiFi.RSSI());
netserver.requestOnChange(NRSSI, 0);
if(display.ready()) display.putRequest(DSPRSSI, netserver.getRSSI());
}
#ifdef USE_SD
if(display.mode()!=SDCHANGE) player.sendCommand({PR_CHECKSD, 0});
#endif
player.sendCommand({PR_VUTONUS, 0});
}
void TimeKeeper::_upSDPos(){
if(player.isRunning() && config.getMode()==PM_SDCARD) netserver.requestOnChange(SDPOS, 0);
}
void TimeKeeper::timeTask(){
static uint8_t tsFailCnt = 0;
if(getLocalTime(&network.timeinfo)){
tsFailCnt = 0;
forceTimeSync = false;
mktime(&network.timeinfo);
display.putRequest(CLOCK);
network.requestTimeSync(true);
#if RTCSUPPORTED
if (config.isRTCFound()) rtc.setTime(&network.timeinfo);
#endif
}else{
if(tsFailCnt<4){
forceTimeSync = true;
tsFailCnt++;
}else{
forceTimeSync = false;
tsFailCnt=0;
}
}
}
void TimeKeeper::weatherTask(){
if(!network.weatherBuf || strlen(config.store.weatherkey)==0 || !config.store.showweather) return;
forceWeather = false;
_getWeather(network.weatherBuf);
}
bool _getWeather(char *wstr) {
#if (DSP_MODEL!=DSP_DUMMY || defined(USE_NEXTION)) && !defined(HIDE_WEATHER)
WiFiClient client;
const char* host = "api.openweathermap.org";
if (!client.connect(host, 80)) {
Serial.println("##WEATHER###: connection failed");
return false;
}
char httpget[250] = {0};
sprintf(httpget, "GET /data/2.5/weather?lat=%s&lon=%s&units=%s&lang=%s&appid=%s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", config.store.weatherlat, config.store.weatherlon, weatherUnits, weatherLang, config.store.weatherkey, host);
client.print(httpget);
unsigned long timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 2000UL) {
Serial.println("##WEATHER###: client available timeout !");
client.stop();
return false;
}
}
timeout = millis();
String line = "";
if (client.connected()) {
while (client.available())
{
line = client.readStringUntil('\n');
if (strstr(line.c_str(), "\"temp\"") != NULL) {
client.stop();
break;
}
if ((millis() - timeout) > 500)
{
client.stop();
Serial.println("##WEATHER###: client read timeout !");
return false;
}
}
}
if (strstr(line.c_str(), "\"temp\"") == NULL) {
Serial.println("##WEATHER###: weather not found !");
return false;
}
char *tmpe;
char *tmps;
char *tmpc;
const char* cursor = line.c_str();
char desc[120], temp[20], hum[20], press[20], icon[5];
tmps = strstr(cursor, "\"description\":\"");
if (tmps == NULL) { Serial.println("##WEATHER###: description not found !"); return false;}
tmps += 15;
tmpe = strstr(tmps, "\",\"");
if (tmpe == NULL) { Serial.println("##WEATHER###: description not found !"); return false;}
strlcpy(desc, tmps, tmpe - tmps + 1);
cursor = tmpe + 2;
// "ясно","icon":"01d"}],
tmps = strstr(cursor, "\"icon\":\"");
if (tmps == NULL) { Serial.println("##WEATHER###: icon not found !"); return false;}
tmps += 8;
tmpe = strstr(tmps, "\"}");
if (tmpe == NULL) { Serial.println("##WEATHER###: icon not found !"); return false;}
strlcpy(icon, tmps, tmpe - tmps + 1);
cursor = tmpe + 2;
tmps = strstr(cursor, "\"temp\":");
if (tmps == NULL) { Serial.println("##WEATHER###: temp not found !"); return false;}
tmps += 7;
tmpe = strstr(tmps, ",\"");
if (tmpe == NULL) { Serial.println("##WEATHER###: temp not found !"); return false;}
strlcpy(temp, tmps, tmpe - tmps + 1);
cursor = tmpe + 1;
float tempf = atof(temp);
tmps = strstr(cursor, "\"feels_like\":");
if (tmps == NULL) { Serial.println("##WEATHER###: feels_like not found !"); return false;}
tmps += 13;
tmpe = strstr(tmps, ",\"");
if (tmpe == NULL) { Serial.println("##WEATHER###: feels_like not found !"); return false;}
strlcpy(temp, tmps, tmpe - tmps + 1);
cursor = tmpe + 2;
float tempfl = atof(temp); (void)tempfl;
tmps = strstr(cursor, "\"pressure\":");
if (tmps == NULL) { Serial.println("##WEATHER###: pressure not found !"); return false;}
tmps += 11;
tmpe = strstr(tmps, ",\"");
if (tmpe == NULL) { Serial.println("##WEATHER###: pressure not found !"); return false;}
strlcpy(press, tmps, tmpe - tmps + 1);
cursor = tmpe + 2;
int pressi = (float)atoi(press) / 1.333;
tmps = strstr(cursor, "humidity\":");
if (tmps == NULL) { Serial.println("##WEATHER###: humidity not found !"); return false;}
tmps += 10;
tmpe = strstr(tmps, ",\"");
tmpc = strstr(tmps, "}");
if (tmpe == NULL) { Serial.println("##WEATHER###: humidity not found !"); return false;}
strlcpy(hum, tmps, tmpe - tmps + (tmpc>tmpe?1:0));
tmps = strstr(cursor, "\"grnd_level\":");
bool grnd_level_pr = (tmps != NULL);
if(grnd_level_pr){
tmps += 13;
tmpe = strstr(tmps, ",\"");
if (tmpe == NULL) { Serial.println("##WEATHER###: grnd_level not found !"); return false;}
strlcpy(press, tmps, tmpe - tmps + 1);
cursor = tmpe + 2;
pressi = (float)atoi(press) / 1.333;
}
tmps = strstr(cursor, "\"speed\":");
if (tmps == NULL) { Serial.println("##WEATHER###: wind speed not found !"); return false;}
tmps += 8;
tmpe = strstr(tmps, ",\"");
if (tmpe == NULL) { Serial.println("##WEATHER###: wind speed not found !"); return false;}
strlcpy(temp, tmps, tmpe - tmps + 1);
cursor = tmpe + 1;
float wind_speed = atof(temp); (void)wind_speed;
tmps = strstr(cursor, "\"deg\":");
if (tmps == NULL) { Serial.println("##WEATHER###: wind deg not found !"); return false;}
tmps += 6;
tmpe = strstr(tmps, ",\"");
if (tmpe == NULL) { Serial.println("##WEATHER###: wind deg not found !"); return false;}
strlcpy(temp, tmps, tmpe - tmps + 1);
cursor = tmpe + 1;
int wind_deg = atof(temp)/22.5;
if(wind_deg<0) wind_deg = 16+wind_deg;
#ifdef USE_NEXTION
nextion.putcmdf("press_txt.txt=\"%dmm\"", pressi);
nextion.putcmdf("hum_txt.txt=\"%d%%\"", atoi(hum));
char cmd[30];
snprintf(cmd, sizeof(cmd)-1,"temp_txt.txt=\"%.1f\"", tempf);
nextion.putcmd(cmd);
int iconofset;
if(strstr(icon,"01")!=NULL) iconofset = 0;
else if(strstr(icon,"02")!=NULL) iconofset = 1;
else if(strstr(icon,"03")!=NULL) iconofset = 2;
else if(strstr(icon,"04")!=NULL) iconofset = 3;
else if(strstr(icon,"09")!=NULL) iconofset = 4;
else if(strstr(icon,"10")!=NULL) iconofset = 5;
else if(strstr(icon,"11")!=NULL) iconofset = 6;
else if(strstr(icon,"13")!=NULL) iconofset = 7;
else if(strstr(icon,"50")!=NULL) iconofset = 8;
else iconofset = 9;
nextion.putcmd("cond_img.pic", 50+iconofset);
nextion.weatherVisible(1);
#endif
Serial.printf("##WEATHER###: description: %s, temp:%.1f C, pressure:%dmmHg, humidity:%s%%\n", desc, tempf, pressi, hum);
#ifdef WEATHER_FMT_SHORT
sprintf(wstr, weatherFmt, tempf, pressi, hum);
#else
#if EXT_WEATHER
sprintf(wstr, weatherFmt, desc, tempf, tempfl, pressi, hum, wind_speed, wind[wind_deg]);
#else
sprintf(wstr, weatherFmt, desc, tempf, pressi, hum);
#endif
#endif
display.putRequest(NEWWEATHER);
return true;
#endif // if (DSP_MODEL!=DSP_DUMMY || defined(USE_NEXTION)) && !defined(HIDE_WEATHER)
return false;
}
//******************

View File

@@ -0,0 +1,42 @@
#ifndef timekeeper_h
#define timekeeper_h
#include "Arduino.h"
void _syncTask(void * pvParameters);
bool _getWeather(char *wstr);
class TimeKeeper {
public:
volatile bool forceWeather;
volatile bool forceTimeSync;
volatile bool busy;
public:
TimeKeeper() {
busy = false;
forceWeather = true;
forceTimeSync = true;
_returnPlayerTime = _doAfterTime = 0;
}
bool loop0();
bool loop1();
void timeTask();
void weatherTask();
void waitAndReturnPlayer(uint8_t time_s);
void waitAndDo(uint8_t time_s, void (*callback)());
private:
uint32_t _returnPlayerTime, _doAfterTime;
void (*_aftercallback)();
void (*_watchdogcallback)();
void _upRSSI();
void _upSDPos();
void _upClock();
void _upScreensaver();
void _returnPlayer();
void _doAfterWait();
void _doWatchDog();
};
extern TimeKeeper timekeeper;
#endif

View File

@@ -13,7 +13,7 @@ void Pager::loop(){
} }
Page& Pager::addPage(Page* page, bool setNow){ Page& Pager::addPage(Page* page, bool setNow){
_pages.add(page); _pages.push_back(page);
if(setNow) setPage(page); if(setNow) setPage(page);
return *page; return *page;
} }
@@ -21,7 +21,15 @@ Page& Pager::addPage(Page* page, bool setNow){
bool Pager::removePage(Page* page){ bool Pager::removePage(Page* page){
page->setActive(false); page->setActive(false);
dsp.clearDsp(); dsp.clearDsp();
return _pages.remove(page); auto i = std::find_if(_pages.begin(), _pages.end(), [&page](const Page* pn){ return page == pn; });
if (i != _pages.end()){
delete (*i);
(*i) = nullptr;
_pages.erase(i);
return true;
}
return false;
//return _pages.remove(page);
} }
void Pager::setPage(Page* page, bool black){ void Pager::setPage(Page* page, bool black){
@@ -33,12 +41,13 @@ void Pager::setPage(Page* page, bool black){
/*******************************************************/ /*******************************************************/
Page::Page() : _widgets(LinkedList<Widget * >([](Widget * wd) { delete wd;})), _pages(LinkedList<Page*>([](Page* pg){ delete pg; })) { //Page::Page() : _widgets(LinkedList<Widget * >([](Widget * wd) { delete wd;})), _pages(LinkedList<Page*>([](Page* pg){ delete pg; })) {
_active = false; // _active = false;
} //}
Page::~Page() { Page::~Page() {
for (const auto& w : _widgets) removeWidget(w); for (const auto& w : _widgets) removeWidget(w);
// what about deleting _pages ???
} }
void Page::loop() { void Page::loop() {
@@ -46,23 +55,40 @@ void Page::loop() {
} }
Widget& Page::addWidget(Widget* widget) { Widget& Page::addWidget(Widget* widget) {
_widgets.add(widget); _widgets.push_back(widget);
widget->setActive(_active, _active); widget->setActive(_active, _active);
return *widget; return *widget;
} }
bool Page::removeWidget(Widget* widget){ bool Page::removeWidget(Widget* widget){
widget->setActive(false, _active); widget->setActive(false, _active);
return _widgets.remove(widget); auto i = std::find_if(_widgets.begin(), _widgets.end(), [&widget](const Widget* wn){ return widget == wn; });
if (i != _widgets.end()){
delete (*i);
(*i) = nullptr;
_widgets.erase(i);
return true;
}
return false;
//return _widgets.remove(widget);
} }
Page& Page::addPage(Page* page){ Page& Page::addPage(Page* page){
_pages.add(page); _pages.push_back(page);
return *page; return *page;
} }
bool Page::removePage(Page* page){ bool Page::removePage(Page* page){
return _pages.remove(page); auto i = std::find_if(_pages.begin(), _pages.end(), [&page](const Page* pn){ return page == pn; });
if (i != _pages.end()){
delete (*i);
(*i) = nullptr;
_pages.erase(i);
return true;
}
return false;
// return _pages.remove(page);
} }
void Page::setActive(bool act) { void Page::setActive(bool act) {

View File

@@ -1,16 +1,16 @@
#ifndef pages_h #ifndef pages_h
#define pages_h #define pages_h
#include "Arduino.h" #include <list>
#include "../../AsyncWebServer/StringArray.h"
class Page { class Page {
protected: protected:
LinkedList<Widget*> _widgets; std::list<Widget*> _widgets;
LinkedList<Page*> _pages; std::list<Page*> _pages;
bool _active; bool _active;
public: public:
Page(); //Page();
~Page(); ~Page();
void loop(); void loop();
Widget& addWidget(Widget* widget); Widget& addWidget(Widget* widget);
@@ -23,14 +23,14 @@ class Page {
class Pager{ class Pager{
public: public:
Pager() : _pages(LinkedList<Page*>([](Page* pg){ delete pg; })) {} //Pager() : _pages(std::list<Page*>([](Page* pg){ delete pg; })) {}
void begin(); void begin();
void loop(); void loop();
Page& addPage(Page* page, bool setNow = false); Page& addPage(Page* page, bool setNow = false);
bool removePage(Page* page); bool removePage(Page* page);
void setPage(Page* page, bool black=false); void setPage(Page* page, bool black=false);
private: private:
LinkedList<Page*> _pages; std::list<Page*> _pages;
}; };

View File

@@ -119,6 +119,7 @@ class TextWidget: public Widget {
TextWidget() {} TextWidget() {}
TextWidget(WidgetConfig wconf, uint16_t buffsize, bool uppercase, uint16_t fgcolor, uint16_t bgcolor) { init(wconf, buffsize, uppercase, fgcolor, bgcolor); } TextWidget(WidgetConfig wconf, uint16_t buffsize, bool uppercase, uint16_t fgcolor, uint16_t bgcolor) { init(wconf, buffsize, uppercase, fgcolor, bgcolor); }
~TextWidget(); ~TextWidget();
using Widget::init;
void init(WidgetConfig wconf, uint16_t buffsize, bool uppercase, uint16_t fgcolor, uint16_t bgcolor); void init(WidgetConfig wconf, uint16_t buffsize, bool uppercase, uint16_t fgcolor, uint16_t bgcolor);
void setText(const char* txt); void setText(const char* txt);
void setText(int val, const char *format); void setText(int val, const char *format);
@@ -139,6 +140,7 @@ class FillWidget: public Widget {
public: public:
FillWidget() {} FillWidget() {}
FillWidget(FillConfig conf, uint16_t bgcolor) { init(conf, bgcolor); } FillWidget(FillConfig conf, uint16_t bgcolor) { init(conf, bgcolor); }
using Widget::init;
void init(FillConfig conf, uint16_t bgcolor); void init(FillConfig conf, uint16_t bgcolor);
void setHeight(uint16_t newHeight); void setHeight(uint16_t newHeight);
protected: protected:
@@ -151,6 +153,7 @@ class ScrollWidget: public TextWidget {
ScrollWidget(){} ScrollWidget(){}
ScrollWidget(const char* separator, ScrollConfig conf, uint16_t fgcolor, uint16_t bgcolor); ScrollWidget(const char* separator, ScrollConfig conf, uint16_t fgcolor, uint16_t bgcolor);
~ScrollWidget(); ~ScrollWidget();
using Widget::init;
void init(const char* separator, ScrollConfig conf, uint16_t fgcolor, uint16_t bgcolor); void init(const char* separator, ScrollConfig conf, uint16_t fgcolor, uint16_t bgcolor);
void loop(); void loop();
void setText(const char* txt); void setText(const char* txt);
@@ -182,6 +185,7 @@ class SliderWidget: public Widget {
SliderWidget(FillConfig conf, uint16_t fgcolor, uint16_t bgcolor, uint32_t maxval, uint16_t oucolor=0){ SliderWidget(FillConfig conf, uint16_t fgcolor, uint16_t bgcolor, uint32_t maxval, uint16_t oucolor=0){
init(conf, fgcolor, bgcolor, maxval, oucolor); init(conf, fgcolor, bgcolor, maxval, oucolor);
} }
using Widget::init;
void init(FillConfig conf, uint16_t fgcolor, uint16_t bgcolor, uint32_t maxval, uint16_t oucolor=0); void init(FillConfig conf, uint16_t fgcolor, uint16_t bgcolor, uint32_t maxval, uint16_t oucolor=0);
void setValue(uint32_t val); void setValue(uint32_t val);
protected: protected:
@@ -199,6 +203,7 @@ class VuWidget: public Widget {
VuWidget() {} VuWidget() {}
VuWidget(WidgetConfig wconf, VUBandsConfig bands, uint16_t vumaxcolor, uint16_t vumincolor, uint16_t bgcolor) { init(wconf, bands, vumaxcolor, vumincolor, bgcolor); } VuWidget(WidgetConfig wconf, VUBandsConfig bands, uint16_t vumaxcolor, uint16_t vumincolor, uint16_t bgcolor) { init(wconf, bands, vumaxcolor, vumincolor, bgcolor); }
~VuWidget(); ~VuWidget();
using Widget::init;
void init(WidgetConfig wconf, VUBandsConfig bands, uint16_t vumaxcolor, uint16_t vumincolor, uint16_t bgcolor); void init(WidgetConfig wconf, VUBandsConfig bands, uint16_t vumaxcolor, uint16_t vumincolor, uint16_t bgcolor);
void loop(); void loop();
protected: protected:
@@ -213,6 +218,7 @@ class VuWidget: public Widget {
class NumWidget: public TextWidget { class NumWidget: public TextWidget {
public: public:
using Widget::init;
void init(WidgetConfig wconf, uint16_t buffsize, bool uppercase, uint16_t fgcolor, uint16_t bgcolor); void init(WidgetConfig wconf, uint16_t buffsize, bool uppercase, uint16_t fgcolor, uint16_t bgcolor);
void setText(const char* txt); void setText(const char* txt);
void setText(int val, const char *format); void setText(int val, const char *format);
@@ -227,6 +233,7 @@ class ProgressWidget: public TextWidget {
ProgressWidget(WidgetConfig conf, ProgressConfig pconf, uint16_t fgcolor, uint16_t bgcolor) { ProgressWidget(WidgetConfig conf, ProgressConfig pconf, uint16_t fgcolor, uint16_t bgcolor) {
init(conf, pconf, fgcolor, bgcolor); init(conf, pconf, fgcolor, bgcolor);
} }
using Widget::init;
void init(WidgetConfig conf, ProgressConfig pconf, uint16_t fgcolor, uint16_t bgcolor){ void init(WidgetConfig conf, ProgressConfig pconf, uint16_t fgcolor, uint16_t bgcolor){
TextWidget::init(conf, pconf.width, false, fgcolor, bgcolor); TextWidget::init(conf, pconf.width, false, fgcolor, bgcolor);
_speed = pconf.speed; _width = pconf.width; _barwidth = pconf.barwidth; _speed = pconf.speed; _width = pconf.width; _barwidth = pconf.barwidth;
@@ -254,6 +261,7 @@ class BitrateWidget: public Widget {
BitrateWidget() {} BitrateWidget() {}
BitrateWidget(BitrateConfig bconf, uint16_t fgcolor, uint16_t bgcolor) { init(bconf, fgcolor, bgcolor); } BitrateWidget(BitrateConfig bconf, uint16_t fgcolor, uint16_t bgcolor) { init(bconf, fgcolor, bgcolor); }
~BitrateWidget(){} ~BitrateWidget(){}
using Widget::init;
void init(BitrateConfig bconf, uint16_t fgcolor, uint16_t bgcolor); void init(BitrateConfig bconf, uint16_t fgcolor, uint16_t bgcolor);
void setBitrate(uint16_t bitrate); void setBitrate(uint16_t bitrate);
void setFormat(BitrateFormat format); void setFormat(BitrateFormat format);

View File

@@ -9,6 +9,16 @@
#include "core/controls.h" #include "core/controls.h"
#include "core/mqtt.h" #include "core/mqtt.h"
#include "core/optionschecker.h" #include "core/optionschecker.h"
#include "core/timekeeper.h"
#if USE_OTA
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
#include <NetworkUdp.h>
#else
#include <WiFiUdp.h>
#endif
#include <ArduinoOTA.h>
#endif
#if DSP_HSPI || TS_HSPI || VS_HSPI #if DSP_HSPI || TS_HSPI || VS_HSPI
SPIClass SPI2(HOOPSENb); SPIClass SPI2(HOOPSENb);
@@ -16,6 +26,44 @@ SPIClass SPI2(HOOPSENb);
extern __attribute__((weak)) void yoradio_on_setup(); extern __attribute__((weak)) void yoradio_on_setup();
#if USE_OTA
void setupOTA(){
if(strlen(config.store.mdnsname)>0)
ArduinoOTA.setHostname(config.store.mdnsname);
#ifdef OTA_PASS
ArduinoOTA.setPassword(OTA_PASS);
#endif
ArduinoOTA
.onStart([]() {
player.sendCommand({PR_STOP, 0});
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");
ESP.restart();
})
.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
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);
if(REAL_LEDBUILTIN!=255) pinMode(REAL_LEDBUILTIN, OUTPUT); if(REAL_LEDBUILTIN!=255) pinMode(REAL_LEDBUILTIN, OUTPUT);
@@ -45,23 +93,29 @@ void setup() {
#ifdef MQTT_ROOT_TOPIC #ifdef MQTT_ROOT_TOPIC
mqttInit(); mqttInit();
#endif #endif
#if USE_OTA
setupOTA();
#endif
if (config.getMode()==PM_SDCARD) player.initHeaders(config.station.url); if (config.getMode()==PM_SDCARD) player.initHeaders(config.station.url);
player.lockOutput=false; player.lockOutput=false;
if (config.store.smartstart == 1) { if (config.store.smartstart == 1) {
delay(99); delay(250);
player.sendCommand({PR_PLAY, config.lastStation()}); player.sendCommand({PR_PLAY, config.lastStation()});
} }
pm.on_end_setup(); pm.on_end_setup();
} }
void loop() { void loop() {
timekeeper.loop1();
telnet.loop(); telnet.loop();
if (network.status == CONNECTED || network.status==SDREADY) { if (network.status == CONNECTED || network.status==SDREADY) {
player.loop(); player.loop();
//loopControls(); #if USE_OTA
ArduinoOTA.handle();
#endif
} }
loopControls(); loopControls();
netserver.loop(); //netserver.loop();
} }
#include "core/audiohandlers.h" #include "core/audiohandlers.h"