diff --git a/src/CHANGES.md b/src/CHANGES.md index 414da944..370cf194 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -1,5 +1,14 @@ # Changelog +## 0.5.58 +* improved stability +* improved wifi initial connection - especially if station wifi is not available +* removed new operators from web.h (reduce dynamic allocation) +* improved sun calculation #515, #505 +* fixed wifi auto reconnect #509 +* added disable night communication flag to MQTT #505 +* changed MQTT publish of `available` and `available_text` to sunset #468 + ## 0.5.57 * improved stability * added icons to index.html, added wifi-strength symbol on each page @@ -8,7 +17,7 @@ ## 0.5.56 * factory reset formats entire little fs -* renamed sunrise / sunset on indext.html to start / stop communication +* renamed sunrise / sunset on index.html to start / stop communication * show system information only if called directly from menu * beautified system.html diff --git a/src/app.cpp b/src/app.cpp index ed031d19..841d853c 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -45,18 +45,13 @@ void app::setup() { every(std::bind(&app::tickSend, this), mConfig->nrf.sendInterval); #if !defined(AP_ONLY) once(std::bind(&app::tickNtpUpdate, this), 2); - if((mConfig->sun.lat) && (mConfig->sun.lon)) { - mCalculatedTimezoneOffset = (int8_t)((mConfig->sun.lon >= 0 ? mConfig->sun.lon + 7.5 : mConfig->sun.lon - 7.5) / 15) * 3600; - once(std::bind(&app::tickCalcSunrise, this), 5); - } #endif - if(mSys->Radio.isChipConnected()) { - mSys->addInverters(&mConfig->inst); - mPayload.setup(mSys); - mPayload.enableSerialDebug(mConfig->serial.debug); - } - else + mSys->addInverters(&mConfig->inst); + mPayload.setup(mSys); + mPayload.enableSerialDebug(mConfig->serial.debug); + + if(!mSys->Radio.isChipConnected()) DPRINTLN(DBG_WARN, F("WARNING! your NRF24 module can't be reached, check the wiring")); // when WiFi is in client mode, then enable mqtt broker @@ -95,8 +90,6 @@ void app::loop(void) { mWifi.loop(); #endif - mWeb.loop(); - mSys->Radio.loop(); yield(); @@ -134,26 +127,46 @@ void app::loop(void) { //----------------------------------------------------------------------------- void app::tickNtpUpdate(void) { uint32_t nxtTrig = 5; // default: check again in 5 sec - if (mWifi.getNtpTime()) + if (mWifi.getNtpTime()) { nxtTrig = 43200; // check again in 12 h + if((mSunrise == 0) && (mConfig->sun.lat) && (mConfig->sun.lon)) { + mCalculatedTimezoneOffset = (int8_t)((mConfig->sun.lon >= 0 ? mConfig->sun.lon + 7.5 : mConfig->sun.lon - 7.5) / 15) * 3600; + tickCalcSunrise(); + } + } once(std::bind(&app::tickNtpUpdate, this), nxtTrig); } - //----------------------------------------------------------------------------- void app::tickCalcSunrise(void) { - if (0 == mTimestamp) { - once(std::bind(&app::tickCalcSunrise, this), 5); // check again in 5 secs - return; - } ah::calculateSunriseSunset(mTimestamp, mCalculatedTimezoneOffset, mConfig->sun.lat, mConfig->sun.lon, &mSunrise, &mSunset); + tickIVCommunication(); - uint32_t nxtTrig = mTimestamp - ((mTimestamp - 10) % 86400) + 86400; // next midnight, -10 for safety that it is certain next day + uint32_t nxtTrig = mTimestamp - ((mTimestamp - 1) % 86400) + 86400; // next midnight, -10 for safety that it is certain next day onceAt(std::bind(&app::tickCalcSunrise, this), nxtTrig); if (mConfig->mqtt.broker[0] > 0) { - mMqtt.tickerSun(mSunrise, mSunset, mConfig->sun.offsetSec); + mMqtt.tickerSun(mSunrise, mSunset, mConfig->sun.offsetSec, mConfig->sun.disNightCom); onceAt(std::bind(&PubMqttType::tickSunrise, &mMqtt), (mSunrise - mConfig->sun.offsetSec)); - onceAt(std::bind(&PubMqttType::tickSunset, &mMqtt), (mSunset + mConfig->sun.offsetSec)); + onceAt(std::bind(&PubMqttType::tickSunset, &mMqtt), mSunset); + } +} + +//----------------------------------------------------------------------------- +void app::tickIVCommunication(void) { + mIVCommunicationOn = !mConfig->sun.disNightCom; // if sun.disNightCom is false, communication is always on + if (!mIVCommunicationOn) { // inverter communication only during the day + uint32_t nxtTrig; + if (mTimestamp < (mSunrise - mConfig->sun.offsetSec)) { // current time is before communication start, set next trigger to communication start + nxtTrig = mSunrise - mConfig->sun.offsetSec; + } else { + if (mTimestamp > (mSunset + mConfig->sun.offsetSec)) { // current time is past communication stop, nothing to do. Next update will be done at midnight by tickCalcSunrise + return; + } else { // current time lies within communication start/stop time, set next trigger to communication stop + mIVCommunicationOn = true; + nxtTrig = mSunset + mConfig->sun.offsetSec; + } + } + onceAt(std::bind(&app::tickIVCommunication, this), nxtTrig); } } @@ -163,7 +176,7 @@ void app::tickSend(void) { DPRINTLN(DBG_WARN, "NRF24 not connected!"); return; } - if ((mTimestamp > 0) && (!mConfig->sun.disNightCom || (mTimestamp >= (mSunrise - mConfig->sun.offsetSec) && mTimestamp <= (mSunset + mConfig->sun.offsetSec)))) { // Timestamp is set and (inverter communication only during the day if the option is activated and sunrise/sunset is set) + if (mIVCommunicationOn) { if (!mSys->BufCtrl.empty()) { if (mConfig->serial.debug) DPRINTLN(DBG_DEBUG, F("recbuf not empty! #") + String(mSys->BufCtrl.getFill())); diff --git a/src/app.h b/src/app.h index c2d8a4e8..b28d85b1 100644 --- a/src/app.h +++ b/src/app.h @@ -184,31 +184,12 @@ class app : public IApp, public ah::Scheduler { } void tickNtpUpdate(void); - void tickCalcSunrise(void); + void tickIVCommunication(void); void tickSend(void); - void stats(void) { - DPRINTLN(DBG_VERBOSE, F("main.h:stats")); - #ifdef ESP8266 - uint32_t free; - uint16_t max; - uint8_t frag; - ESP.getHeapStats(&free, &max, &frag); - #elif defined(ESP32) - uint32_t free; - uint32_t max; - uint8_t frag; - free = ESP.getFreeHeap(); - max = ESP.getMaxAllocHeap(); - frag = 0; - #endif - DPRINT(DBG_VERBOSE, F("free: ") + String(free)); - DPRINT(DBG_VERBOSE, F(" - max: ") + String(max) + "%"); - DPRINTLN(DBG_VERBOSE, F(" - frag: ") + String(frag)); - } - bool mShowRebootRequest; + bool mIVCommunicationOn; ahoywifi mWifi; WebType mWeb; diff --git a/src/defines.h b/src/defines.h index 56582290..28c71fd1 100644 --- a/src/defines.h +++ b/src/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 5 -#define VERSION_PATCH 57 +#define VERSION_PATCH 58 //------------------------------------- typedef struct { diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h index e90aa095..13508a47 100644 --- a/src/hm/hmRadio.h +++ b/src/hm/hmRadio.h @@ -151,8 +151,8 @@ class HmRadio { } void loop(void) { - DISABLE_IRQ; if(mIrqRcvd) { + DISABLE_IRQ; mIrqRcvd = false; bool tx_ok, tx_fail, rx_ready; mNrf24.whatHappened(tx_ok, tx_fail, rx_ready); // resets the IRQ pin to HIGH @@ -175,9 +175,8 @@ class HmRadio { break; } mNrf24.flush_rx(); // drop the packet - } - else RESTORE_IRQ; + } } void enableDebug() { @@ -185,7 +184,6 @@ class HmRadio { } void handleIntr(void) { - //DPRINTLN(DBG_VERBOSE, F("hmRadio.h:handleIntr")); mIrqRcvd = true; } @@ -267,7 +265,8 @@ class HmRadio { } bool switchRxCh(uint16_t addLoop = 0) { - //DPRINTLN(DBG_VERBOSE, F("hmRadio.h:switchRxCh")); + if(!mNrf24.isChipConnected()) + return true; mRxLoopCnt += addLoop; if(mRxLoopCnt != 0) { mRxLoopCnt--; diff --git a/src/hm/hmSystem.h b/src/hm/hmSystem.h index dad8b4aa..85976cbd 100644 --- a/src/hm/hmSystem.h +++ b/src/hm/hmSystem.h @@ -104,14 +104,15 @@ class HmSystem { } uint8_t getNumInverters(void) { - uint8_t num = 0; + /*uint8_t num = 0; INVERTERTYPE *p; for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { p = &mInverter[i]; if(p->config->serial.u64 != 0ULL) num++; } - return num; + return num;*/ + return MAX_NUM_INVERTERS; } void enableDebug() { diff --git a/src/hm/payload.h b/src/hm/payload.h index d67445b0..ef69c4fe 100644 --- a/src/hm/payload.h +++ b/src/hm/payload.h @@ -9,6 +9,7 @@ #include "../utils/dbg.h" #include "../utils/crc.h" #include "../utils/handler.h" +#include "../config/config.h" #include typedef struct { diff --git a/src/publisher/pubMqtt.h b/src/publisher/pubMqtt.h index f67c1c00..8ae2f5a7 100644 --- a/src/publisher/pubMqtt.h +++ b/src/publisher/pubMqtt.h @@ -91,11 +91,12 @@ class PubMqtt { tickSunset(); } - void tickerSun(uint32_t sunrise, uint32_t sunset, uint32_t offs) { + void tickerSun(uint32_t sunrise, uint32_t sunset, uint32_t offs, bool disNightCom) { publish("sunrise", String(sunrise).c_str(), true); publish("sunset", String(sunset).c_str(), true); publish("comm_start", String(sunrise - offs).c_str(), true); publish("comm_stop", String(sunset + offs).c_str(), true); + publish("dis_night_comm", ((disNightCom) ? "true" : "false"), true); } void tickSunrise() { diff --git a/src/web/RestApi.h b/src/web/RestApi.h index eb262aae..ed5bc94d 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -353,6 +353,7 @@ class RestApi { obj[F("ts_sunrise")] = mApp->getSunrise(); obj[F("ts_sunset")] = mApp->getSunset(); obj[F("ts_offset")] = mConfig->sun.offsetSec; + obj[F("disNightComm")] = mConfig->sun.disNightCom; JsonArray inv = obj.createNestedArray(F("inverter")); Inverter<> *iv; diff --git a/src/web/html/index.html b/src/web/html/index.html index 065f5f8d..e702312b 100644 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -47,10 +47,11 @@

Support this project:

@@ -145,7 +146,7 @@ && ((obj["ts_sunset"] + obj["ts_offset"]) > obj["ts_now"])) { commInfo = "Polling inverter(s), will stop at " + (new Date((obj["ts_sunset"] + obj["ts_offset"]) * 1000).toLocaleString('de-DE')); } - else { + else if(obj["disNightComm"]) { commInfo = "Night time, no Communication to Inverter, "; if(obj["ts_now"] > (obj["ts_sunrise"] - obj["ts_offset"])) { commInfo += "stopped polling at " + (new Date((obj["ts_sunset"] + obj["ts_offset"]) * 1000).toLocaleString('de-DE')); @@ -176,6 +177,8 @@ avail = "available and is "; if(false == i["is_producing"]) avail += "not "; + else + color = "#090"; avail += "producing"; } diff --git a/src/web/html/system.html b/src/web/html/system.html index 12d48a8f..5419d7ba 100644 --- a/src/web/html/system.html +++ b/src/web/html/system.html @@ -103,18 +103,21 @@ } function parseIndex(obj) { - var h = div(["head", "p-2"]); - var r = div(["row"]); - r.appendChild(div(["col", "a-c"], "Sun")); - h.appendChild(r); + if(obj["ts_sunrise"] > 0) { + var h = div(["head", "p-2"]); + var r = div(["row"]); + r.appendChild(div(["col", "a-c"], "Sun")); + h.appendChild(r); - document.getElementById("sun").append ( - h, - genTabRow("Sunrise", new Date(obj["ts_sunrise"] * 1000).toLocaleString('de-DE')), - genTabRow("Sunset", new Date(obj["ts_sunset"] * 1000).toLocaleString('de-DE')), - genTabRow("Communication start", new Date((obj["ts_sunrise"] - obj["ts_offset"]) * 1000).toLocaleString('de-DE')), - genTabRow("Communication stop", new Date((obj["ts_sunset"] + obj["ts_offset"]) * 1000).toLocaleString('de-DE')) - ); + document.getElementById("sun").append ( + h, + genTabRow("Sunrise", new Date(obj["ts_sunrise"] * 1000).toLocaleString('de-DE')), + genTabRow("Sunset", new Date(obj["ts_sunset"] * 1000).toLocaleString('de-DE')), + genTabRow("Communication start", new Date((obj["ts_sunrise"] - obj["ts_offset"]) * 1000).toLocaleString('de-DE')), + genTabRow("Communication stop", new Date((obj["ts_sunset"] + obj["ts_offset"]) * 1000).toLocaleString('de-DE')), + genTabRow("Night Communication", ((obj["disNightComm"]) ? "disabled" : "enabled")) + ); + } } function parse(obj) { diff --git a/src/web/web.h b/src/web/web.h index 4a381a35..63a46ec2 100644 --- a/src/web/web.h +++ b/src/web/web.h @@ -39,74 +39,61 @@ const char* const pinArgNames[] = {"pinCs", "pinCe", "pinIrq", "pinLed0", "pinLe template class Web { public: - Web(void) { + Web(void) : mWeb(80), mEvts("/events") { mProtected = true; mLogoutTimeout = 0; memset(mSerialBuf, 0, WEB_SERIAL_BUF_SIZE); mSerialBufFill = 0; - mWebSerialTicker = 0; - mWebSerialInterval = 1000; // [ms] mSerialAddTime = true; + mSerialClientConnnected = false; } void setup(IApp *app, HMSYSTEM *sys, settings_t *config) { mApp = app; mSys = sys; mConfig = config; - mWeb = new AsyncWebServer(80); - mEvts = new AsyncEventSource("/events"); - DPRINTLN(DBG_VERBOSE, F("app::setup-begin")); - mWeb->begin(); DPRINTLN(DBG_VERBOSE, F("app::setup-on")); - mWeb->on("/", HTTP_GET, std::bind(&Web::onIndex, this, std::placeholders::_1)); - mWeb->on("/login", HTTP_ANY, std::bind(&Web::onLogin, this, std::placeholders::_1)); - mWeb->on("/logout", HTTP_GET, std::bind(&Web::onLogout, this, std::placeholders::_1)); - mWeb->on("/style.css", HTTP_GET, std::bind(&Web::onCss, this, std::placeholders::_1)); - mWeb->on("/api.js", HTTP_GET, std::bind(&Web::onApiJs, this, std::placeholders::_1)); - mWeb->on("/favicon.ico", HTTP_GET, std::bind(&Web::onFavicon, this, std::placeholders::_1)); - mWeb->onNotFound ( std::bind(&Web::showNotFound, this, std::placeholders::_1)); - mWeb->on("/reboot", HTTP_ANY, std::bind(&Web::onReboot, this, std::placeholders::_1)); - mWeb->on("/system", HTTP_ANY, std::bind(&Web::onSystem, this, std::placeholders::_1)); - mWeb->on("/erase", HTTP_ANY, std::bind(&Web::showErase, this, std::placeholders::_1)); - mWeb->on("/factory", HTTP_ANY, std::bind(&Web::showFactoryRst, this, std::placeholders::_1)); + mWeb.on("/", HTTP_GET, std::bind(&Web::onIndex, this, std::placeholders::_1)); + mWeb.on("/login", HTTP_ANY, std::bind(&Web::onLogin, this, std::placeholders::_1)); + mWeb.on("/logout", HTTP_GET, std::bind(&Web::onLogout, this, std::placeholders::_1)); + mWeb.on("/style.css", HTTP_GET, std::bind(&Web::onCss, this, std::placeholders::_1)); + mWeb.on("/api.js", HTTP_GET, std::bind(&Web::onApiJs, this, std::placeholders::_1)); + mWeb.on("/favicon.ico", HTTP_GET, std::bind(&Web::onFavicon, this, std::placeholders::_1)); + mWeb.onNotFound ( std::bind(&Web::showNotFound, this, std::placeholders::_1)); + mWeb.on("/reboot", HTTP_ANY, std::bind(&Web::onReboot, this, std::placeholders::_1)); + mWeb.on("/system", HTTP_ANY, std::bind(&Web::onSystem, this, std::placeholders::_1)); + mWeb.on("/erase", HTTP_ANY, std::bind(&Web::showErase, this, std::placeholders::_1)); + mWeb.on("/factory", HTTP_ANY, std::bind(&Web::showFactoryRst, this, std::placeholders::_1)); - mWeb->on("/setup", HTTP_GET, std::bind(&Web::onSetup, this, std::placeholders::_1)); - mWeb->on("/save", HTTP_ANY, std::bind(&Web::showSave, this, std::placeholders::_1)); + mWeb.on("/setup", HTTP_GET, std::bind(&Web::onSetup, this, std::placeholders::_1)); + mWeb.on("/save", HTTP_ANY, std::bind(&Web::showSave, this, std::placeholders::_1)); - mWeb->on("/live", HTTP_ANY, std::bind(&Web::onLive, this, std::placeholders::_1)); - mWeb->on("/api1", HTTP_POST, std::bind(&Web::showWebApi, this, std::placeholders::_1)); + mWeb.on("/live", HTTP_ANY, std::bind(&Web::onLive, this, std::placeholders::_1)); + mWeb.on("/api1", HTTP_POST, std::bind(&Web::showWebApi, this, std::placeholders::_1)); #ifdef ENABLE_JSON_EP - mWeb->on("/json", HTTP_ANY, std::bind(&Web::showJson, this, std::placeholders::_1)); + mWeb.on("/json", HTTP_ANY, std::bind(&Web::showJson, this, std::placeholders::_1)); #endif #ifdef ENABLE_PROMETHEUS_EP - mWeb->on("/metrics", HTTP_ANY, std::bind(&Web::showMetrics, this, std::placeholders::_1)); + mWeb.on("/metrics", HTTP_ANY, std::bind(&Web::showMetrics, this, std::placeholders::_1)); #endif - mWeb->on("/update", HTTP_GET, std::bind(&Web::onUpdate, this, std::placeholders::_1)); - mWeb->on("/update", HTTP_POST, std::bind(&Web::showUpdate, this, std::placeholders::_1), + mWeb.on("/update", HTTP_GET, std::bind(&Web::onUpdate, this, std::placeholders::_1)); + mWeb.on("/update", HTTP_POST, std::bind(&Web::showUpdate, this, std::placeholders::_1), std::bind(&Web::showUpdate2, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6)); - mWeb->on("/serial", HTTP_GET, std::bind(&Web::onSerial, this, std::placeholders::_1)); + mWeb.on("/serial", HTTP_GET, std::bind(&Web::onSerial, this, std::placeholders::_1)); - mEvts->onConnect(std::bind(&Web::onConnect, this, std::placeholders::_1)); - mWeb->addHandler(mEvts); + mEvts.onConnect(std::bind(&Web::onConnect, this, std::placeholders::_1)); + mWeb.addHandler(&mEvts); + + mWeb.begin(); registerDebugCb(std::bind(&Web::serialCb, this, std::placeholders::_1)); // dbg.h } - void loop(void) { - if(ah::checkTicker(&mWebSerialTicker, mWebSerialInterval)) { - if(mSerialBufFill > 0) { - mEvts->send(mSerialBuf, "serial", millis()); - memset(mSerialBuf, 0, WEB_SERIAL_BUF_SIZE); - mSerialBufFill = 0; - } - } - } - void tickSecond() { if(0 != mLogoutTimeout) { mLogoutTimeout -= 1; @@ -117,10 +104,18 @@ class Web { DPRINTLN(DBG_DEBUG, "auto logout in " + String(mLogoutTimeout)); } + + if(mSerialClientConnnected) { + if(mSerialBufFill > 0) { + mEvts.send(mSerialBuf, "serial", millis()); + memset(mSerialBuf, 0, WEB_SERIAL_BUF_SIZE); + mSerialBufFill = 0; + } + } } AsyncWebServer *getWebSrvPtr(void) { - return mWeb; + return &mWeb; } void setProtection(bool protect) { @@ -151,6 +146,9 @@ class Web { } void serialCb(String msg) { + if(!mSerialClientConnnected) + return; + msg.replace("\r\n", ""); if(mSerialAddTime) { if((9 + mSerialBufFill) <= WEB_SERIAL_BUF_SIZE) { @@ -159,7 +157,7 @@ class Web { } else { mSerialBufFill = 0; - mEvts->send("webSerial, buffer overflow!", "serial", millis()); + mEvts.send("webSerial, buffer overflow!", "serial", millis()); } mSerialAddTime = false; } @@ -174,7 +172,7 @@ class Web { } else { mSerialBufFill = 0; - mEvts->send("webSerial, buffer overflow!", "serial", millis()); + mEvts.send("webSerial, buffer overflow!", "serial", millis()); } } @@ -212,6 +210,8 @@ class Web { void onConnect(AsyncEventSourceClient *client) { DPRINTLN(DBG_VERBOSE, "onConnect"); + mSerialClientConnnected = true; + if(client->lastId()) DPRINTLN(DBG_VERBOSE, "Client reconnected! Last message ID that it got is: " + String(client->lastId())); @@ -262,6 +262,7 @@ class Web { } void onCss(AsyncWebServerRequest *request) { + DPRINTLN(DBG_VERBOSE, F("onCss")); mLogoutTimeout = LOGOUT_TIMEOUT; AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/css"), style_css, style_css_len); response->addHeader(F("Content-Encoding"), "gzip"); @@ -373,136 +374,137 @@ class Web { return; } - if(request->args() > 0) { - char buf[20] = {0}; + if(request->args() == 0) + return; - // general - if(request->arg("ssid") != "") - request->arg("ssid").toCharArray(mConfig->sys.stationSsid, SSID_LEN); - if(request->arg("pwd") != "{PWD}") - request->arg("pwd").toCharArray(mConfig->sys.stationPwd, PWD_LEN); - if(request->arg("device") != "") - request->arg("device").toCharArray(mConfig->sys.deviceName, DEVNAME_LEN); - if(request->arg("adminpwd") != "{PWD}") { - request->arg("adminpwd").toCharArray(mConfig->sys.adminPwd, PWD_LEN); - mProtected = (strlen(mConfig->sys.adminPwd) > 0); + char buf[20] = {0}; + + // general + if(request->arg("ssid") != "") + request->arg("ssid").toCharArray(mConfig->sys.stationSsid, SSID_LEN); + if(request->arg("pwd") != "{PWD}") + request->arg("pwd").toCharArray(mConfig->sys.stationPwd, PWD_LEN); + if(request->arg("device") != "") + request->arg("device").toCharArray(mConfig->sys.deviceName, DEVNAME_LEN); + if(request->arg("adminpwd") != "{PWD}") { + request->arg("adminpwd").toCharArray(mConfig->sys.adminPwd, PWD_LEN); + mProtected = (strlen(mConfig->sys.adminPwd) > 0); + } + + + // static ip + request->arg("ipAddr").toCharArray(buf, 20); + ah::ip2Arr(mConfig->sys.ip.ip, buf); + request->arg("ipMask").toCharArray(buf, 20); + ah::ip2Arr(mConfig->sys.ip.mask, buf); + request->arg("ipDns1").toCharArray(buf, 20); + ah::ip2Arr(mConfig->sys.ip.dns1, buf); + request->arg("ipDns2").toCharArray(buf, 20); + ah::ip2Arr(mConfig->sys.ip.dns2, buf); + request->arg("ipGateway").toCharArray(buf, 20); + ah::ip2Arr(mConfig->sys.ip.gateway, buf); + + + // inverter + Inverter<> *iv; + for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) { + iv = mSys->getInverterByPos(i, false); + // enable communication + iv->config->enabled = (request->arg("inv" + String(i) + "Enable") == "on"); + // address + request->arg("inv" + String(i) + "Addr").toCharArray(buf, 20); + if(strlen(buf) == 0) + memset(buf, 0, 20); + iv->config->serial.u64 = ah::Serial2u64(buf); + switch(iv->config->serial.b[4]) { + case 0x21: iv->type = INV_TYPE_1CH; iv->channels = 1; break; + case 0x41: iv->type = INV_TYPE_2CH; iv->channels = 2; break; + case 0x61: iv->type = INV_TYPE_4CH; iv->channels = 4; break; + default: break; } + // name + request->arg("inv" + String(i) + "Name").toCharArray(iv->config->name, MAX_NAME_LENGTH); - // static ip - request->arg("ipAddr").toCharArray(buf, 20); - ah::ip2Arr(mConfig->sys.ip.ip, buf); - request->arg("ipMask").toCharArray(buf, 20); - ah::ip2Arr(mConfig->sys.ip.mask, buf); - request->arg("ipDns1").toCharArray(buf, 20); - ah::ip2Arr(mConfig->sys.ip.dns1, buf); - request->arg("ipDns2").toCharArray(buf, 20); - ah::ip2Arr(mConfig->sys.ip.dns2, buf); - request->arg("ipGateway").toCharArray(buf, 20); - ah::ip2Arr(mConfig->sys.ip.gateway, buf); - - - // inverter - Inverter<> *iv; - for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) { - iv = mSys->getInverterByPos(i, false); - // enable communication - iv->config->enabled = (request->arg("inv" + String(i) + "Enable") == "on"); - // address - request->arg("inv" + String(i) + "Addr").toCharArray(buf, 20); - if(strlen(buf) == 0) - memset(buf, 0, 20); - iv->config->serial.u64 = ah::Serial2u64(buf); - switch(iv->config->serial.b[4]) { - case 0x21: iv->type = INV_TYPE_1CH; iv->channels = 1; break; - case 0x41: iv->type = INV_TYPE_2CH; iv->channels = 2; break; - case 0x61: iv->type = INV_TYPE_4CH; iv->channels = 4; break; - default: break; - } - - // name - request->arg("inv" + String(i) + "Name").toCharArray(iv->config->name, MAX_NAME_LENGTH); - - // max channel power / name - for(uint8_t j = 0; j < 4; j++) { - iv->config->chMaxPwr[j] = request->arg("inv" + String(i) + "ModPwr" + String(j)).toInt() & 0xffff; - request->arg("inv" + String(i) + "ModName" + String(j)).toCharArray(iv->config->chName[j], MAX_NAME_LENGTH); - } - iv->initialized = true; + // max channel power / name + for(uint8_t j = 0; j < 4; j++) { + iv->config->chMaxPwr[j] = request->arg("inv" + String(i) + "ModPwr" + String(j)).toInt() & 0xffff; + request->arg("inv" + String(i) + "ModName" + String(j)).toCharArray(iv->config->chName[j], MAX_NAME_LENGTH); } + iv->initialized = true; + } - if(request->arg("invInterval") != "") - mConfig->nrf.sendInterval = request->arg("invInterval").toInt(); - if(request->arg("invRetry") != "") - mConfig->nrf.maxRetransPerPyld = request->arg("invRetry").toInt(); + if(request->arg("invInterval") != "") + mConfig->nrf.sendInterval = request->arg("invInterval").toInt(); + if(request->arg("invRetry") != "") + mConfig->nrf.maxRetransPerPyld = request->arg("invRetry").toInt(); - // pinout - uint8_t pin; - for(uint8_t i = 0; i < 5; i ++) { - pin = request->arg(String(pinArgNames[i])).toInt(); - switch(i) { - default: mConfig->nrf.pinCs = ((pin != 0xff) ? pin : DEF_CS_PIN); break; - case 1: mConfig->nrf.pinCe = ((pin != 0xff) ? pin : DEF_CE_PIN); break; - case 2: mConfig->nrf.pinIrq = ((pin != 0xff) ? pin : DEF_IRQ_PIN); break; - case 3: mConfig->led.led0 = pin; break; - case 4: mConfig->led.led1 = pin; break; - } + // pinout + uint8_t pin; + for(uint8_t i = 0; i < 5; i ++) { + pin = request->arg(String(pinArgNames[i])).toInt(); + switch(i) { + default: mConfig->nrf.pinCs = ((pin != 0xff) ? pin : DEF_CS_PIN); break; + case 1: mConfig->nrf.pinCe = ((pin != 0xff) ? pin : DEF_CE_PIN); break; + case 2: mConfig->nrf.pinIrq = ((pin != 0xff) ? pin : DEF_IRQ_PIN); break; + case 3: mConfig->led.led0 = pin; break; + case 4: mConfig->led.led1 = pin; break; } + } - // nrf24 amplifier power - mConfig->nrf.amplifierPower = request->arg("rf24Power").toInt() & 0x03; + // nrf24 amplifier power + mConfig->nrf.amplifierPower = request->arg("rf24Power").toInt() & 0x03; - // ntp - if(request->arg("ntpAddr") != "") { - request->arg("ntpAddr").toCharArray(mConfig->ntp.addr, NTP_ADDR_LEN); - mConfig->ntp.port = request->arg("ntpPort").toInt() & 0xffff; - } + // ntp + if(request->arg("ntpAddr") != "") { + request->arg("ntpAddr").toCharArray(mConfig->ntp.addr, NTP_ADDR_LEN); + mConfig->ntp.port = request->arg("ntpPort").toInt() & 0xffff; + } - // sun - if(request->arg("sunLat") == "" || (request->arg("sunLon") == "")) { - mConfig->sun.lat = 0.0; - mConfig->sun.lon = 0.0; - mConfig->sun.disNightCom = false; - mConfig->sun.offsetSec = 0; - } else { - mConfig->sun.lat = request->arg("sunLat").toFloat(); - mConfig->sun.lon = request->arg("sunLon").toFloat(); - mConfig->sun.disNightCom = (request->arg("sunDisNightCom") == "on"); - mConfig->sun.offsetSec = request->arg("sunOffs").toInt() * 60; - } + // sun + if(request->arg("sunLat") == "" || (request->arg("sunLon") == "")) { + mConfig->sun.lat = 0.0; + mConfig->sun.lon = 0.0; + mConfig->sun.disNightCom = false; + mConfig->sun.offsetSec = 0; + } else { + mConfig->sun.lat = request->arg("sunLat").toFloat(); + mConfig->sun.lon = request->arg("sunLon").toFloat(); + mConfig->sun.disNightCom = (request->arg("sunDisNightCom") == "on"); + mConfig->sun.offsetSec = request->arg("sunOffs").toInt() * 60; + } - // mqtt - if(request->arg("mqttAddr") != "") { - String addr = request->arg("mqttAddr"); - addr.trim(); - addr.toCharArray(mConfig->mqtt.broker, MQTT_ADDR_LEN); - } - else - mConfig->mqtt.broker[0] = '\0'; - request->arg("mqttUser").toCharArray(mConfig->mqtt.user, MQTT_USER_LEN); - if(request->arg("mqttPwd") != "{PWD}") - request->arg("mqttPwd").toCharArray(mConfig->mqtt.pwd, MQTT_PWD_LEN); - request->arg("mqttTopic").toCharArray(mConfig->mqtt.topic, MQTT_TOPIC_LEN); - mConfig->mqtt.port = request->arg("mqttPort").toInt(); + // mqtt + if(request->arg("mqttAddr") != "") { + String addr = request->arg("mqttAddr"); + addr.trim(); + addr.toCharArray(mConfig->mqtt.broker, MQTT_ADDR_LEN); + } + else + mConfig->mqtt.broker[0] = '\0'; + request->arg("mqttUser").toCharArray(mConfig->mqtt.user, MQTT_USER_LEN); + if(request->arg("mqttPwd") != "{PWD}") + request->arg("mqttPwd").toCharArray(mConfig->mqtt.pwd, MQTT_PWD_LEN); + request->arg("mqttTopic").toCharArray(mConfig->mqtt.topic, MQTT_TOPIC_LEN); + mConfig->mqtt.port = request->arg("mqttPort").toInt(); - // serial console - if(request->arg("serIntvl") != "") { - mConfig->serial.interval = request->arg("serIntvl").toInt() & 0xffff; + // serial console + if(request->arg("serIntvl") != "") { + mConfig->serial.interval = request->arg("serIntvl").toInt() & 0xffff; - mConfig->serial.debug = (request->arg("serDbg") == "on"); - mConfig->serial.showIv = (request->arg("serEn") == "on"); - // Needed to log TX buffers to serial console - mSys->Radio.mSerialDebug = mConfig->serial.debug; - } - mApp->saveSettings(); + mConfig->serial.debug = (request->arg("serDbg") == "on"); + mConfig->serial.showIv = (request->arg("serEn") == "on"); + // Needed to log TX buffers to serial console + mSys->Radio.mSerialDebug = mConfig->serial.debug; + } + mApp->saveSettings(); - if(request->arg("reboot") == "on") - onReboot(request); - else { - AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html"), system_html, system_html_len); - response->addHeader(F("Content-Encoding"), "gzip"); - request->send(response); - } + if(request->arg("reboot") == "on") + onReboot(request); + else { + AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html"), system_html, system_html_len); + response->addHeader(F("Content-Encoding"), "gzip"); + request->send(response); } } @@ -632,7 +634,7 @@ class Web { } modJson += F("\"json_ts\": \"") + String(ah::getDateTimeStr(mMain->mTimestamp)) + F("\"\n}\n"); - mWeb->send(200, F("application/json"), modJson); + mWeb.send(200, F("application/json"), modJson); } #endif @@ -663,7 +665,7 @@ class Web { } } - mWeb->send(200, F("text/plain"), metrics); + mWeb.send(200, F("text/plain"), metrics); } std::pair convertToPromUnits(String shortUnit) { @@ -679,8 +681,8 @@ class Web { } #endif - AsyncWebServer *mWeb; - AsyncEventSource *mEvts; + AsyncWebServer mWeb; + AsyncEventSource mEvts; bool mProtected; uint32_t mLogoutTimeout; IApp *mApp; @@ -691,8 +693,7 @@ class Web { bool mSerialAddTime; char mSerialBuf[WEB_SERIAL_BUF_SIZE]; uint16_t mSerialBufFill; - uint32_t mWebSerialTicker; - uint32_t mWebSerialInterval; + bool mSerialClientConnnected; }; #endif /*__WEB_H__*/ diff --git a/src/wifi/ahoywifi.cpp b/src/wifi/ahoywifi.cpp index 203df285..6b0c333d 100644 --- a/src/wifi/ahoywifi.cpp +++ b/src/wifi/ahoywifi.cpp @@ -12,33 +12,31 @@ // NTP CONFIG #define NTP_PACKET_SIZE 48 +enum {WIFI_NOT_FOUND = 0, WIFI_FOUND, WIFI_NOT_COMPLETE}; + //----------------------------------------------------------------------------- -ahoywifi::ahoywifi() { - mCnt = 0; - mConnected = false; - mReconnect = false; +ahoywifi::ahoywifi() : mApIp(192, 168, 4, 1), mApMask(255, 255, 255, 0) { + mDnsActive = false; + mClientCnt = 0; + mLoopCnt = 250; + mExtScan = false; } //----------------------------------------------------------------------------- void ahoywifi::setup(settings_t *config, uint32_t *utcTimestamp) { + mCnt = 0; + mConnected = false; + mReconnect = false; mConfig = config; mUtcTimestamp = utcTimestamp; + if(String(mConfig->sys.deviceName) != "") + WiFi.hostname(mConfig->sys.deviceName); + #if !defined(FB_WIFI_OVERRIDDEN) - if(strncmp(mConfig->sys.stationSsid, FB_WIFI_SSID, 14) == 0) - setupAp(); - #endif - #if !defined(AP_ONLY) - if(mConfig->valid) { - #if !defined(FB_WIFI_OVERRIDDEN) - if(strncmp(mConfig->sys.stationSsid, FB_WIFI_SSID, 14) != 0) - setupStation(); - #else - setupStation(); - #endif - } + setupAp(); #endif #if defined(ESP8266) @@ -59,19 +57,43 @@ void ahoywifi::loop() { if((mCnt % 50) == 0) WiFi.disconnect(); else if((mCnt % 60) == 0) { - WiFi.reconnect(); + DPRINTLN(DBG_INFO, F("[WiFi] reconnect")); + WiFi.begin(mConfig->sys.stationSsid, mConfig->sys.stationPwd); mCnt = 0; } } - #endif + yield(); + if(mDnsActive) { + mDns.processNextRequest(); + uint8_t cnt = WiFi.softAPgetStationNum(); + if(cnt != mClientCnt) { + mClientCnt = cnt; + DPRINTLN(DBG_INFO, String(cnt) + F(" client(s) connected")); + } + if(!mExtScan && (mLoopCnt == 240)) { + if(scanStationNetwork()) { + setupStation(); + mLoopCnt = 0; + } + } + + if(0 != mLoopCnt) { + if(++mLoopCnt > 250) { + mLoopCnt = 1; + if(!mExtScan) + scanAvailNetworks(false); + } + delay(25); + } + } + #endif } //----------------------------------------------------------------------------- void ahoywifi::setupAp(void) { - DPRINTLN(DBG_VERBOSE, F("wifi::setupAp")); - IPAddress apIp(192, 168, 4, 1); + DPRINTLN(DBG_INFO, F("wifi::setupAp")); DBGPRINTLN(F("\n---------\nAhoyDTU Info:")); DBGPRINT(F("Version: ")); @@ -83,20 +105,22 @@ void ahoywifi::setupAp(void) { DBGPRINTLN(WIFI_AP_SSID); DBGPRINT(F("PWD: ")); DBGPRINTLN(WIFI_AP_PWD); - DBGPRINTLN("IP Address: http://" + apIp.toString()); + DBGPRINTLN("IP Address: http://" + mApIp.toString()); DBGPRINTLN(F("---------\n")); WiFi.mode(WIFI_AP_STA); - WiFi.softAPConfig(apIp, apIp, IPAddress(255, 255, 255, 0)); + WiFi.softAPConfig(mApIp, mApIp, mApMask); WiFi.softAP(WIFI_AP_SSID, WIFI_AP_PWD); - mDns.start(53, "*", apIp); + mDns.setErrorReplyCode(DNSReplyCode::NoError); + mDns.start(53, "*", WiFi.softAPIP()); + mDnsActive = true; } //----------------------------------------------------------------------------- void ahoywifi::setupStation(void) { - DPRINTLN(DBG_VERBOSE, F("wifi::setupStation")); + DPRINTLN(DBG_INFO, F("wifi::setupStation")); if(mConfig->sys.ip.ip[0] != 0) { IPAddress ip(mConfig->sys.ip.ip); IPAddress mask(mConfig->sys.ip.mask); @@ -107,12 +131,27 @@ void ahoywifi::setupStation(void) { DPRINTLN(DBG_ERROR, F("failed to set static IP!")); } WiFi.begin(mConfig->sys.stationSsid, mConfig->sys.stationPwd); - if(String(mConfig->sys.deviceName) != "") - WiFi.hostname(mConfig->sys.deviceName); DBGPRINT(F("connect to network '")); DBGPRINT(mConfig->sys.stationSsid); - DBGPRINTLN(F("' ...")); +} + + +//----------------------------------------------------------------------------- +bool ahoywifi::scanStationNetwork(void) { + bool found = false; + int n = WiFi.scanComplete(); + if(n > 0) { + for (int i = 0; i < n; i++) { + DPRINTLN(DBG_INFO, "found network: " + WiFi.SSID(i)); + if(String(mConfig->sys.stationSsid) == WiFi.SSID(i)) { + found = true; + break; + } + } + WiFi.scanDelete(); + } + return found; } @@ -156,9 +195,10 @@ bool ahoywifi::getNtpTime(void) { //----------------------------------------------------------------------------- -void ahoywifi::scanAvailNetworks(void) { - int n = WiFi.scanComplete(); - if(n == -2) +void ahoywifi::scanAvailNetworks(bool externalCall) { + if(externalCall) + mExtScan = true; + if(-2 == WiFi.scanComplete()) WiFi.scanNetworks(true); } @@ -182,6 +222,7 @@ void ahoywifi::getAvailNetworks(JsonObject obj) { } WiFi.scanDelete(); } + mExtScan = false; } @@ -215,6 +256,7 @@ void ahoywifi::sendNTPpacket(IPAddress& address) { DBGPRINTLN(F("\n[WiFi] Connected")); WiFi.mode(WIFI_STA); DBGPRINTLN(F("[WiFi] AP disabled")); + mDnsActive = false; mDns.stop(); welcome(WiFi.localIP().toString() + F(" (Station)")); @@ -245,6 +287,7 @@ void ahoywifi::sendNTPpacket(IPAddress& address) { WiFi.mode(WIFI_STA); WiFi.begin(); DBGPRINTLN(F("[WiFi] AP disabled")); + mDnsActive = false; mDns.stop(); mReconnect = false; } diff --git a/src/wifi/ahoywifi.h b/src/wifi/ahoywifi.h index 644952e9..58b063bd 100644 --- a/src/wifi/ahoywifi.h +++ b/src/wifi/ahoywifi.h @@ -23,12 +23,13 @@ class ahoywifi { void setup(settings_t *config, uint32_t *utcTimestamp); void loop(void); bool getNtpTime(void); - void scanAvailNetworks(void); + void scanAvailNetworks(bool externalCall = true); void getAvailNetworks(JsonObject obj); private: void setupAp(void); void setupStation(void); + bool scanStationNetwork(void); void sendNTPpacket(IPAddress& address); #if defined(ESP8266) void onConnect(const WiFiEventStationModeGotIP& event); @@ -38,18 +39,23 @@ class ahoywifi { #endif void welcome(String msg); + settings_t *mConfig; DNSServer mDns; + IPAddress mApIp, mApMask; WiFiUDP mUdp; // for time server #if defined(ESP8266) WiFiEventHandler wifiConnectHandler; WiFiEventHandler wifiDisconnectHandler; #endif - bool mConnected, mReconnect; - uint8_t mCnt; + bool mConnected, mReconnect, mDnsActive; + uint8_t mCnt, mClientCnt; uint32_t *mUtcTimestamp; + + uint8_t mLoopCnt; + bool mExtScan; }; #endif /*__AHOYWIFI_H__*/