diff --git a/src/CHANGES.md b/src/CHANGES.md index 3b810e1d..447054fa 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -2,6 +2,12 @@ (starting from release version `0.5.66`) +## 0.5.89 +* reduced heap fragmentation (removed `strtok` completely) #644, #645, #682 +* added part of mac address to MQTT client ID to seperate multiple ESPs in same network +* added dictionary for MQTT to reduce heap-fragmentation +* removed `last Alarm` from Live view, because it showed always the same alarm - will change in future + ## 0.5.88 * MQTT Yield Day zero, next try to fix #671, thx @beegee3 * added Solenso inverter to supported devices diff --git a/src/app.cpp b/src/app.cpp index 7352589d..43b1c4e3 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// 2022 Ahoy, https://ahoydtu.de +// 2023 Ahoy, https://ahoydtu.de // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ //----------------------------------------------------------------------------- @@ -21,9 +21,19 @@ void app::setup() { resetSystem(); + /*DBGPRINTLN("--- start"); + DBGPRINTLN(String(ESP.getFreeHeap())); + DBGPRINTLN(String(ESP.getHeapFragmentation())); + DBGPRINTLN(String(ESP.getMaxFreeBlockSize()));*/ + + mSettings.setup(); mSettings.getPtr(mConfig); - DPRINTLN(DBG_INFO, F("Settings valid: ") + String((mSettings.getValid()) ? F("true") : F("false"))); + DPRINT(DBG_INFO, F("Settings valid: ")); + if(mSettings.getValid()) + DBGPRINTLN(F("true")); + else + DBGPRINTLN(F("false")); mSys.enableDebug(); mSys.setup(mConfig->nrf.amplifierPower, mConfig->nrf.pinIrq, mConfig->nrf.pinCe, mConfig->nrf.pinCs); @@ -47,6 +57,11 @@ void app::setup() { mMiPayload.setup(this, &mSys, &mStat, mConfig->nrf.maxRetransPerPyld, &mTimestamp); mMiPayload.enableSerialDebug(mConfig->serial.debug); + /*DBGPRINTLN("--- after payload"); + DBGPRINTLN(String(ESP.getFreeHeap())); + DBGPRINTLN(String(ESP.getHeapFragmentation())); + DBGPRINTLN(String(ESP.getMaxFreeBlockSize()));*/ + if(!mSys.Radio.isChipConnected()) DPRINTLN(DBG_WARN, F("WARNING! your NRF24 module can't be reached, check the wiring")); @@ -73,6 +88,12 @@ void app::setup() { mPubSerial.setup(mConfig, &mSys, &mTimestamp); regularTickers(); + + + /*DBGPRINTLN("--- end setup"); + DBGPRINTLN(String(ESP.getFreeHeap())); + DBGPRINTLN(String(ESP.getHeapFragmentation())); + DBGPRINTLN(String(ESP.getMaxFreeBlockSize()));*/ } //----------------------------------------------------------------------------- @@ -89,7 +110,11 @@ void app::loopStandard(void) { packet_t *p = &mSys.Radio.mBufCtrl.front(); if (mConfig->serial.debug) { - DPRINT(DBG_INFO, "RX " + String(p->len) + "B Ch" + String(p->ch) + " | "); + DPRINT(DBG_INFO, F("RX ")); + DBGPRINT(String(p->len)); + DBGPRINT(F("B Ch")); + DBGPRINT(String(p->ch)); + DBGPRINT(F(" | ")); mSys.Radio.dumpBuf(p->packet, p->len); } mStat.frmCnt++; @@ -158,13 +183,13 @@ void app::tickNtpUpdate(void) { bool isOK = mWifi.getNtpTime(); if (isOK || mTimestamp != 0) { if (mMqttReconnect && mMqttEnabled) { - mMqtt.connect(); + mMqtt.tickerMinute(); everySec(std::bind(&PubMqttType::tickerSecond, &mMqtt), "mqttS"); everyMin(std::bind(&PubMqttType::tickerMinute, &mMqtt), "mqttM"); } // only install schedulers once even if NTP wasn't successful in first loop - if(mMqttReconnect) { // @TODO: mMqttReconnect is wrong name here + if(mMqttReconnect) { // @TODO: mMqttReconnect is variable which scope has changed if(mConfig->inst.rstValsNotAvail) everyMin(std::bind(&app::tickMinute, this), "tMin"); if(mConfig->inst.rstYieldMidNight) { @@ -282,8 +307,6 @@ void app::tickMidnight(void) { uint32_t nxtTrig = mTimestamp - ((mTimestamp - 1) % 86400) + 86400; // next midnight onceAt(std::bind(&app::tickMidnight, this), nxtTrig, "mid2"); - DPRINTLN(DBG_INFO, "tickMidnight " + String(nxtTrig)); - Inverter<> *iv; // set values to zero, except yield total for (uint8_t id = 0; id < mSys.getNumInverters(); id++) { @@ -302,13 +325,15 @@ void app::tickMidnight(void) { //----------------------------------------------------------------------------- void app::tickSend(void) { if(!mSys.Radio.isChipConnected()) { - DPRINTLN(DBG_WARN, "NRF24 not connected!"); + DPRINTLN(DBG_WARN, F("NRF24 not connected!")); return; } if (mIVCommunicationOn) { if (!mSys.Radio.mBufCtrl.empty()) { - if (mConfig->serial.debug) - DPRINTLN(DBG_DEBUG, F("recbuf not empty! #") + String(mSys.Radio.mBufCtrl.size())); + if (mConfig->serial.debug) { + DPRINT(DBG_DEBUG, F("recbuf not empty! #")); + DBGPRINTLN(String(mSys.Radio.mBufCtrl.size())); + } } int8_t maxLoop = MAX_NUM_INVERTERS; diff --git a/src/app.h b/src/app.h index 15dbf8f7..68281492 100644 --- a/src/app.h +++ b/src/app.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// 2022 Ahoy, https://ahoydtu.de +// 2023 Ahoy, https://ahoydtu.de // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ //----------------------------------------------------------------------------- @@ -182,7 +182,8 @@ class app : public IApp, public ah::Scheduler { } void setTimestamp(uint32_t newTime) { - DPRINTLN(DBG_DEBUG, F("setTimestamp: ") + String(newTime)); + DPRINT(DBG_DEBUG, F("setTimestamp: ")); + DBGPRINTLN(String(newTime)); if(0 == newTime) mWifi.getNtpTime(); else diff --git a/src/config/settings.h b/src/config/settings.h index 9e58515b..527dea72 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// 2022 Ahoy, https://ahoydtu.de +// 2023 Ahoy, https://ahoydtu.de // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ //----------------------------------------------------------------------------- @@ -212,7 +212,6 @@ class settings { } bool readSettings(const char* path) { - bool success = false; loadDefaults(); File fp = LittleFS.open(path, "r"); if(!fp) @@ -233,7 +232,6 @@ class settings { jsonLed(root[F("led")]); jsonPlugin(root[F("plugin")]); jsonInst(root[F("inst")]); - success = true; } else { Serial.println(F("failed to parse json, using default config")); @@ -241,7 +239,7 @@ class settings { fp.close(); } - return success; + return mCfg.valid; } bool saveSettings(void) { diff --git a/src/defines.h b/src/defines.h index 30ad1b64..8698cbe1 100644 --- a/src/defines.h +++ b/src/defines.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// 2022 Ahoy, https://www.mikrocontroller.net/topic/525778 +// 2023 Ahoy, https://www.mikrocontroller.net/topic/525778 // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ //----------------------------------------------------------------------------- @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 5 -#define VERSION_PATCH 88 +#define VERSION_PATCH 89 //------------------------------------- typedef struct { diff --git a/src/hm/hmInverter.h b/src/hm/hmInverter.h index ae3dc1a4..c764cee0 100644 --- a/src/hm/hmInverter.h +++ b/src/hm/hmInverter.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// 2022 Ahoy, https://www.mikrocontroller.net/topic/525778 +// 2023 Ahoy, https://www.mikrocontroller.net/topic/525778 // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ //----------------------------------------------------------------------------- @@ -119,7 +119,7 @@ class Inverter { record_t recordInfo; // structure for info values record_t recordConfig; // structure for system config values record_t recordAlarm; // structure for alarm values - String lastAlarmMsg; + //String lastAlarmMsg; bool initialized; // needed to check if the inverter was correctly added (ESP32 specific - union types are never null) bool isConnected; // shows if inverter was successfully identified (fw version and hardware info) @@ -131,7 +131,7 @@ class Inverter { mDevControlRequest = false; devControlCmd = InitDataState; initialized = false; - lastAlarmMsg = "nothing"; + //lastAlarmMsg = "nothing"; alarmMesIndex = 0; isConnected = false; } @@ -294,9 +294,9 @@ class Inverter { } else if (rec->assign == AlarmDataAssignment) { DPRINTLN(DBG_DEBUG, "add alarm"); - if (getPosByChFld(0, FLD_LAST_ALARM_CODE, rec) == pos){ - lastAlarmMsg = getAlarmStr(rec->record[pos]); - } + //if (getPosByChFld(0, FLD_LAST_ALARM_CODE, rec) == pos){ + // lastAlarmMsg = getAlarmStr(rec->record[pos]); + //} } else DPRINTLN(DBG_WARN, F("add with unknown assginment")); @@ -305,6 +305,11 @@ class Inverter { DPRINTLN(DBG_ERROR, F("addValue: assignment not found with cmd 0x")); } + /*inline REC_TYP getPowerLimit(void) { + record_t<> *rec = getRecordStruct(SystemConfigPara); + return getChannelFieldValue(CH0, FLD_ACT_ACTIVE_PWR_LIMIT, rec); + }*/ + bool setValue(uint8_t pos, record_t<> *rec, REC_TYP val) { DPRINTLN(DBG_VERBOSE, F("hmInverter.h:setValue")); if(NULL == rec) diff --git a/src/hm/hmPayload.h b/src/hm/hmPayload.h index ab11ee66..7a05bee3 100644 --- a/src/hm/hmPayload.h +++ b/src/hm/hmPayload.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// 2022 Ahoy, https://ahoydtu.de +// 2023 Ahoy, https://ahoydtu.de // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ //----------------------------------------------------------------------------- @@ -71,7 +71,7 @@ class HmPayload { } void zeroYieldDay(Inverter<> *iv) { - DPRINTLN(DBG_INFO, "zeroYieldDay"); + DPRINTLN(DBG_DEBUG, F("zeroYieldDay")); record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); uint8_t pos; for(uint8_t ch = 0; ch < iv->channels; ch++) { @@ -81,7 +81,7 @@ class HmPayload { } void zeroInverterValues(Inverter<> *iv) { - DPRINTLN(DBG_INFO, "zeroInverterValues"); + DPRINTLN(DBG_DEBUG, F("zeroInverterValues")); record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); for(uint8_t ch = 0; ch <= iv->channels; ch++) { uint8_t pos = 0; @@ -119,8 +119,11 @@ class HmPayload { if (mSerialDebug) DPRINTLN(DBG_INFO, F("enqueued cmd failed/timeout")); if (mSerialDebug) { - DPRINT(DBG_INFO, F("(#") + String(iv->id) + ") "); - DPRINTLN(DBG_INFO, F("no Payload received! (retransmits: ") + String(mPayload[iv->id].retransmits) + ")"); + DPRINT(DBG_INFO, F("(#")); + DBGPRINT(String(iv->id)); + DBGPRINT(F(") no Payload received! (retransmits: ")); + DBGPRINT(String(mPayload[iv->id].retransmits)); + DBGPRINTLN(F(")")); } } } @@ -130,19 +133,31 @@ class HmPayload { mPayload[iv->id].requested = true; yield(); - if (mSerialDebug) - DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") Requesting Inv SN ") + String(iv->config->serial.u64, HEX)); + if (mSerialDebug) { + DPRINT(DBG_INFO, F("(#")); + DBGPRINT(String(iv->id)); + DBGPRINT(F(") Requesting Inv SN ")); + DBGPRINTLN(String(iv->config->serial.u64, HEX)); + } if (iv->getDevControlRequest()) { - if (mSerialDebug) - DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") Devcontrol request 0x") + String(iv->devControlCmd, HEX) + F(" power limit ") + String(iv->powerLimit[0])); + if (mSerialDebug) { + DPRINT(DBG_INFO, F("(#")); + DBGPRINT(String(iv->id)); + DBGPRINT(F(") Devcontrol request 0x")); + DBGPRINT(String(iv->devControlCmd, HEX)); + DBGPRINT(F(" power limit ")); + DBGPRINTLN(String(iv->powerLimit[0])); + } mSys->Radio.sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit, false); mPayload[iv->id].txCmd = iv->devControlCmd; //iv->clearCmdQueue(); //iv->enqueCommand(SystemConfigPara); // read back power limit } else { uint8_t cmd = iv->getQueuedCmd(); - DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") prepareDevInformCmd")); // + String(cmd, HEX)); + DPRINT(DBG_INFO, F("(#")); + DBGPRINT(String(iv->id)); + DBGPRINT(F(") prepareDevInformCmd")); // + String(cmd, HEX)); mSys->Radio.prepareDevInformCmd(iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmMesIndex, false); mPayload[iv->id].txCmd = cmd; } @@ -179,12 +194,20 @@ class HmPayload { iv->clearDevControlRequest(); if ((p->packet[12] == ActivePowerContr) && (p->packet[13] == 0x00)) { - String msg = ""; + bool ok = true; if((p->packet[10] == 0x00) && (p->packet[11] == 0x00)) mApp->setMqttPowerLimitAck(iv); else - msg = "NOT "; - DPRINTLN(DBG_INFO, F("Inverter ") + String(iv->id) + F(" has ") + msg + F("accepted power limit set point ") + String(iv->powerLimit[0]) + F(" with PowerLimitControl ") + String(iv->powerLimit[1])); + ok = false; + DPRINT(DBG_INFO, F("(#")); + DBGPRINT(String(iv->id)); + DBGPRINT(F(" has ")); + if(!ok) DBGPRINT(F("not ")); + DBGPRINT(F("accepted power limit set point ")); + DBGPRINT(String(iv->powerLimit[0])); + DBGPRINT(F(" with PowerLimitControl ")); + DBGPRINT(String(iv->powerLimit[1])); + iv->clearCmdQueue(); iv->enqueCommand(SystemConfigPara); // read back power limit } @@ -229,12 +252,16 @@ class HmPayload { DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") prepareDevInformCmd 0x") + String(mPayload[iv->id].txCmd, HEX)); mSys->Radio.prepareDevInformCmd(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex, true); */ - DPRINTLN(DBG_WARN, F("(#") + String(iv->id) + F(") nothing received")); + DPRINT(DBG_INFO, F("(#")); + DBGPRINT(String(iv->id)); + DBGPRINTLN(F(") nothing received")); mPayload[iv->id].retransmits = mMaxRetrans; } else { for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId - 1); i++) { if (mPayload[iv->id].len[i] == 0) { - DPRINTLN(DBG_WARN, F("Frame ") + String(i + 1) + F(" missing: Request Retransmit")); + DPRINT(DBG_WARN, F("Frame ")); + DBGPRINT(String(i + 1)); + DBGPRINTLN(F(" missing: Request Retransmit")); mSys->Radio.sendCmdPacket(iv->radioId.u64, TX_REQ_INFO, (SINGLE_FRAME + i), true); break; // only request retransmit one frame per loop } @@ -249,12 +276,17 @@ class HmPayload { mPayload[iv->id].retransmits++; DPRINTLN(DBG_WARN, F("CRC Error: Request Complete Retransmit")); mPayload[iv->id].txCmd = iv->getQueuedCmd(); - DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") prepareDevInformCmd 0x") + String(mPayload[iv->id].txCmd, HEX)); + DPRINT(DBG_INFO, F("(#")); + DBGPRINT(String(iv->id)); + DBGPRINT(F(") prepareDevInformCmd 0x")); + DBGPRINTLN(String(mPayload[iv->id].txCmd, HEX)); mSys->Radio.prepareDevInformCmd(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex, true); } } else { // payload complete - DPRINTLN(DBG_INFO, F("procPyld: cmd: 0x") + String(mPayload[iv->id].txCmd, HEX)); - DPRINTLN(DBG_INFO, F("procPyld: txid: 0x") + String(mPayload[iv->id].txId, HEX)); + DPRINT(DBG_INFO, F("procPyld: cmd: 0x")); + DBGPRINTLN(String(mPayload[iv->id].txCmd, HEX)); + DPRINT(DBG_INFO, F("procPyld: txid: 0x")); + DBGPRINTLN(String(mPayload[iv->id].txId, HEX)); DPRINTLN(DBG_DEBUG, F("procPyld: max: ") + String(mPayload[iv->id].maxPackId)); record_t<> *rec = iv->getRecordStruct(mPayload[iv->id].txCmd); // choose the parser mPayload[iv->id].complete = true; @@ -272,7 +304,9 @@ class HmPayload { payloadLen -= 2; if (mSerialDebug) { - DPRINT(DBG_INFO, F("Payload (") + String(payloadLen) + "): "); + DPRINT(DBG_INFO, F("Payload (")); + DBGPRINT(String(payloadLen)); + DBGPRINT(F("): ")); mSys->Radio.dumpBuf(payload, payloadLen); } @@ -304,7 +338,9 @@ class HmPayload { } } } else { - DPRINTLN(DBG_ERROR, F("plausibility check failed, expected ") + String(rec->pyldLen) + F(" bytes")); + DPRINT(DBG_ERROR, F("plausibility check failed, expected ")); + DBGPRINT(String(rec->pyldLen)); + DBGPRINTLN(F(" bytes")); mStat->rxFail++; } @@ -356,7 +392,8 @@ class HmPayload { } void reset(uint8_t id) { - DPRINTLN(DBG_INFO, "resetPayload: id: " + String(id)); + DPRINT(DBG_INFO, "resetPayload: id: "); + DBGPRINTLN(String(id)); memset(mPayload[id].len, 0, MAX_PAYLOAD_ENTRIES); mPayload[id].txCmd = 0; mPayload[id].gotFragment = false; diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h index 3ad39f31..f0236362 100644 --- a/src/hm/hmRadio.h +++ b/src/hm/hmRadio.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// 2022 Ahoy, https://github.com/lumpapu/ahoy +// 2023 Ahoy, https://github.com/lumpapu/ahoy // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ //----------------------------------------------------------------------------- @@ -176,7 +176,8 @@ class HmRadio { } void sendControlPacket(uint64_t invId, uint8_t cmd, uint16_t *data, bool isRetransmit) { - DPRINTLN(DBG_INFO, F("sendControlPacket cmd: 0x") + String(cmd, HEX)); + DPRINT(DBG_INFO, F("sendControlPacket cmd: 0x")); + DBGPRINTLN(String(cmd, HEX)); initPacket(invId, TX_REQ_DEVCONTROL, SINGLE_FRAME); uint8_t cnt = 10; mTxBuf[cnt++] = cmd; // cmd -> 0 on, 1 off, 2 restart, 11 active power, 12 reactive power, 13 power factor @@ -294,7 +295,11 @@ class HmRadio { len++; if(mSerialDebug) { - DPRINT(DBG_INFO, "TX " + String(len) + "B Ch" + String(mRfChLst[mTxChIdx]) + " | "); + DPRINT(DBG_INFO, F("TX ")); + DBGPRINT(String(len)); + DBGPRINT("B Ch"); + DBGPRINT(String(mRfChLst[mTxChIdx])); + DBGPRINT(F(" | ")); dumpBuf(mTxBuf, len); } diff --git a/src/publisher/pubMqtt.h b/src/publisher/pubMqtt.h index 9ea674e8..df2cf0dc 100644 --- a/src/publisher/pubMqtt.h +++ b/src/publisher/pubMqtt.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// 2022 Ahoy, https://ahoydtu.de +// 2023 Ahoy, https://ahoydtu.de // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ //----------------------------------------------------------------------------- @@ -21,6 +21,8 @@ #include "../defines.h" #include "../hm/hmSystem.h" +#include "pubMqttDefs.h" + #define QOS_0 0 typedef std::function subscriptionCb; @@ -57,9 +59,11 @@ class PubMqtt { if((strlen(mCfgMqtt->user) > 0) && (strlen(mCfgMqtt->pwd) > 0)) mClient.setCredentials(mCfgMqtt->user, mCfgMqtt->pwd); - mClient.setClientId(mDevName); // TODO: add mac? + char clientId[64]; + snprintf(clientId, 64, "%s_%s-%s-%s", mDevName, WiFi.macAddress().substring(9,11).c_str(), WiFi.macAddress().substring(12,14).c_str(), WiFi.macAddress().substring(15,17).c_str()); + mClient.setClientId(clientId); mClient.setServer(mCfgMqtt->broker, mCfgMqtt->port); - mClient.setWill(mLwtTopic, QOS_0, true, mLwtOffline); + mClient.setWill(mLwtTopic, QOS_0, true, mqttStr[MQTT_STR_LWT_NOT_CONN]); mClient.onConnect(std::bind(&PubMqtt::onConnect, this, std::placeholders::_1)); mClient.onDisconnect(std::bind(&PubMqtt::onDisconnect, this, std::placeholders::_1)); mClient.onMessage(std::bind(&PubMqtt::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6)); @@ -72,10 +76,6 @@ class PubMqtt { #endif } - inline void connect() { - if(!mClient.connected()) - mClient.connect(); - } void tickerSecond() { if (mIntervalTimeout > 0) @@ -100,20 +100,20 @@ class PubMqtt { void tickerMinute() { char val[12]; snprintf(val, 12, "%ld", millis() / 1000); - publish("uptime", val); - publish("wifi_rssi", String(WiFi.RSSI()).c_str()); - publish("free_heap", String(ESP.getFreeHeap()).c_str()); + publish(subtopics[MQTT_UPTIME], val); + publish(subtopics[MQTT_RSSI], String(WiFi.RSSI()).c_str()); + publish(subtopics[MQTT_FREE_HEAP], String(ESP.getFreeHeap()).c_str()); } bool tickerSun(uint32_t sunrise, uint32_t sunset, uint32_t offs, bool disNightCom) { if (!mClient.connected()) return false; - 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); + publish(subtopics[MQTT_SUNRISE], String(sunrise).c_str(), true); + publish(subtopics[MQTT_SUNSET], String(sunset).c_str(), true); + publish(subtopics[MQTT_COMM_START], String(sunrise - offs).c_str(), true); + publish(subtopics[MQTT_COMM_STOP], String(sunset + offs).c_str(), true); + publish(subtopics[MQTT_DIS_NIGHT_COMM], ((disNightCom) ? dict[STR_TRUE] : dict[STR_FALSE]), true); return true; } @@ -122,8 +122,8 @@ class PubMqtt { if (!mClient.connected()) return false; - publish("comm_disabled", ((disabled) ? "true" : "false"), true); - publish("comm_dis_ts", String(*mUtcTimestamp).c_str(), true); + publish(subtopics[MQTT_COMM_DISABLED], ((disabled) ? dict[STR_TRUE] : dict[STR_FALSE]), true); + publish(subtopics[MQTT_COMM_DIS_TS], String(*mUtcTimestamp).c_str(), true); return true; } @@ -138,7 +138,6 @@ class PubMqtt { } void payloadEventListener(uint8_t cmd) { - connect(); if(mClient.connected()) { // prevent overflow if MQTT broker is not reachable but set if((0 == mCfgMqtt->interval) || (RealTimeRunData_Debug != cmd)) // no interval or no live data mSendList.push(cmd); @@ -290,7 +289,7 @@ class PubMqtt { if (NULL != iv) { char topic[7 + MQTT_TOPIC_LEN]; - snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/ack_pwr_limit", iv->config->name); + snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/%s", iv->config->name, subtopics[MQTT_ACK_PWR_LMT]); publish(topic, "true", true); } } @@ -299,18 +298,18 @@ class PubMqtt { void onConnect(bool sessionPreset) { DPRINTLN(DBG_INFO, F("MQTT connected")); - publish("version", mVersion, true); - publish("device", mDevName, true); - publish("ip_addr", WiFi.localIP().toString().c_str(), true); + publish(subtopics[MQTT_VERSION], mVersion, true); + publish(subtopics[MQTT_DEVICE], mDevName, true); + publish(subtopics[MQTT_IP_ADDR], WiFi.localIP().toString().c_str(), true); tickerMinute(); - publish(mLwtTopic, mLwtOnline, true, false); - - subscribe("ctrl/limit_persistent_relative"); - subscribe("ctrl/limit_persistent_absolute"); - subscribe("ctrl/limit_nonpersistent_relative"); - subscribe("ctrl/limit_nonpersistent_absolute"); - subscribe("setup/set_time"); - subscribe("setup/sync_ntp"); + publish(mLwtTopic, mqttStr[MQTT_STR_LWT_CONN], true, false); + + subscribe(subscr[MQTT_SUBS_LMT_PERI_REL]); + subscribe(subscr[MQTT_SUBS_LMT_PERI_ABS]); + subscribe(subscr[MQTT_SUBS_LMT_NONPERI_REL]); + subscribe(subscr[MQTT_SUBS_LMT_NONPERI_ABS]); + subscribe(subscr[MQTT_SUBS_SET_TIME]); + subscribe(subscr[MQTT_SUBS_SYNC_NTP]); //subscribe("status/#"); } @@ -341,16 +340,14 @@ class PubMqtt { } void onMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total) { - DPRINTLN(DBG_INFO, F("MQTT got topic: ") + String(topic)); + DPRINT(DBG_INFO, mqttStr[MQTT_STR_GOT_TOPIC]); + DBGPRINTLN(String(topic)); if(NULL == mSubscriptionCb) return; - char *tpc = new char[strlen(topic) + 1]; - uint8_t cnt = 0; DynamicJsonDocument json(128); JsonObject root = json.to(); - strncpy(tpc, topic, strlen(topic) + 1); if(len > 0) { char *pyld = new char[len + 1]; strncpy(pyld, (const char*)payload, len); @@ -359,34 +356,28 @@ class PubMqtt { delete[] pyld; } - char *p = strtok(tpc, "/"); - p = strtok(NULL, "/"); // remove mCfgMqtt->topic - while(NULL != p) { - if(0 == cnt) { - if(0 == strncmp(p, "ctrl", 4)) { - if(NULL != (p = strtok(NULL, "/"))) { - root[F("path")] = F("ctrl"); - root[F("cmd")] = p; - } - } else if(0 == strncmp(p, "setup", 5)) { - if(NULL != (p = strtok(NULL, "/"))) { - root[F("path")] = F("setup"); - root[F("cmd")] = p; - } - } else if(0 == strncmp(p, "status", 6)) { - if(NULL != (p = strtok(NULL, "/"))) { - root[F("path")] = F("status"); - root[F("cmd")] = p; - } + const char *p = topic; + uint8_t pos = 0; + uint8_t elm = 0; + char tmp[30]; + + while(1) { + if(('/' == p[pos]) || ('\0' == p[pos])) { + strncpy(tmp, p, pos); + tmp[pos] = '\0'; + switch(elm++) { + case 1: root[F("path")] = String(tmp); break; + case 2: root[F("cmd")] = String(tmp); break; + case 3: root[F("id")] = atoi(tmp); break; + default: break; } + if('\0' == p[pos]) + break; + p = p + pos + 1; + pos = 0; } - else if(1 == cnt) { - root[F("id")] = atoi(p); - } - p = strtok(NULL, "/"); - cnt++; + pos++; } - delete[] tpc; /*char out[128]; serializeJson(root, out, 128); @@ -447,11 +438,11 @@ class PubMqtt { mLastIvState[id] = status; changed = true; - snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/available", iv->config->name); + snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/%s", iv->config->name, mqttStr[MQTT_STR_AVAILABLE]); snprintf(val, 40, "%d", status); publish(topic, val, true); - snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/last_success", iv->config->name); + snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/%s", iv->config->name, mqttStr[MQTT_STR_LAST_SUCCESS]); snprintf(val, 40, "%d", iv->getLastTs(rec)); publish(topic, val, true); } @@ -459,7 +450,7 @@ class PubMqtt { if(changed) { snprintf(val, 32, "%d", ((allAvail) ? MQTT_STATUS_ONLINE : ((anyAvail) ? MQTT_STATUS_PARTIAL : MQTT_STATUS_OFFLINE))); - publish("status", val, true); + publish(subtopics[MQTT_STATUS], val, true); } return anyAvail; @@ -471,9 +462,9 @@ class PubMqtt { Inverter<> *iv = mSys->getInverterByPos(0, false); while(!mAlarmList.empty()) { alarm_t alarm = mAlarmList.front(); - publish("alarm", iv->getAlarmStr(alarm.code).c_str()); - publish("alarm_start", String(alarm.start).c_str()); - publish("alarm_end", String(alarm.end).c_str()); + publish(subtopics[MQTT_ALARM], iv->getAlarmStr(alarm.code).c_str()); + publish(subtopics[MQTT_ALARM_START], String(alarm.start).c_str()); + publish(subtopics[MQTT_ALARM_END], String(alarm.end).c_str()); mAlarmList.pop(); } } @@ -573,7 +564,7 @@ class PubMqtt { fieldId = FLD_PDC; break; } - snprintf(topic, 32 + MAX_NAME_LENGTH, "total/%s", fields[fieldId]); + snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/%s", mqttStr[MQTT_STR_TOTAL], fields[fieldId]); snprintf(val, 40, "%g", ah::round3(total[i])); publish(topic, val, true); } @@ -606,8 +597,6 @@ class PubMqtt { // last will topic and payload must be available trough lifetime of 'espMqttClient' char mLwtTopic[MQTT_TOPIC_LEN+5]; - const char* mLwtOnline = "connected"; - const char* mLwtOffline = "not connected"; const char *mDevName, *mVersion; }; diff --git a/src/publisher/pubMqttDefs.h b/src/publisher/pubMqttDefs.h new file mode 100644 index 00000000..2045c2bc --- /dev/null +++ b/src/publisher/pubMqttDefs.h @@ -0,0 +1,104 @@ +//----------------------------------------------------------------------------- +// 2023 Ahoy, https://ahoydtu.de +// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ +//----------------------------------------------------------------------------- + +#ifndef __PUB_MQTT_DEFS_H__ +#define __PUB_MQTT_DEFS_H__ + +#include + +enum { + STR_TRUE, + STR_FALSE +}; + +const char* const dict[] PROGMEM = { + "true", + "false" +}; + +enum { + MQTT_STR_LWT_CONN, + MQTT_STR_LWT_NOT_CONN, + MQTT_STR_AVAILABLE, + MQTT_STR_LAST_SUCCESS, + MQTT_STR_TOTAL, + MQTT_STR_GOT_TOPIC +}; + +const char* const mqttStr[] PROGMEM = { + "connected", + "not connected", + "available", + "last_success", + "total", + "MQTT got topic: " +}; + + +enum { + MQTT_UPTIME = 0, + MQTT_RSSI, + MQTT_FREE_HEAP, + MQTT_SUNRISE, + MQTT_SUNSET, + MQTT_COMM_START, + MQTT_COMM_STOP, + MQTT_DIS_NIGHT_COMM, + MQTT_COMM_DISABLED, + MQTT_COMM_DIS_TS, + MQTT_VERSION, + MQTT_DEVICE, + MQTT_IP_ADDR, + MQTT_STATUS, + MQTT_ALARM, + MQTT_ALARM_START, + MQTT_ALARM_END, + MQTT_LWT_ONLINE, + MQTT_LWT_OFFLINE, + MQTT_ACK_PWR_LMT +}; + +const char* const subtopics[] PROGMEM = { + "uptime", + "wifi_rssi", + "free_heap", + "sunrise", + "sunset", + "comm_start", + "comm_stop", + "dis_night_comm", + "comm_disabled", + "comm_dis_ts", + "version", + "device", + "ip_addr", + "status", + "alarm", + "alarm_start", + "alarm_end", + "connected", + "not connected", + "ack_pwr_limit" +}; + +enum { + MQTT_SUBS_LMT_PERI_REL, + MQTT_SUBS_LMT_PERI_ABS, + MQTT_SUBS_LMT_NONPERI_REL, + MQTT_SUBS_LMT_NONPERI_ABS, + MQTT_SUBS_SET_TIME, + MQTT_SUBS_SYNC_NTP +}; + +const char* const subscr[] PROGMEM = { + "ctrl/limit_persistent_relative", + "ctrl/limit_persistent_absolute", + "ctrl/limit_nonpersistent_relative", + "ctrl/limit_nonpersistent_absolute", + "setup/set_time", + "setup/sync_ntp" +}; + +#endif /*__PUB_MQTT_DEFS_H__*/ diff --git a/src/utils/dbg.h b/src/utils/dbg.h index 4f765ca6..bdfa2b15 100644 --- a/src/utils/dbg.h +++ b/src/utils/dbg.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// 2022 Ahoy, https://www.mikrocontroller.net/topic/525778 +// 2023 Ahoy, https://www.mikrocontroller.net/topic/525778 // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ //----------------------------------------------------------------------------- @@ -19,6 +19,8 @@ #define DBG_DEBUG 4 #define DBG_VERBOSE 5 +//#define LOG_MAX_MSG_LEN 100 + //----------------------------------------------------------------------------- // globally used level @@ -58,7 +60,7 @@ mCb(String(b, HEX)); } } - inline void DHEX(uint16_t b) { + /*inline void DHEX(uint16_t b) { if( b<0x10 ) DSERIAL.print(F("000")); else if( b<0x100 ) DSERIAL.print(F("00")); else if( b<0x1000 ) DSERIAL.print(F("0")); @@ -89,7 +91,7 @@ else if( b<0x10000000 ) mCb(F("0")); mCb(String(b, HEX)); } - } + }*/ #endif #endif @@ -154,4 +156,52 @@ }\ }) +/*class ahoyLog { + public: + ahoyLog() {} + + inline void logMsg(uint8_t lvl, bool newLine, const char *fmt, va_list args) { + snprintf(mLogBuf, LOG_MAX_MSG_LEN, fmt, args); + DSERIAL.print(mLogBuf); + if(NULL != mCb) + mCb(mLogBuf); + if(newLine) { + DSERIAL.print(F("\r\n")); + if(NULL != mCb) + mCb(F("\r\n")); + } + } + + inline void logError(const char *fmt, ...) { + #if DEBUG_LEVEL >= DBG_ERROR + va_list args; + va_start(args, fmt); + logMsg(DBG_ERROR, true, fmt, args); + va_end(args); + #endif + } + + inline void logWarn(const char *fmt, ...) { + #if DEBUG_LEVEL >= DBG_WARN + va_list args; + va_start(args, fmt); + logMsg(DBG_ERROR, true, fmt, args); + va_end(args); + #endif + } + + inline void logInfo(const char *fmt, ...) { + #if DEBUG_LEVEL >= DBG_INFO + va_list args; + va_start(args, fmt); + logMsg(DBG_ERROR, true, fmt, args); + va_end(args); + #endif + } + + private: + char mLogBuf[LOG_MAX_MSG_LEN]; + DBG_CB mCb = NULL; +};*/ + #endif /*__DBG_H__*/ diff --git a/src/utils/helper.cpp b/src/utils/helper.cpp index 7d54e2f4..97d7418b 100644 --- a/src/utils/helper.cpp +++ b/src/utils/helper.cpp @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// 2022 Ahoy, https://github.com/lumpapu/ahoy +// 2023 Ahoy, https://github.com/lumpapu/ahoy // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ //----------------------------------------------------------------------------- @@ -7,15 +7,15 @@ namespace ah { void ip2Arr(uint8_t ip[], const char *ipStr) { - char tmp[16]; + uint8_t p = 1; memset(ip, 0, 4); - memset(tmp, 0, 16); - snprintf(tmp, 16, ipStr); - char *p = strtok(tmp, "."); - uint8_t i = 0; - while(NULL != p) { - ip[i++] = atoi(p); - p = strtok(NULL, "."); + for(uint8_t i = 0; i < 16; i++) { + if(ipStr[i] == 0) + return; + if(0 == i) + ip[0] = atoi(ipStr); + else if(ipStr[i] == '.') + ip[p++] = atoi(&ipStr[i+1]); } } diff --git a/src/utils/scheduler.h b/src/utils/scheduler.h index 280f4a66..ca250a3e 100644 --- a/src/utils/scheduler.h +++ b/src/utils/scheduler.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// 2022 Ahoy, https://ahoydtu.de +// 2023 Ahoy, https://ahoydtu.de // Lukas Pusch, lukas@lpusch.de // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ //----------------------------------------------------------------------------- @@ -106,7 +106,11 @@ namespace ah { void printSchedulers() { for (uint8_t i = 0; i < MAX_NUM_TICKER; i++) { if (mTickerInUse[i]) { - DPRINTLN(DBG_INFO, String(mTicker[i].name) + ", tmt: " + String(mTicker[i].timeout) + ", rel: " + String(mTicker[i].reload)); + DPRINT(DBG_INFO, String(mTicker[i].name)); + DBGPRINT(", tmt: "); + DBGPRINT(String(mTicker[i].timeout)); + DBGPRINT(", rel: "); + DBGPRINTLN(String(mTicker[i].reload)); } } } diff --git a/src/web/RestApi.h b/src/web/RestApi.h index 9c3f931f..3dcbc481 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -1,3 +1,8 @@ +//----------------------------------------------------------------------------- +// 2023 Ahoy, https://ahoydtu.de +// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ +//----------------------------------------------------------------------------- + #ifndef __WEB_API_H__ #define __WEB_API_H__ @@ -24,7 +29,9 @@ class RestApi { public: RestApi() { mTimezoneOffset = 0; - mFreeHeap = 0; + mHeapFree = 0; + mHeapFreeBlk = 0; + mHeapFrag = 0; nr = 0; } @@ -49,7 +56,7 @@ class RestApi { serializeJson(obj, out, 128); DPRINTLN(DBG_INFO, "RestApi: " + String(out));*/ DynamicJsonDocument json(128); - JsonObject dummy = json.to(); + JsonObject dummy = json.as(); if(obj[F("path")] == "ctrl") setCtrl(obj, dummy); else if(obj[F("path")] == "setup") @@ -58,7 +65,11 @@ class RestApi { private: void onApi(AsyncWebServerRequest *request) { - mFreeHeap = ESP.getFreeHeap(); + mHeapFree = ESP.getFreeHeap(); + #ifndef ESP32 + mHeapFreeBlk = ESP.getMaxFreeBlockSize(); + mHeapFrag = ESP.getHeapFragmentation(); + #endif AsyncJsonResponse* response = new AsyncJsonResponse(false, 8192); JsonObject root = response->getRoot(); @@ -84,6 +95,7 @@ class RestApi { else getNotFound(root, F("http://") + request->host() + F("/api/")); + //DPRINTLN(DBG_INFO, "API mem usage: " + String(root.memoryUsage())); response->addHeader("Access-Control-Allow-Origin", "*"); response->addHeader("Access-Control-Allow-Headers", "content-type"); response->setLength(); @@ -194,7 +206,7 @@ class RestApi { obj[F("sdk")] = ESP.getSdkVersion(); obj[F("cpu_freq")] = ESP.getCpuFreqMHz(); - obj[F("heap_free")] = mFreeHeap; + obj[F("heap_free")] = mHeapFree; obj[F("sketch_total")] = ESP.getFreeSketchSpace(); obj[F("sketch_used")] = ESP.getSketchSize() / 1024; // in kb getGeneric(obj); @@ -219,8 +231,8 @@ class RestApi { //obj[F("chip_cores")] = F("n/a"); obj[F("core_version")] = ESP.getCoreVersion(); obj[F("flash_size")] = ESP.getFlashChipRealSize() / 1024; // in kb - obj[F("heap_frag")] = ESP.getHeapFragmentation(); - obj[F("max_free_blk")] = ESP.getMaxFreeBlockSize(); + obj[F("heap_frag")] = mHeapFrag; + obj[F("max_free_blk")] = mHeapFreeBlk; obj[F("reboot_reason")] = ESP.getResetReason(); #endif //obj[F("littlefs_total")] = LittleFS.totalBytes(); @@ -498,7 +510,7 @@ class RestApi { obj2[F("name")] = String(iv->config->name); obj2[F("channels")] = iv->channels; obj2[F("power_limit_read")] = ah::round3(iv->actPowerLimit); - obj2[F("last_alarm")] = String(iv->lastAlarmMsg); + //obj2[F("last_alarm")] = String(iv->lastAlarmMsg); obj2[F("ts_last_success")] = rec->ts; JsonArray ch = obj2.createNestedArray("ch"); @@ -625,7 +637,8 @@ class RestApi { settings_t *mConfig; uint32_t mTimezoneOffset; - uint32_t mFreeHeap; + uint32_t mHeapFree, mHeapFreeBlk; + uint8_t mHeapFrag; uint16_t nr; }; diff --git a/src/web/html/visualization.html b/src/web/html/visualization.html index cb1a6098..c278dd25 100644 --- a/src/web/html/visualization.html +++ b/src/web/html/visualization.html @@ -67,7 +67,7 @@ var limit = iv["power_limit_read"] + "%"; if(limit == "65535%") limit = "n/a"; - ch0.appendChild(span(iv["name"] + " Limit " + limit + " | last Alarm: " + iv["last_alarm"], ["head"])); + ch0.appendChild(span(iv["name"] + " Limit " + limit, ["head"])); for(var j = 0; j < root.ch0_fld_names.length; j++) { var val = Math.round(iv["ch"][0][j] * 100) / 100; diff --git a/src/wifi/ahoywifi.cpp b/src/wifi/ahoywifi.cpp index 1e9165e6..9109c831 100644 --- a/src/wifi/ahoywifi.cpp +++ b/src/wifi/ahoywifi.cpp @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// 2022 Ahoy, https://www.mikrocontroller.net/topic/525778 +// 2023 Ahoy, https://www.mikrocontroller.net/topic/525778 // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ //----------------------------------------------------------------------------- @@ -72,7 +72,7 @@ void ahoywifi::tickWifiLoop() { mScanActive = false; } DBGPRINTLN(F("AP client connected")); - welcome(mApIp.toString()); + welcome(mApIp.toString(), ""); WiFi.mode(WIFI_AP); mDns.start(53, "*", mApIp); mAppWifiCb(true); @@ -118,7 +118,7 @@ void ahoywifi::tickWifiLoop() { if(mStaConn != CONNECTED) mStaConn = CONNECTING; if(mBSSIDList.size() > 0) { // get first BSSID in list - DBGPRINT("try to connect to AP with BSSID:"); + DBGPRINT(F("try to connect to AP with BSSID:")); uint8_t bssid[6]; for (int j = 0; j < 6; j++) { bssid[j] = mBSSIDList.front(); @@ -142,7 +142,11 @@ void ahoywifi::setupAp(void) { DBGPRINTLN(F("\n---------\nAhoyDTU Info:")); DBGPRINT(F("Version: ")); - DBGPRINTLN(String(VERSION_MAJOR) + F(".") + String(VERSION_MINOR) + F(".") + String(VERSION_PATCH)); + DBGPRINT(String(VERSION_MAJOR)); + DBGPRINT(F(".")); + DBGPRINT(String(VERSION_MINOR)); + DBGPRINT(F(".")); + DBGPRINTLN(String(VERSION_PATCH)); DBGPRINT(F("Github Hash: ")); DBGPRINTLN(String(AUTO_GIT_HASH)); @@ -150,7 +154,8 @@ void ahoywifi::setupAp(void) { DBGPRINTLN(WIFI_AP_SSID); DBGPRINT(F("PWD: ")); DBGPRINTLN(WIFI_AP_PWD); - DBGPRINTLN("IP Address: http://" + mApIp.toString()); + DBGPRINT(F("IP Address: http://")); + DBGPRINTLN(mApIp.toString()); DBGPRINTLN(F("---------\n")); WiFi.mode(WIFI_AP_STA); @@ -327,9 +332,11 @@ void ahoywifi::connectionEvent(WiFiStatus_t status) { WiFi.scanDelete(); mScanActive = false; } - welcome(WiFi.localIP().toString() + F(" (Station)")); + welcome(WiFi.localIP().toString(), F(" (Station)")); + WiFi.softAPdisconnect(); WiFi.mode(WIFI_STA); DBGPRINTLN(F("[WiFi] AP disabled")); + delay(100); mAppWifiCb(true); break; @@ -393,11 +400,12 @@ void ahoywifi::connectionEvent(WiFiStatus_t status) { //----------------------------------------------------------------------------- -void ahoywifi::welcome(String msg) { +void ahoywifi::welcome(String ip, String mode) { DBGPRINTLN(F("\n\n--------------------------------")); DBGPRINTLN(F("Welcome to AHOY!")); DBGPRINT(F("\npoint your browser to http://")); - DBGPRINTLN(msg); + DBGPRINT(ip); + DBGPRINTLN(mode); DBGPRINTLN(F("to configure your device")); DBGPRINTLN(F("--------------------------------\n")); } diff --git a/src/wifi/ahoywifi.h b/src/wifi/ahoywifi.h index ac8c0dd0..3a3a35a8 100644 --- a/src/wifi/ahoywifi.h +++ b/src/wifi/ahoywifi.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// 2022 Ahoy, https://www.mikrocontroller.net/topic/525778 +// 2023 Ahoy, https://www.mikrocontroller.net/topic/525778 // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ //----------------------------------------------------------------------------- @@ -52,7 +52,7 @@ class ahoywifi { #else void onWiFiEvent(WiFiEvent_t event); #endif - void welcome(String msg); + void welcome(String ip, String mode); settings_t *mConfig;