This commit is contained in:
e2002
2025-07-30 09:42:04 +03:00
parent f320c021b2
commit 53cddf3c1d
13 changed files with 137 additions and 186 deletions

View File

@@ -234,6 +234,11 @@ Work is in progress...
---
## Version history
### 0.9.555
- fixed error "assert failed: udp_new_ip_type /IDF/components/lwip/lwip/src/core/udp.c:1278 (Required to lock TCPIP core functionality!)"\
part #2
- weather synchronization code rewritten
### 0.9.553
- fix "No 'Access-Control-Allow-Origin' header is present on the requested resource" on saving playlist\
just reupload the file `script.js.gz` with Webboard uploader

View File

@@ -72,9 +72,9 @@ const char apPassTxt[] PROGMEM = "PASSWORD";
const char bootstrFmt[] PROGMEM = "Trying to %s";
const char apSettFmt[] PROGMEM = "SETTINGS PAGE ON: HTTP://%s/";
#if EXT_WEATHER
const char weatherFmt[] PROGMEM = "%s, %.1f\011C \007 feels like: %.1f\011C \007 pressure: %d мм \007 humidity: %s%% \007 wind: %.1f m/s [%s]";
const char weatherFmt[] PROGMEM = "%s, %.1f\011C \007 feels like: %.1f\011C \007 pressure: %d мм \007 humidity: %d%% \007 wind: %.1f m/s [%s]";
#else
const char weatherFmt[] PROGMEM = "%s, %.1f\011C \007 pressure: %d mm \007 humidity: %s%%";
const char weatherFmt[] PROGMEM = "%s, %.1f\011C \007 pressure: %d mm \007 humidity: %d%%";
#endif
const char weatherUnits[] PROGMEM = "metric"; /* standard, metric, imperial */
const char weatherLang[] PROGMEM = "en"; /* https://openweathermap.org/current#multi */

View File

@@ -72,9 +72,9 @@ const char apPassTxt[] PROGMEM = "ПАРОЛЬ";
const char bootstrFmt[] PROGMEM = "Соединяюсь с %s";
const char apSettFmt[] PROGMEM = "НАСТРОЙКИ: HTTP://%s/";
#if EXT_WEATHER
const char weatherFmt[] PROGMEM = "%s, %.1f\011C \007 ощущается: %.1f\011C \007 давление: %d мм \007 влажность: %s%% \007 ветер: %.1f м/с [%s]";
const char weatherFmt[] PROGMEM = "%s, %.1f\011C \007 ощущается: %.1f\011C \007 давление: %d мм \007 влажность: %d%% \007 ветер: %.1f м/с [%s]";
#else
const char weatherFmt[] PROGMEM = "%s, %.1f\011C \007 давление: %d mm \007 влажность: %s%%";
const char weatherFmt[] PROGMEM = "%s, %.1f\011C \007 давление: %d mm \007 влажность: %d%%";
#endif
const char weatherUnits[] PROGMEM = "metric"; /* standard, metric, imperial */
const char weatherLang[] PROGMEM = "ru"; /* https://openweathermap.org/current#multi */

View File

@@ -451,7 +451,7 @@ void Display::loop() {
break;
}
case NEWWEATHER: {
if(_weather && network.weatherBuf) _weather->setText(network.weatherBuf);
if(_weather && timekeeper.weatherBuf) _weather->setText(timekeeper.weatherBuf);
break;
}
case BOOTSTRING: {

View File

@@ -12,6 +12,7 @@ TimerHandle_t mqttReconnectTimer;
char topic[100], status[BUFLEN+50];
void connectToMqtt() {
while(!player.connproc) vTaskDelay(50);
mqttClient.connect();
}

View File

@@ -125,7 +125,7 @@ void NetServer::chunkedHtmlPage(const String& contentType, AsyncWebServerRequest
#endif
#ifndef NS_QUEUE_TICKS
#define NS_QUEUE_TICKS 0
#define NS_QUEUE_TICKS 2
#endif
const char *getFormat(BitrateFormat _format) {

View File

@@ -153,11 +153,6 @@ void MyNetwork::setWifiParams(){
WiFi.setSleep(false);
WiFi.onEvent(WiFiReconnected, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP);
WiFi.onEvent(WiFiLostConnection, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED);
weatherBuf=NULL;
#if (DSP_MODEL!=DSP_DUMMY || defined(USE_NEXTION)) && !defined(HIDE_WEATHER)
weatherBuf = (char *) malloc(sizeof(char) * WEATHER_STRING_L);
memset(weatherBuf, 0, WEATHER_STRING_L);
#endif
//config.setTimeConf(); //??
}

View File

@@ -6,9 +6,6 @@
#define apSsid "yoRadioAP"
#define apPassword "12345987"
//#define TSYNC_DELAY 10800000 // 1000*60*60*3 = 3 hours
#define TSYNC_DELAY 3600000 // 1000*60*60 = 1 hour
#define WEATHER_STRING_L 254
enum n_Status_e { CONNECTED, SOFT_AP, FAILED, SDREADY };
@@ -17,9 +14,6 @@ class MyNetwork {
n_Status_e status;
struct tm timeinfo;
bool lostPlaying = false, beginReconnect = false;
//uint8_t tsFailCnt, wsFailCnt;
char *weatherBuf;
//bool trueWeather;
public:
MyNetwork() {};
void begin();

View File

@@ -1,7 +1,7 @@
#ifndef options_h
#define options_h
#define YOVERSION "0.9.553"
#define YOVERSION "0.9.555"
/*******************************************************
DO NOT EDIT THIS FILE.

View File

@@ -226,7 +226,9 @@ void Player::_play(uint16_t stationId) {
}else {
config.saveValue(&config.store.play_mode, static_cast<uint8_t>(PM_WEB));
}
connproc = false;
if(config.getMode()==PM_WEB) isConnected=connecttohost(config.station.url);
connproc = true;
if(isConnected){
_status = PLAYING;
config.configPostPlaying(stationId);

View File

@@ -44,6 +44,7 @@ class Player: public Audio {
public:
bool lockOutput = true;
bool resumeAfterUrl = false;
volatile bool connproc = true;
uint32_t sd_min, sd_max;
#ifdef MQTT_ROOT_TOPIC
char burl[MQTT_BURL_SIZE]; /* buffer for browseUrl */

View File

@@ -23,8 +23,8 @@ TimeKeeper timekeeper;
void _syncTask(void *pvParameters) {
if (timekeeper.forceWeather && timekeeper.forceTimeSync) {
timekeeper.weatherTask();
timekeeper.timeTask();
timekeeper.weatherTask();
}
else if (timekeeper.forceWeather) {
timekeeper.weatherTask();
@@ -36,6 +36,18 @@ void _syncTask(void *pvParameters) {
vTaskDelete(NULL);
}
TimeKeeper::TimeKeeper(){
busy = false;
forceWeather = true;
forceTimeSync = true;
_returnPlayerTime = _doAfterTime = 0;
weatherBuf=NULL;
#if (DSP_MODEL!=DSP_DUMMY || defined(USE_NEXTION)) && !defined(HIDE_WEATHER)
weatherBuf = (char *) malloc(sizeof(char) * WEATHER_STRING_L);
memset(weatherBuf, 0, WEATHER_STRING_L);
#endif
}
bool TimeKeeper::loop0(){ // core0 (display)
uint32_t currentTime = millis();
static uint32_t _last1s = 0;
@@ -214,173 +226,116 @@ void TimeKeeper::timeTask(){
}
}
void TimeKeeper::weatherTask(){
if(!network.weatherBuf || strlen(config.store.weatherkey)==0 || !config.store.showweather) return;
if(!weatherBuf || strlen(config.store.weatherkey)==0 || !config.store.showweather) return;
forceWeather = false;
_getWeather(network.weatherBuf);
_getWeather();
}
bool _getWeather(char *wstr) {
bool _getWeather() {
#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;
static AsyncClient * weatherClient = NULL;
static const char* host = "api.openweathermap.org";
if(weatherClient) return false;
weatherClient = new AsyncClient();
if(!weatherClient) return false;
weatherClient->onError([](void * arg, AsyncClient * client, int error){
Serial.println("##WEATHER###: connection error");
weatherClient = NULL;
delete client;
}, NULL);
weatherClient->onConnect([](void * arg, AsyncClient * client){
weatherClient->onError(NULL, NULL);
weatherClient->onDisconnect([](void * arg, AsyncClient * c){ weatherClient = NULL; delete c; }, NULL);
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->write(httpget);
client->onData([](void * arg, AsyncClient * c, void * data, size_t len){
uint8_t * d = (uint8_t*)data;
const char *bodyStart = strstr((const char*)d, "\r\n\r\n");
if (bodyStart != NULL) {
bodyStart += 4;
size_t bodyLen = len - (bodyStart - (const char*)d);
char line[bodyLen+1];
memcpy(line, bodyStart, bodyLen);
line[bodyLen] = '\0';
/* parse it */
char *cursor;
char desc[120], icon[5];
float tempf, tempfl, wind_speed;
int hum, press, wind_deg;
bool result = true;
cursor = strstr(line, "\"description\":\"");
if (cursor) { sscanf(cursor, "\"description\":\"%119[^\"]", desc); }else{ Serial.println("##WEATHER###: description not found !"); result=false; }
cursor = strstr(line, "\"icon\":\"");
if (cursor) { sscanf(cursor, "\"icon\":\"%4[^\"]", icon); }else{ Serial.println("##WEATHER###: icon not found !"); result=false; }
cursor = strstr(line, "\"temp\":");
if (cursor) { sscanf(cursor, "\"temp\":%f", &tempf); }else{ Serial.println("##WEATHER###: temp not found !"); result=false; }
cursor = strstr(line, "\"pressure\":");
if (cursor) { sscanf(cursor, "\"pressure\":%d", &press); }else{ Serial.println("##WEATHER###: pressure not found !"); result=false; }
cursor = strstr(line, "\"humidity\":");
if (cursor) { sscanf(cursor, "\"humidity\":%d", &hum); }else{ Serial.println("##WEATHER###: humidity not found !"); result=false; }
cursor = strstr(line, "\"feels_like\":");
if (cursor) { sscanf(cursor, "\"feels_like\":%f", &tempfl); }else{ Serial.println("##WEATHER###: feels_like not found !"); result=false; }
cursor = strstr(line, "\"grnd_level\":");
if (cursor) { sscanf(cursor, "\"grnd_level\":%d", &press); }
cursor = strstr(line, "\"speed\":");
if (cursor) { sscanf(cursor, "\"speed\":%f", &wind_speed); }else{ Serial.println("##WEATHER###: wind speed not found !"); result=false; }
cursor = strstr(line, "\"deg\":");
if (cursor) { sscanf(cursor, "\"deg\":%d", &wind_deg); }else{ Serial.println("##WEATHER###: wind deg not found !"); result=false; }
press = press / 1.333;
if(!result) return;
#ifdef USE_NEXTION
nextion.putcmdf("press_txt.txt=\"%dmm\"", press);
nextion.putcmdf("hum_txt.txt=\"%d%%\"", 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:%d%%, wind: %d\n", desc, tempf, press, hum, (int)(wind_deg/22.5));
#ifdef WEATHER_FMT_SHORT
sprintf(timekeeper.weatherBuf, weatherFmt, tempf, press, hum);
#else
#if EXT_WEATHER
sprintf(timekeeper.weatherBuf, weatherFmt, desc, tempf, tempfl, press, hum, wind_speed, wind[(int)(wind_deg/22.5)]);
#else
sprintf(timekeeper.weatherBuf, weatherFmt, desc, tempf, press, hum);
#endif
#endif
display.putRequest(NEWWEATHER);
} else {
Serial.println("##WEATHER###: weather not found !");
}
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;
}, NULL); // <-- client->onData
}, NULL); // <-- weatherClient->onConnect
while(!player.connproc) vTaskDelay(50);
if(!weatherClient->connect(host, 80)){
Serial.println("##WEATHER###: connection failed");
AsyncClient * client = weatherClient;
weatherClient = NULL;
delete client;
}
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

@@ -2,21 +2,19 @@
#define timekeeper_h
#include "Arduino.h"
#define WEATHER_STRING_L 254
void _syncTask(void * pvParameters);
bool _getWeather(char *wstr);
bool _getWeather();
class TimeKeeper {
public:
volatile bool forceWeather;
volatile bool forceTimeSync;
volatile bool busy;
char *weatherBuf;
public:
TimeKeeper() {
busy = false;
forceWeather = true;
forceTimeSync = true;
_returnPlayerTime = _doAfterTime = 0;
}
TimeKeeper();
bool loop0();
bool loop1();
void timeTask();