diff --git a/src/CHANGES.md b/src/CHANGES.md index 1eae1916..97705c3d 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -1,5 +1,10 @@ # Development Changes +## 0.7.9 - 2023-07-08 +* added 'improve' functions to set wifi password directly with ESP web tools #1014 +* fixed MqTT publish while appling power limit #1013 +* slightly improved HMT live view (Voltage & Current) + ## 0.7.8 - 2023-07-05 * fix `YieldDay`, `YieldTotal` and `P_AC` in `TotalValues` #929 * fix some serial debug prints diff --git a/src/app.cpp b/src/app.cpp index 73cdfabd..0c51949a 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -96,12 +96,15 @@ void app::setup() { mPubSerial.setup(mConfig, &mSys, &mTimestamp); + mImprov.setup(this, mConfig->sys.deviceName, mVersion); + regularTickers(); } //----------------------------------------------------------------------------- void app::loop(void) { mInnerLoopCb(); + mImprov.tickSerial(); } //----------------------------------------------------------------------------- @@ -209,7 +212,7 @@ void app::regularTickers(void) { if (mConfig->plugin.display.type != 0) everySec(std::bind(&DisplayType::tickerSecond, &mDisplay), "disp"); every(std::bind(&PubSerialType::tick, &mPubSerial), mConfig->serial.interval, "uart"); - + //everySec(std::bind(&Improv::tickSerial, &mImprov), "impro"); // every([this]() {mPayload.simulation();}, 15, "simul"); } @@ -326,7 +329,7 @@ void app::tickMinute(void) { // only triggered if 'reset values on no avail is enabled' zeroIvValues(true); -} + } //----------------------------------------------------------------------------- void app::tickMidnight(void) { diff --git a/src/app.h b/src/app.h index 60ba9ff0..6f9cda5d 100644 --- a/src/app.h +++ b/src/app.h @@ -25,6 +25,7 @@ #include "utils/crc.h" #include "utils/dbg.h" #include "utils/scheduler.h" +#include "utils/improv.h" #include "web/RestApi.h" #include "web/web.h" #include "wifi/ahoywifi.h" @@ -121,8 +122,8 @@ class app : public IApp, public ah::Scheduler { mWifi.scanAvailNetworks(); } - void getAvailNetworks(JsonObject obj) { - mWifi.getAvailNetworks(obj); + bool getAvailNetworks(JsonObject obj) { + return mWifi.getAvailNetworks(obj); } void setOnUpdate() { @@ -284,20 +285,6 @@ class app : public IApp, public ah::Scheduler { void tickMinute(void); void tickZeroValues(void); void tickMidnight(void); - /*void tickSerial(void) { - if(Serial.available() == 0) - return; - - uint8_t buf[80]; - uint8_t len = Serial.readBytes(buf, 80); - DPRINTLN(DBG_INFO, "got serial data, len: " + String(len)); - for(uint8_t i = 0; i < len; i++) { - if((0 != i) && (i % 8 == 0)) - DBGPRINTLN(""); - DBGPRINT(String(buf[i], HEX) + " "); - } - DBGPRINTLN(""); - }*/ innerLoopCb mInnerLoopCb; @@ -313,6 +300,7 @@ class app : public IApp, public ah::Scheduler { PayloadType mPayload; MiPayloadType mMiPayload; PubSerialType mPubSerial; + Improv mImprov; #ifdef ESP32 CmtRadioType mCmtRadio; HmsPayloadType mHmsPayload; diff --git a/src/appInterface.h b/src/appInterface.h index d88ff6a8..5a70aa43 100644 --- a/src/appInterface.h +++ b/src/appInterface.h @@ -26,7 +26,7 @@ class IApp { virtual const char *getVersion() = 0; virtual statistics_t *getStatistics() = 0; virtual void scanAvailNetworks() = 0; - virtual void getAvailNetworks(JsonObject obj) = 0; + virtual bool getAvailNetworks(JsonObject obj) = 0; virtual uint32_t getUptime() = 0; virtual uint32_t getTimestamp() = 0; diff --git a/src/defines.h b/src/defines.h index 45782b7a..40e56d23 100644 --- a/src/defines.h +++ b/src/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 7 -#define VERSION_PATCH 8 +#define VERSION_PATCH 9 //------------------------------------- typedef struct { diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h index dba24bb3..280d1240 100644 --- a/src/hm/hmRadio.h +++ b/src/hm/hmRadio.h @@ -1,6 +1,6 @@ //----------------------------------------------------------------------------- // 2023 Ahoy, https://github.com/lumpapu/ahoy -// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ +// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/4.0/deed //----------------------------------------------------------------------------- #ifndef __RADIO_H__ diff --git a/src/publisher/pubMqttIvData.h b/src/publisher/pubMqttIvData.h index 111bbd37..1a697388 100644 --- a/src/publisher/pubMqttIvData.h +++ b/src/publisher/pubMqttIvData.h @@ -72,6 +72,7 @@ class PubMqttIvData { mIvSend = mSendList->front().iv; if((RealTimeRunData_Debug != mCmd) || !mRTRDataHasBeenSent) { // send RealTimeRunData only once + mSendTotals = (RealTimeRunData_Debug == mCmd); memset(mTotal, 0, sizeof(float) * 4); mState = FIND_NXT_IV; } else @@ -98,7 +99,7 @@ class PubMqttIvData { mPos = 0; if(found) mState = SEND_DATA; - else + else if(mSendTotals) mState = SEND_TOTALS; } @@ -201,6 +202,7 @@ class PubMqttIvData { uint8_t mCmd; uint8_t mLastIvId; + bool mSendTotals; float mTotal[4]; Inverter<> *mIv, *mIvSend; diff --git a/src/utils/dbg.cpp b/src/utils/dbg.cpp index 9f7c9fd3..e7ae4581 100644 --- a/src/utils/dbg.cpp +++ b/src/utils/dbg.cpp @@ -1,3 +1,4 @@ #include "dbg.h" DBG_CB mCb = NULL; +bool mDebugEn = true; diff --git a/src/utils/dbg.h b/src/utils/dbg.h index a18d2f77..6c861329 100644 --- a/src/utils/dbg.h +++ b/src/utils/dbg.h @@ -39,6 +39,7 @@ #ifdef ARDUINO #define DBG_CB std::function extern DBG_CB mCb; + extern bool mDebugEn; inline void registerDebugCb(DBG_CB cb) { mCb = cb; @@ -48,22 +49,28 @@ #define DSERIAL Serial #endif + inline void setDebugEn(bool en) { + mDebugEn = en; + } + //template - inline void DBGPRINT(String str) { DSERIAL.print(str); if(NULL != mCb) mCb(str); } + inline void DBGPRINT(String str, bool ser = true) { if(ser && mDebugEn) DSERIAL.print(str); if(NULL != mCb) mCb(str); } //template - inline void DBGPRINTLN(String str) { DBGPRINT(str); DBGPRINT(F("\r\n")); } - inline void DHEX(uint8_t b) { - if( b<0x10 ) DSERIAL.print(F("0")); - DSERIAL.print(b,HEX); + inline void DBGPRINTLN(String str, bool ser = true) { DBGPRINT(str); DBGPRINT(F("\r\n")); } + inline void DHEX(uint8_t b, bool ser = true) { + if(ser && mDebugEn) { + if( b<0x10 ) DSERIAL.print(F("0")); + DSERIAL.print(b,HEX); + } if(NULL != mCb) { if( b<0x10 ) mCb(F("0")); mCb(String(b, HEX)); } } - inline void DBGHEXLN(uint8_t b) { - DHEX(b); - DBGPRINT(F("\r\n")); + inline void DBGHEXLN(uint8_t b, bool ser = true) { + DHEX(b, ser); + DBGPRINT(F("\r\n"), ser); } /*inline void DHEX(uint16_t b) { if( b<0x10 ) DSERIAL.print(F("000")); diff --git a/src/utils/improv.h b/src/utils/improv.h new file mode 100644 index 00000000..ade1291e --- /dev/null +++ b/src/utils/improv.h @@ -0,0 +1,219 @@ +//----------------------------------------------------------------------------- +// 2023 Ahoy, https://github.com/lumpapu/ahoy +// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/4.0/deed +//----------------------------------------------------------------------------- + +#ifndef __IMPROV_H__ +#define __IMPROV_H__ + +#include +#include +#include "dbg.h" +#include "AsyncJson.h" + +// https://www.improv-wifi.com/serial/ +// https://github.com/jnthas/improv-wifi-demo/blob/main/src/esp32-wifiimprov/esp32-wifiimprov.ino + +// configure ESP through Serial interface +class Improv { + public: + void setup(IApp *app, const char *devName, const char *version) { + mApp = app; + mDevName = devName; + mVersion = version; + + mScanRunning = false; + } + + void tickSerial(void) { + if(mScanRunning) + getNetworks(); + + if(Serial.available() == 0) + return; + + uint8_t buf[40]; + uint8_t len = Serial.readBytes(buf, 40); + + if(!checkPaket(&buf[0], len, [this](uint8_t type, uint8_t buf[], uint8_t len) { + parsePayload(type, buf, len); + })) { + DBGPRINTLN(F("check paket failed")); + } + dumpBuf(buf, len); + } + + private: + enum State : uint8_t { + STATE_STOPPED = 0x00, + STATE_AWAITING_AUTHORIZATION = 0x01, + STATE_AUTHORIZED = 0x02, + STATE_PROVISIONING = 0x03, + STATE_PROVISIONED = 0x04, + }; + + enum Command : uint8_t { + UNKNOWN = 0x00, + WIFI_SETTINGS = 0x01, + IDENTIFY = 0x02, + GET_CURRENT_STATE = 0x02, + GET_DEVICE_INFO = 0x03, + GET_WIFI_NETWORKS = 0x04, + BAD_CHECKSUM = 0xFF, + }; + + enum ImprovSerialType : uint8_t { + TYPE_CURRENT_STATE = 0x01, + TYPE_ERROR_STATE = 0x02, + TYPE_RPC = 0x03, + TYPE_RPC_RESPONSE = 0x04 + }; + + void dumpBuf(uint8_t buf[], uint8_t len) { + for(uint8_t i = 0; i < len; i++) { + DHEX(buf[i], false); + DBGPRINT(" ", false); + } + DBGPRINTLN("", false); + } + + inline uint8_t buildChecksum(uint8_t buf[], uint8_t len) { + uint8_t calc = 0; + for(uint8_t i = 0; i < len; i++) { + calc += buf[i]; + } + return calc; + } + + inline bool checkChecksum(uint8_t buf[], uint8_t len) { + /*DHEX(buf[len], false); + DBGPRINT(F(" == "), false); + DBGHEXLN(buildChecksum(buf, len), false);*/ + return ((buildChecksum(buf, len)) == buf[len]); + } + + bool checkPaket(uint8_t buf[], uint8_t len, std::function cb) { + if(len < 11) + return false; + + if(0 != strncmp((char*)buf, "IMPROV", 6)) + return false; + + // verison check (only version 1 is supported!) + if(0x01 != buf[6]) + return false; + + if(!checkChecksum(buf, (9 + buf[8]))) + return false; + + cb(buf[7], &buf[9], buf[8]); + + return true; + } + + uint8_t char2Improv(const char *str, uint8_t buf[]) { + uint8_t len = strlen(str); + buf[0] = len; + for(uint8_t i = 1; i <= len; i++) { + buf[i] = (uint8_t)str[i-1]; + } + return len + 1; + } + + void sendDevInfo(void) { + uint8_t buf[50]; + buf[7] = TYPE_RPC_RESPONSE; + buf[9] = GET_DEVICE_INFO; // repsonse to cmd + uint8_t p = 11; + // firmware name + p += char2Improv("AhoyDTU", &buf[p]); + // firmware version + p += char2Improv(mVersion, &buf[p]); + // chip variant + #if defined(ESP32) + p += char2Improv("ESP32", &buf[p]); + #else + p += char2Improv("ESP8266", &buf[p]); + #endif + // device name + p += char2Improv(mDevName, &buf[p]); + + buf[10] = p - 11; // sub length + buf[8] = p - 9; // paket length + + sendPaket(buf, p); + } + + void getNetworks(void) { + if(!mScanRunning) + mApp->scanAvailNetworks(); + + JsonObject obj; + if(!mApp->getAvailNetworks(obj)) + return; + + mScanRunning = false; + + uint8_t buf[50]; + buf[7] = TYPE_RPC_RESPONSE; + buf[9] = GET_WIFI_NETWORKS; // repsonse to cmd + uint8_t p = 11; + + JsonArray arr = obj[F("networks")]; + for(uint8_t i = 0; i < arr.size(); i++) { + buf[p++] = strlen(arr[i][F("ssid")]); + // ssid + p += char2Improv(arr[i][F("ssid")], &buf[p]); + buf[p++] = String(arr[i][F("rssi")]).length(); + // rssi + p += char2Improv(String(arr[i][F("rssi")]).c_str(), &buf[p]); + + buf[10] = p - 11; // sub length + buf[8] = p - 9; // paket length + + sendPaket(buf, p); + } + } + + void setState(uint8_t state) { + uint8_t buf[20]; + buf[7] = TYPE_CURRENT_STATE; + buf[8] = 0x01; + buf[9] = state; + sendPaket(buf, 10); + } + + void sendPaket(uint8_t buf[], uint8_t len) { + buf[0] = 'I'; + buf[1] = 'M'; + buf[2] = 'P'; + buf[3] = 'R'; + buf[4] = 'O'; + buf[5] = 'V'; + buf[6] = 1; // protocol version + + buf[len] = buildChecksum(buf, len); + len++; + Serial.write(buf, len); + dumpBuf(buf, len); + } + + void parsePayload(uint8_t type, uint8_t buf[], uint8_t len) { + if(TYPE_RPC == type) { + if(GET_CURRENT_STATE == buf[0]) { + setDebugEn(false); + setState(STATE_AUTHORIZED); + } + else if(GET_DEVICE_INFO == buf[0]) + sendDevInfo(); + else if(GET_WIFI_NETWORKS == buf[0]) + getNetworks(); + } + } + + IApp *mApp; + const char *mDevName, *mVersion; + bool mScanRunning; +}; + +#endif /*__IMPROV_H__*/ diff --git a/src/web/RestApi.h b/src/web/RestApi.h index 886d89e8..c4964dc3 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -24,6 +24,7 @@ #endif const uint8_t acList[] = {FLD_UAC, FLD_IAC, FLD_PAC, FLD_F, FLD_PF, FLD_T, FLD_YT, FLD_YD, FLD_PDC, FLD_EFF, FLD_Q}; +const uint8_t acListHmt[] = {FLD_UAC_1N, FLD_IAC_1, FLD_PAC, FLD_F, FLD_PF, FLD_T, FLD_YT, FLD_YD, FLD_PDC, FLD_EFF, FLD_Q}; const uint8_t dcList[] = {FLD_UDC, FLD_IDC, FLD_PDC, FLD_YD, FLD_YT, FLD_IRR}; template @@ -342,6 +343,7 @@ class RestApi { obj[F("version")] = String(iv->getFwVersion()); obj[F("power_limit_read")] = ah::round3(iv->actPowerLimit); obj[F("ts_last_success")] = rec->ts; + obj[F("generation")] = iv->ivGen; JsonArray ch = obj.createNestedArray("ch"); @@ -349,9 +351,16 @@ class RestApi { uint8_t pos; obj[F("ch_name")][0] = "AC"; JsonArray ch0 = ch.createNestedArray(); - for (uint8_t fld = 0; fld < sizeof(acList); fld++) { - pos = (iv->getPosByChFld(CH0, acList[fld], rec)); - ch0[fld] = (0xff != pos) ? ah::round3(iv->getValue(pos, rec)) : 0.0; + if(IV_HMT == iv->ivGen) { + for (uint8_t fld = 0; fld < sizeof(acListHmt); fld++) { + pos = (iv->getPosByChFld(CH0, acListHmt[fld], rec)); + ch0[fld] = (0xff != pos) ? ah::round3(iv->getValue(pos, rec)) : 0.0; + } + } else { + for (uint8_t fld = 0; fld < sizeof(acList); fld++) { + pos = (iv->getPosByChFld(CH0, acList[fld], rec)); + ch0[fld] = (0xff != pos) ? ah::round3(iv->getValue(pos, rec)) : 0.0; + } } // DC diff --git a/src/wifi/ahoywifi.cpp b/src/wifi/ahoywifi.cpp index 933ffff7..cd0656b0 100644 --- a/src/wifi/ahoywifi.cpp +++ b/src/wifi/ahoywifi.cpp @@ -288,12 +288,12 @@ void ahoywifi::scanAvailNetworks(void) { } //----------------------------------------------------------------------------- -void ahoywifi::getAvailNetworks(JsonObject obj) { +bool ahoywifi::getAvailNetworks(JsonObject obj) { JsonArray nets = obj.createNestedArray("networks"); int n = WiFi.scanComplete(); if (n < 0) - return; + return false; if(n > 0) { int sort[n]; sortRSSI(&sort[0], n); @@ -306,6 +306,8 @@ void ahoywifi::getAvailNetworks(JsonObject obj) { WiFi.scanDelete(); if(mStaConn == IN_AP_MODE) WiFi.mode(WIFI_AP); + + return true; } //----------------------------------------------------------------------------- diff --git a/src/wifi/ahoywifi.h b/src/wifi/ahoywifi.h index 3a3a35a8..3fde2ab3 100644 --- a/src/wifi/ahoywifi.h +++ b/src/wifi/ahoywifi.h @@ -27,7 +27,7 @@ class ahoywifi { void tickWifiLoop(void); bool getNtpTime(void); void scanAvailNetworks(void); - void getAvailNetworks(JsonObject obj); + bool getAvailNetworks(JsonObject obj); private: typedef enum WiFiStatus {