diff --git a/src/CHANGES.md b/src/CHANGES.md index 9405fb15..9e31ada2 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -2,10 +2,18 @@ (starting from release version `0.5.66`) +## 0.5.78 +* further improvements regarding wifi #611, fix connection if only one AP with same SSID is there +* fix endless loop in `zerovalues` #564 +* fix auto discover again #565 +* added total values to autodiscover #630 +* improved zero at midnight #625 + ## 0.5.77 * fix wrong filename for automatically created manifest (online installer) #620 * added rotate display feature #619 -* improved Prometheus endpoint #615, thx to fsck-block +* improved Prometheus endpoint #615, thx to @fsck-block +* improved wifi to connect always to strongest RSSI, thx to @beegee3 #611 ## 0.5.76 * reduce MQTT retry interval from maximum speed to one second diff --git a/src/defines.h b/src/defines.h index ecb161bc..5c626e02 100644 --- a/src/defines.h +++ b/src/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 5 -#define VERSION_PATCH 77 +#define VERSION_PATCH 78 //------------------------------------- typedef struct { diff --git a/src/publisher/pubMqtt.h b/src/publisher/pubMqtt.h index e59eb9f4..1b3effd3 100644 --- a/src/publisher/pubMqtt.h +++ b/src/publisher/pubMqtt.h @@ -133,6 +133,7 @@ class PubMqtt { void tickerMidnight() { Inverter<> *iv; record_t<> *rec; + char topic[7 + MQTT_TOPIC_LEN], val[4]; // set YieldDay to zero for (uint8_t id = 0; id < mSys->getNumInverters(); id++) { @@ -142,10 +143,11 @@ class PubMqtt { rec = iv->getRecordStruct(RealTimeRunData_Debug); uint8_t pos = iv->getPosByChFld(CH0, FLD_YD, rec); iv->setValue(pos, rec, 0.0f); - } - mSendList.push(RealTimeRunData_Debug); - sendIvData(); + snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/ch0/%s", iv->config->name, fields[FLD_YD]); + snprintf(val, 4, "0.0"); + publish(topic, val, true); + } } void payloadEventListener(uint8_t cmd) { @@ -210,7 +212,11 @@ class PubMqtt { DPRINTLN(DBG_VERBOSE, F("sendMqttDiscoveryConfig")); char topic[64], name[32], uniq_id[32]; - DynamicJsonDocument doc(512); + DynamicJsonDocument doc(256); + + uint8_t fldTotal[4] = {FLD_PAC, FLD_YT, FLD_YD, FLD_PDC}; + const char* unitTotal[4] = {"W", "kWh", "Wh", "W"}; + for (uint8_t id = 0; id < mSys->getNumInverters(); id++) { Inverter<> *iv = mSys->getInverterByPos(id); if (NULL == iv) @@ -218,29 +224,39 @@ class PubMqtt { record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); doc.clear(); + doc[F("name")] = iv->config->name; doc[F("ids")] = String(iv->config->serial.u64, HEX); doc[F("cu")] = F("http://") + String(WiFi.localIP().toString()); doc[F("mf")] = F("Hoymiles"); doc[F("mdl")] = iv->config->name; - JsonObject deviceObj = doc.as(); - - for (uint8_t i = 0; i < rec->length; i++) { - if (rec->assign[i].ch == CH0) - snprintf(name, 32, "%s %s", iv->config->name, iv->getFieldName(i, rec)); - else - snprintf(name, 32, "%s CH%d %s", iv->config->name, rec->assign[i].ch, iv->getFieldName(i, rec)); - snprintf(topic, 64, "/ch%d/%s", rec->assign[i].ch, iv->getFieldName(i, rec)); - snprintf(uniq_id, 32, "ch%d_%s", rec->assign[i].ch, iv->getFieldName(i, rec)); - - const char *devCls = getFieldDeviceClass(rec->assign[i].fieldId); - const char *stateCls = getFieldStateClass(rec->assign[i].fieldId); + JsonObject deviceObj = doc.as(); // deviceObj is only pointer!? + + for (uint8_t i = 0; i < (rec->length + 4); i++) { + const char *devCls, *stateCls; + if(i < rec->length) { + if (rec->assign[i].ch == CH0) + snprintf(name, 32, "%s %s", iv->config->name, iv->getFieldName(i, rec)); + else + snprintf(name, 32, "%s CH%d %s", iv->config->name, rec->assign[i].ch, iv->getFieldName(i, rec)); + snprintf(topic, 64, "/ch%d/%s", rec->assign[i].ch, iv->getFieldName(i, rec)); + snprintf(uniq_id, 32, "ch%d_%s", rec->assign[i].ch, iv->getFieldName(i, rec)); + + devCls = getFieldDeviceClass(rec->assign[i].fieldId); + stateCls = getFieldStateClass(rec->assign[i].fieldId); + } + else { // total values + snprintf(name, 32, "Total %s", fields[fldTotal[i-rec->length]]); + snprintf(topic, 64, "/%s", fields[fldTotal[i-rec->length]]); + snprintf(uniq_id, 32, "total_%s", fields[fldTotal[i-rec->length]]); + devCls = getFieldDeviceClass(fldTotal[i-rec->length]); + stateCls = getFieldStateClass(fldTotal[i-rec->length]); + } DynamicJsonDocument doc2(512); - doc2.clear(); doc2[F("name")] = name; - doc2[F("stat_t")] = String(mCfgMqtt->topic) + "/" + String(iv->config->name) + String(topic); - doc2[F("unit_of_meas")] = iv->getUnit(i, rec); + doc2[F("stat_t")] = String(mCfgMqtt->topic) + "/" + ((i < rec->length) ? String(iv->config->name) : "total" ) + String(topic); + doc2[F("unit_of_meas")] = ((i < rec->length) ? (iv->getUnit(i,rec)) : (unitTotal[i-rec->length])); doc2[F("uniq_id")] = String(iv->config->serial.u64, HEX) + "_" + uniq_id; doc2[F("dev")] = deviceObj; doc2[F("exp_aft")] = MQTT_INTERVAL + 5; // add 5 sec if connection is bad or ESP too slow @TODO: stimmt das wirklich als expire!? @@ -249,7 +265,10 @@ class PubMqtt { if (stateCls != NULL) doc2[F("stat_cla")] = String(stateCls); - snprintf(topic, 64, "%s/sensor/%s/ch%d_%s/config", MQTT_DISCOVERY_PREFIX, iv->config->name, rec->assign[i].ch, iv->getFieldName(i, rec)); + if(i < rec->length) + snprintf(topic, 64, "%s/sensor/%s/ch%d_%s/config", MQTT_DISCOVERY_PREFIX, iv->config->name, rec->assign[i].ch, iv->getFieldName(i, rec)); + else // total values + snprintf(topic, 64, "%s/sensor/%s/total_%s/config", MQTT_DISCOVERY_PREFIX, iv->config->name, fields[fldTotal[i-rec->length]]); size_t size = measureJson(doc2) + 1; char *buf = new char[size]; memset(buf, 0, size); @@ -258,72 +277,8 @@ class PubMqtt { delete[] buf; } - yield(); - } -//Début modif - - String node_mac = WiFi.macAddress().substring(12,14)+ WiFi.macAddress().substring(15,17); - String node_id = "AHOY_DTU_" + node_mac; - doc.clear(); - doc[F("name")] = node_id; - doc[F("ids")] = node_id; - doc[F("cu")] = F("http://") + String(WiFi.localIP().toString()); - doc[F("mf")] = F("Hoymiles"); - doc[F("mdl")] = "AHOY_DTU"; - JsonObject deviceObj = doc.as(); - - uint8_t fieldId; - String fieldUnit; - for (uint8_t i = 0; i < 4; i++) { - switch (i) { - default: - case 0: - fieldId = FLD_PAC; - fieldUnit = "W"; - break; - case 1: - fieldId = FLD_YT; - fieldUnit = "kWh"; - break; - case 2: - fieldId = FLD_YD; - fieldUnit = "Wh"; - break; - case 3: - fieldId = FLD_PDC; - fieldUnit = "W"; - break; - } - - snprintf(name, 32, "Total %s", fields[fieldId]); - snprintf(topic, 64, "/%s", fields[fieldId]); - - - const char *devCls = getFieldDeviceClass(fieldId); - const char *stateCls = getFieldStateClass(fieldId); - - DynamicJsonDocument doc2(512); - doc2.clear(); - doc2[F("name")] = String(name); - doc2[F("stat_t")] = String(mCfgMqtt->topic) + "/total" + String(topic); - doc2[F("unit_of_meas")] = fieldUnit; - doc2[F("uniq_id")] = String(node_id) + "_" + String(fields[fieldId]); - doc2[F("dev")] = deviceObj; - doc2[F("exp_aft")] = MQTT_INTERVAL + 5; // add 5 sec if connection is bad or ESP too slow @TODO: stimmt das wirklich als expire!? - if (devCls != NULL) - doc2[F("dev_cla")] = String(devCls); - if (stateCls != NULL) - doc2[F("stat_cla")] = String(stateCls); - - snprintf(topic, 64, "%s/sensor/%s/Total_%s/config", MQTT_DISCOVERY_PREFIX, node_id.c_str(),fields[fieldId]); - size_t size = measureJson(doc2) + 1; - char *buf = new char[size]; - memset(buf, 0, size); - serializeJson(doc2, buf, size); - publish(topic, buf, true, false); - delete[] buf; - } yield(); + } } void setPowerLimitAck(Inverter<> *iv) { @@ -452,14 +407,6 @@ class PubMqtt { return (pos >= DEVICE_CLS_ASSIGN_LIST_LEN) ? NULL : stateClasses[deviceFieldAssignment[pos].stateClsId]; } - const char *getFieldUnit(uint8_t fieldId) { - uint8_t pos = 0; - for (; pos < DEVICE_CLS_ASSIGN_LIST_LEN; pos++) { - if (deviceFieldAssignment[pos].fieldId == fieldId) - break; - } - return (pos >= DEVICE_CLS_ASSIGN_LIST_LEN) ? NULL : deviceClasses[deviceFieldAssignment[pos].deviceClsId]; - } bool processIvStatus() { // returns true if all inverters are available bool allAvail = true; @@ -655,6 +602,7 @@ class PubMqtt { case FLD_FW_BUILD_HOUR_MINUTE: case FLD_HW_ID: case FLD_ACT_ACTIVE_PWR_LIMIT: + fld++; continue; break; } diff --git a/src/web/RestApi.h b/src/web/RestApi.h index ec4fd61f..43d53500 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -600,9 +600,8 @@ class RestApi { } bool setSetup(JsonObject jsonIn, JsonObject jsonOut) { - if(F("scan_wifi") == jsonIn[F("cmd")]) { + if(F("scan_wifi") == jsonIn[F("cmd")]) mApp->scanAvailNetworks(); - } else if(F("set_time") == jsonIn[F("cmd")]) mApp->setTimestamp(jsonIn[F("val")]); else if(F("sync_ntp") == jsonIn[F("cmd")]) diff --git a/src/wifi/ahoywifi.cpp b/src/wifi/ahoywifi.cpp index 6f7725eb..4ca7f290 100644 --- a/src/wifi/ahoywifi.cpp +++ b/src/wifi/ahoywifi.cpp @@ -25,6 +25,8 @@ void ahoywifi::setup(settings_t *config, uint32_t *utcTimestamp, appWifiCb cb) { mStaConn = DISCONNECTED; mCnt = 0; mScanActive = false; + mLastApClients = 0; + mScanCnt = 0; #if defined(ESP8266) wifiConnectHandler = WiFi.onStationModeConnected(std::bind(&ahoywifi::onConnect, this, std::placeholders::_1)); @@ -64,33 +66,79 @@ void ahoywifi::tickWifiLoop() { #if !defined(AP_ONLY) if(mStaConn != GOT_IP) { if (WiFi.softAPgetStationNum() > 0) { // do not reconnect if any AP connection exists - mDns.processNextRequest(); - if((WIFI_AP_STA == WiFi.getMode()) && !mScanActive) { - DBGPRINTLN(F("AP client connected")); - welcome(mApIp.toString()); - WiFi.mode(WIFI_AP); - mAppWifiCb(true); + if(WIFI_AP_STA == WiFi.getMode()) { + // first time switch to AP Mode + if(mScanActive && (mLastApClients != WiFi.softAPgetStationNum())) + mScanActive = false; + + // scan is finished + if(!mScanActive) { + WiFi.mode(WIFI_AP); + mDns.start(53, "*", mApIp); + } + + // only once a client connects to AP + if(mLastApClients != WiFi.softAPgetStationNum()) { + mLastApClients = WiFi.softAPgetStationNum(); + WiFi.scanDelete(); + mAppWifiCb(false); + DBGPRINTLN(F("AP client connected")); + welcome(mApIp.toString()); + } } + mDns.processNextRequest(); return; } else if(WIFI_AP == WiFi.getMode()) { + mLastApClients = 0; mCnt = 0; + DPRINTLN(DBG_INFO, "DNS stop"); + mDns.stop(); WiFi.mode(WIFI_AP_STA); } mCnt++; uint8_t timeout = 10; // seconds - if (mStaConn == CONNECTED) // connected but no ip timeout = 20; + + if(!mScanActive && mBSSIDList.empty() && ((mCnt % timeout) == 0)) { // start scanning APs with the given SSID + DBGPRINT(F("scanning APs with SSID ")); + DBGPRINTLN(String(mConfig->sys.stationSsid)); + mScanCnt = 0; + mScanActive = true; + #if defined(ESP8266) + WiFi.scanNetworks(true, false, 0U, (uint8_t *)mConfig->sys.stationSsid); + #else + WiFi.scanNetworks(true, false, false, 300U, 0U, mConfig->sys.stationSsid); + #endif + return; + } DBGPRINT(F("reconnect in ")); DBGPRINT(String(timeout-mCnt)); DBGPRINTLN(F(" seconds")); + if(mScanActive) { + getBSSIDs(); + //if(!mScanActive) // scan completed + // if ((mCnt % timeout) < 8) + // mCnt = timeout - 2; + } if((mCnt % timeout) == 0) { // try to reconnect after x sec without connection if(mStaConn != CONNECTED) mStaConn = CONNECTING; - WiFi.reconnect(); + if(mBSSIDList.size() > 0) { // get first BSSID in list + DBGPRINT("try to connect to AP with BSSID:"); + uint8_t bssid[6]; + for (int j = 0; j < 6; j++) { + bssid[j] = mBSSIDList.front(); + mBSSIDList.pop_front(); + DBGPRINT(" " + String(bssid[j], HEX)); + } + DBGPRINTLN(""); + WiFi.disconnect(); + WiFi.begin(mConfig->sys.stationSsid, mConfig->sys.stationPwd, 0, &bssid[0]); + } mCnt = 0; } } @@ -135,7 +183,7 @@ void ahoywifi::setupStation(void) { if(!WiFi.config(ip, gateway, mask, dns1, dns2)) DPRINTLN(DBG_ERROR, F("failed to set static IP!")); } - mStaConn = (WiFi.begin(mConfig->sys.stationSsid, mConfig->sys.stationPwd) != WL_CONNECTED) ? DISCONNECTED : CONNECTED; + mBSSIDList.clear(); if(String(mConfig->sys.deviceName) != "") WiFi.hostname(mConfig->sys.deviceName); WiFi.mode(WIFI_AP_STA); @@ -206,6 +254,15 @@ void ahoywifi::sendNTPpacket(IPAddress& address) { mUdp.endPacket(); } +//----------------------------------------------------------------------------- +void ahoywifi::sortRSSI(int *sort, int n) { + for (int i = 0; i < n; i++) + sort[i] = i; + for (int i = 0; i < n; i++) + for (int j = i + 1; j < n; j++) + if (WiFi.RSSI(sort[j]) > WiFi.RSSI(sort[i])) + std::swap(sort[i], sort[j]); +} //----------------------------------------------------------------------------- void ahoywifi::scanAvailNetworks(void) { @@ -217,29 +274,50 @@ void ahoywifi::scanAvailNetworks(void) { } } - //----------------------------------------------------------------------------- void ahoywifi::getAvailNetworks(JsonObject obj) { JsonArray nets = obj.createNestedArray("networks"); int n = WiFi.scanComplete(); + if (n < 0) + return; if(n > 0) { int sort[n]; - for (int i = 0; i < n; i++) - sort[i] = i; - for (int i = 0; i < n; i++) - for (int j = i + 1; j < n; j++) - if (WiFi.RSSI(sort[j]) > WiFi.RSSI(sort[i])) - std::swap(sort[i], sort[j]); + sortRSSI(&sort[0], n); for (int i = 0; i < n; ++i) { - nets[i]["ssid"] = WiFi.SSID(sort[i]); - nets[i]["rssi"] = WiFi.RSSI(sort[i]); + nets[i]["ssid"] = WiFi.SSID(sort[i]); + nets[i]["rssi"] = WiFi.RSSI(sort[i]); } - mScanActive = false; - WiFi.scanDelete(); } + mScanActive = false; + WiFi.scanDelete(); } +//----------------------------------------------------------------------------- +void ahoywifi::getBSSIDs() { + int n = WiFi.scanComplete(); + if (n < 0){ + mScanCnt++; + if (mScanCnt < 20) + return; + } + if(n > 0) { + mBSSIDList.clear(); + int sort[n]; + sortRSSI(&sort[0], n); + for (int i = 0; i < n; i++) { + DBGPRINT("BSSID " + String(i) + ":"); + uint8_t *bssid = WiFi.BSSID(sort[i]); + for (int j = 0; j < 6; j++){ + DBGPRINT(" " + String(bssid[j], HEX)); + mBSSIDList.push_back(bssid[j]); + } + DBGPRINTLN(""); + } + } + mScanActive = false; + WiFi.scanDelete(); +} //----------------------------------------------------------------------------- void ahoywifi::connectionEvent(WiFiStatus_t status) { @@ -248,15 +326,19 @@ void ahoywifi::connectionEvent(WiFiStatus_t status) { if(mStaConn != CONNECTED) { mStaConn = CONNECTED; DBGPRINTLN(F("\n[WiFi] Connected")); - WiFi.mode(WIFI_STA); - DBGPRINTLN(F("[WiFi] AP disabled")); - mDns.stop(); } break; case GOT_IP: mStaConn = GOT_IP; + if (mScanActive) { // maybe another scan has started + WiFi.scanDelete(); + mScanActive = false; + } welcome(WiFi.localIP().toString() + F(" (Station)")); + mDns.stop(); + WiFi.mode(WIFI_STA); + DBGPRINTLN(F("[WiFi] AP disabled")); mAppWifiCb(true); break; diff --git a/src/wifi/ahoywifi.h b/src/wifi/ahoywifi.h index 7155a427..321dbb86 100644 --- a/src/wifi/ahoywifi.h +++ b/src/wifi/ahoywifi.h @@ -50,7 +50,8 @@ class ahoywifi { void onWiFiEvent(WiFiEvent_t event); #endif void welcome(String msg); - + void sortRSSI(int *sort, int n); + void getBSSIDs(void); settings_t *mConfig; appWifiCb mAppWifiCb; @@ -68,9 +69,9 @@ class ahoywifi { uint8_t mLoopCnt; bool mScanActive; + uint8_t mLastApClients; + uint8_t mScanCnt; - void sortRSSI(int *sort, int n); - void getBSSIDs(void); std::list mBSSIDList; };