Browse Source

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
pull/698/head
lumapu 2 years ago
parent
commit
a0879cfcbe
  1. 6
      src/CHANGES.md
  2. 45
      src/app.cpp
  3. 5
      src/app.h
  4. 6
      src/config/settings.h
  5. 4
      src/defines.h
  6. 17
      src/hm/hmInverter.h
  7. 79
      src/hm/hmPayload.h
  8. 11
      src/hm/hmRadio.h
  9. 127
      src/publisher/pubMqtt.h
  10. 104
      src/publisher/pubMqttDefs.h
  11. 56
      src/utils/dbg.h
  12. 18
      src/utils/helper.cpp
  13. 8
      src/utils/scheduler.h
  14. 29
      src/web/RestApi.h
  15. 2
      src/web/html/visualization.html
  16. 24
      src/wifi/ahoywifi.cpp
  17. 4
      src/wifi/ahoywifi.h

6
src/CHANGES.md

@ -2,6 +2,12 @@
(starting from release version `0.5.66`) (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 ## 0.5.88
* MQTT Yield Day zero, next try to fix #671, thx @beegee3 * MQTT Yield Day zero, next try to fix #671, thx @beegee3
* added Solenso inverter to supported devices * added Solenso inverter to supported devices

45
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/ // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -21,9 +21,19 @@ void app::setup() {
resetSystem(); resetSystem();
/*DBGPRINTLN("--- start");
DBGPRINTLN(String(ESP.getFreeHeap()));
DBGPRINTLN(String(ESP.getHeapFragmentation()));
DBGPRINTLN(String(ESP.getMaxFreeBlockSize()));*/
mSettings.setup(); mSettings.setup();
mSettings.getPtr(mConfig); 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.enableDebug();
mSys.setup(mConfig->nrf.amplifierPower, mConfig->nrf.pinIrq, mConfig->nrf.pinCe, mConfig->nrf.pinCs); 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.setup(this, &mSys, &mStat, mConfig->nrf.maxRetransPerPyld, &mTimestamp);
mMiPayload.enableSerialDebug(mConfig->serial.debug); mMiPayload.enableSerialDebug(mConfig->serial.debug);
/*DBGPRINTLN("--- after payload");
DBGPRINTLN(String(ESP.getFreeHeap()));
DBGPRINTLN(String(ESP.getHeapFragmentation()));
DBGPRINTLN(String(ESP.getMaxFreeBlockSize()));*/
if(!mSys.Radio.isChipConnected()) if(!mSys.Radio.isChipConnected())
DPRINTLN(DBG_WARN, F("WARNING! your NRF24 module can't be reached, check the wiring")); 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); mPubSerial.setup(mConfig, &mSys, &mTimestamp);
regularTickers(); 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(); packet_t *p = &mSys.Radio.mBufCtrl.front();
if (mConfig->serial.debug) { 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); mSys.Radio.dumpBuf(p->packet, p->len);
} }
mStat.frmCnt++; mStat.frmCnt++;
@ -158,13 +183,13 @@ void app::tickNtpUpdate(void) {
bool isOK = mWifi.getNtpTime(); bool isOK = mWifi.getNtpTime();
if (isOK || mTimestamp != 0) { if (isOK || mTimestamp != 0) {
if (mMqttReconnect && mMqttEnabled) { if (mMqttReconnect && mMqttEnabled) {
mMqtt.connect(); mMqtt.tickerMinute();
everySec(std::bind(&PubMqttType::tickerSecond, &mMqtt), "mqttS"); everySec(std::bind(&PubMqttType::tickerSecond, &mMqtt), "mqttS");
everyMin(std::bind(&PubMqttType::tickerMinute, &mMqtt), "mqttM"); everyMin(std::bind(&PubMqttType::tickerMinute, &mMqtt), "mqttM");
} }
// only install schedulers once even if NTP wasn't successful in first loop // 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) if(mConfig->inst.rstValsNotAvail)
everyMin(std::bind(&app::tickMinute, this), "tMin"); everyMin(std::bind(&app::tickMinute, this), "tMin");
if(mConfig->inst.rstYieldMidNight) { if(mConfig->inst.rstYieldMidNight) {
@ -282,8 +307,6 @@ void app::tickMidnight(void) {
uint32_t nxtTrig = mTimestamp - ((mTimestamp - 1) % 86400) + 86400; // next midnight uint32_t nxtTrig = mTimestamp - ((mTimestamp - 1) % 86400) + 86400; // next midnight
onceAt(std::bind(&app::tickMidnight, this), nxtTrig, "mid2"); onceAt(std::bind(&app::tickMidnight, this), nxtTrig, "mid2");
DPRINTLN(DBG_INFO, "tickMidnight " + String(nxtTrig));
Inverter<> *iv; Inverter<> *iv;
// set values to zero, except yield total // set values to zero, except yield total
for (uint8_t id = 0; id < mSys.getNumInverters(); id++) { for (uint8_t id = 0; id < mSys.getNumInverters(); id++) {
@ -302,13 +325,15 @@ void app::tickMidnight(void) {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void app::tickSend(void) { void app::tickSend(void) {
if(!mSys.Radio.isChipConnected()) { if(!mSys.Radio.isChipConnected()) {
DPRINTLN(DBG_WARN, "NRF24 not connected!"); DPRINTLN(DBG_WARN, F("NRF24 not connected!"));
return; return;
} }
if (mIVCommunicationOn) { if (mIVCommunicationOn) {
if (!mSys.Radio.mBufCtrl.empty()) { if (!mSys.Radio.mBufCtrl.empty()) {
if (mConfig->serial.debug) if (mConfig->serial.debug) {
DPRINTLN(DBG_DEBUG, F("recbuf not empty! #") + String(mSys.Radio.mBufCtrl.size())); DPRINT(DBG_DEBUG, F("recbuf not empty! #"));
DBGPRINTLN(String(mSys.Radio.mBufCtrl.size()));
}
} }
int8_t maxLoop = MAX_NUM_INVERTERS; int8_t maxLoop = MAX_NUM_INVERTERS;

5
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/ // 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) { void setTimestamp(uint32_t newTime) {
DPRINTLN(DBG_DEBUG, F("setTimestamp: ") + String(newTime)); DPRINT(DBG_DEBUG, F("setTimestamp: "));
DBGPRINTLN(String(newTime));
if(0 == newTime) if(0 == newTime)
mWifi.getNtpTime(); mWifi.getNtpTime();
else else

6
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/ // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -212,7 +212,6 @@ class settings {
} }
bool readSettings(const char* path) { bool readSettings(const char* path) {
bool success = false;
loadDefaults(); loadDefaults();
File fp = LittleFS.open(path, "r"); File fp = LittleFS.open(path, "r");
if(!fp) if(!fp)
@ -233,7 +232,6 @@ class settings {
jsonLed(root[F("led")]); jsonLed(root[F("led")]);
jsonPlugin(root[F("plugin")]); jsonPlugin(root[F("plugin")]);
jsonInst(root[F("inst")]); jsonInst(root[F("inst")]);
success = true;
} }
else { else {
Serial.println(F("failed to parse json, using default config")); Serial.println(F("failed to parse json, using default config"));
@ -241,7 +239,7 @@ class settings {
fp.close(); fp.close();
} }
return success; return mCfg.valid;
} }
bool saveSettings(void) { bool saveSettings(void) {

4
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/ // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -13,7 +13,7 @@
//------------------------------------- //-------------------------------------
#define VERSION_MAJOR 0 #define VERSION_MAJOR 0
#define VERSION_MINOR 5 #define VERSION_MINOR 5
#define VERSION_PATCH 88 #define VERSION_PATCH 89
//------------------------------------- //-------------------------------------
typedef struct { typedef struct {

17
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/ // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -119,7 +119,7 @@ class Inverter {
record_t<REC_TYP> recordInfo; // structure for info values record_t<REC_TYP> recordInfo; // structure for info values
record_t<REC_TYP> recordConfig; // structure for system config values record_t<REC_TYP> recordConfig; // structure for system config values
record_t<REC_TYP> recordAlarm; // structure for alarm values record_t<REC_TYP> 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 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) bool isConnected; // shows if inverter was successfully identified (fw version and hardware info)
@ -131,7 +131,7 @@ class Inverter {
mDevControlRequest = false; mDevControlRequest = false;
devControlCmd = InitDataState; devControlCmd = InitDataState;
initialized = false; initialized = false;
lastAlarmMsg = "nothing"; //lastAlarmMsg = "nothing";
alarmMesIndex = 0; alarmMesIndex = 0;
isConnected = false; isConnected = false;
} }
@ -294,9 +294,9 @@ class Inverter {
} }
else if (rec->assign == AlarmDataAssignment) { else if (rec->assign == AlarmDataAssignment) {
DPRINTLN(DBG_DEBUG, "add alarm"); DPRINTLN(DBG_DEBUG, "add alarm");
if (getPosByChFld(0, FLD_LAST_ALARM_CODE, rec) == pos){ //if (getPosByChFld(0, FLD_LAST_ALARM_CODE, rec) == pos){
lastAlarmMsg = getAlarmStr(rec->record[pos]); // lastAlarmMsg = getAlarmStr(rec->record[pos]);
} //}
} }
else else
DPRINTLN(DBG_WARN, F("add with unknown assginment")); 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")); 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) { bool setValue(uint8_t pos, record_t<> *rec, REC_TYP val) {
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:setValue")); DPRINTLN(DBG_VERBOSE, F("hmInverter.h:setValue"));
if(NULL == rec) if(NULL == rec)

79
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/ // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -71,7 +71,7 @@ class HmPayload {
} }
void zeroYieldDay(Inverter<> *iv) { void zeroYieldDay(Inverter<> *iv) {
DPRINTLN(DBG_INFO, "zeroYieldDay"); DPRINTLN(DBG_DEBUG, F("zeroYieldDay"));
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
uint8_t pos; uint8_t pos;
for(uint8_t ch = 0; ch < iv->channels; ch++) { for(uint8_t ch = 0; ch < iv->channels; ch++) {
@ -81,7 +81,7 @@ class HmPayload {
} }
void zeroInverterValues(Inverter<> *iv) { void zeroInverterValues(Inverter<> *iv) {
DPRINTLN(DBG_INFO, "zeroInverterValues"); DPRINTLN(DBG_DEBUG, F("zeroInverterValues"));
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
for(uint8_t ch = 0; ch <= iv->channels; ch++) { for(uint8_t ch = 0; ch <= iv->channels; ch++) {
uint8_t pos = 0; uint8_t pos = 0;
@ -119,8 +119,11 @@ class HmPayload {
if (mSerialDebug) if (mSerialDebug)
DPRINTLN(DBG_INFO, F("enqueued cmd failed/timeout")); DPRINTLN(DBG_INFO, F("enqueued cmd failed/timeout"));
if (mSerialDebug) { if (mSerialDebug) {
DPRINT(DBG_INFO, F("(#") + String(iv->id) + ") "); DPRINT(DBG_INFO, F("(#"));
DPRINTLN(DBG_INFO, F("no Payload received! (retransmits: ") + String(mPayload[iv->id].retransmits) + ")"); 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; mPayload[iv->id].requested = true;
yield(); yield();
if (mSerialDebug) if (mSerialDebug) {
DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") Requesting Inv SN ") + String(iv->config->serial.u64, HEX)); DPRINT(DBG_INFO, F("(#"));
DBGPRINT(String(iv->id));
DBGPRINT(F(") Requesting Inv SN "));
DBGPRINTLN(String(iv->config->serial.u64, HEX));
}
if (iv->getDevControlRequest()) { if (iv->getDevControlRequest()) {
if (mSerialDebug) if (mSerialDebug) {
DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") Devcontrol request 0x") + String(iv->devControlCmd, HEX) + F(" power limit ") + String(iv->powerLimit[0])); 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); mSys->Radio.sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit, false);
mPayload[iv->id].txCmd = iv->devControlCmd; mPayload[iv->id].txCmd = iv->devControlCmd;
//iv->clearCmdQueue(); //iv->clearCmdQueue();
//iv->enqueCommand<InfoCommand>(SystemConfigPara); // read back power limit //iv->enqueCommand<InfoCommand>(SystemConfigPara); // read back power limit
} else { } else {
uint8_t cmd = iv->getQueuedCmd(); 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); mSys->Radio.prepareDevInformCmd(iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmMesIndex, false);
mPayload[iv->id].txCmd = cmd; mPayload[iv->id].txCmd = cmd;
} }
@ -179,12 +194,20 @@ class HmPayload {
iv->clearDevControlRequest(); iv->clearDevControlRequest();
if ((p->packet[12] == ActivePowerContr) && (p->packet[13] == 0x00)) { if ((p->packet[12] == ActivePowerContr) && (p->packet[13] == 0x00)) {
String msg = ""; bool ok = true;
if((p->packet[10] == 0x00) && (p->packet[11] == 0x00)) if((p->packet[10] == 0x00) && (p->packet[11] == 0x00))
mApp->setMqttPowerLimitAck(iv); mApp->setMqttPowerLimitAck(iv);
else else
msg = "NOT "; ok = false;
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])); 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->clearCmdQueue();
iv->enqueCommand<InfoCommand>(SystemConfigPara); // read back power limit iv->enqueCommand<InfoCommand>(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)); 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); 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; mPayload[iv->id].retransmits = mMaxRetrans;
} else { } else {
for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId - 1); i++) { for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId - 1); i++) {
if (mPayload[iv->id].len[i] == 0) { 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); mSys->Radio.sendCmdPacket(iv->radioId.u64, TX_REQ_INFO, (SINGLE_FRAME + i), true);
break; // only request retransmit one frame per loop break; // only request retransmit one frame per loop
} }
@ -249,12 +276,17 @@ class HmPayload {
mPayload[iv->id].retransmits++; mPayload[iv->id].retransmits++;
DPRINTLN(DBG_WARN, F("CRC Error: Request Complete Retransmit")); DPRINTLN(DBG_WARN, F("CRC Error: Request Complete Retransmit"));
mPayload[iv->id].txCmd = iv->getQueuedCmd(); 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); mSys->Radio.prepareDevInformCmd(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex, true);
} }
} else { // payload complete } else { // payload complete
DPRINTLN(DBG_INFO, F("procPyld: cmd: 0x") + String(mPayload[iv->id].txCmd, HEX)); DPRINT(DBG_INFO, F("procPyld: cmd: 0x"));
DPRINTLN(DBG_INFO, F("procPyld: txid: 0x") + String(mPayload[iv->id].txId, HEX)); 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)); DPRINTLN(DBG_DEBUG, F("procPyld: max: ") + String(mPayload[iv->id].maxPackId));
record_t<> *rec = iv->getRecordStruct(mPayload[iv->id].txCmd); // choose the parser record_t<> *rec = iv->getRecordStruct(mPayload[iv->id].txCmd); // choose the parser
mPayload[iv->id].complete = true; mPayload[iv->id].complete = true;
@ -272,7 +304,9 @@ class HmPayload {
payloadLen -= 2; payloadLen -= 2;
if (mSerialDebug) { if (mSerialDebug) {
DPRINT(DBG_INFO, F("Payload (") + String(payloadLen) + "): "); DPRINT(DBG_INFO, F("Payload ("));
DBGPRINT(String(payloadLen));
DBGPRINT(F("): "));
mSys->Radio.dumpBuf(payload, payloadLen); mSys->Radio.dumpBuf(payload, payloadLen);
} }
@ -304,7 +338,9 @@ class HmPayload {
} }
} }
} else { } 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++; mStat->rxFail++;
} }
@ -356,7 +392,8 @@ class HmPayload {
} }
void reset(uint8_t id) { 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); memset(mPayload[id].len, 0, MAX_PAYLOAD_ENTRIES);
mPayload[id].txCmd = 0; mPayload[id].txCmd = 0;
mPayload[id].gotFragment = false; mPayload[id].gotFragment = false;

11
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/ // 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) { 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); initPacket(invId, TX_REQ_DEVCONTROL, SINGLE_FRAME);
uint8_t cnt = 10; uint8_t cnt = 10;
mTxBuf[cnt++] = cmd; // cmd -> 0 on, 1 off, 2 restart, 11 active power, 12 reactive power, 13 power factor 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++; len++;
if(mSerialDebug) { 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); dumpBuf(mTxBuf, len);
} }

127
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/ // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -21,6 +21,8 @@
#include "../defines.h" #include "../defines.h"
#include "../hm/hmSystem.h" #include "../hm/hmSystem.h"
#include "pubMqttDefs.h"
#define QOS_0 0 #define QOS_0 0
typedef std::function<void(JsonObject)> subscriptionCb; typedef std::function<void(JsonObject)> subscriptionCb;
@ -57,9 +59,11 @@ class PubMqtt {
if((strlen(mCfgMqtt->user) > 0) && (strlen(mCfgMqtt->pwd) > 0)) if((strlen(mCfgMqtt->user) > 0) && (strlen(mCfgMqtt->pwd) > 0))
mClient.setCredentials(mCfgMqtt->user, mCfgMqtt->pwd); 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.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.onConnect(std::bind(&PubMqtt::onConnect, this, std::placeholders::_1));
mClient.onDisconnect(std::bind(&PubMqtt::onDisconnect, 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)); 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 #endif
} }
inline void connect() {
if(!mClient.connected())
mClient.connect();
}
void tickerSecond() { void tickerSecond() {
if (mIntervalTimeout > 0) if (mIntervalTimeout > 0)
@ -100,20 +100,20 @@ class PubMqtt {
void tickerMinute() { void tickerMinute() {
char val[12]; char val[12];
snprintf(val, 12, "%ld", millis() / 1000); snprintf(val, 12, "%ld", millis() / 1000);
publish("uptime", val); publish(subtopics[MQTT_UPTIME], val);
publish("wifi_rssi", String(WiFi.RSSI()).c_str()); publish(subtopics[MQTT_RSSI], String(WiFi.RSSI()).c_str());
publish("free_heap", String(ESP.getFreeHeap()).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) { bool tickerSun(uint32_t sunrise, uint32_t sunset, uint32_t offs, bool disNightCom) {
if (!mClient.connected()) if (!mClient.connected())
return false; return false;
publish("sunrise", String(sunrise).c_str(), true); publish(subtopics[MQTT_SUNRISE], String(sunrise).c_str(), true);
publish("sunset", String(sunset).c_str(), true); publish(subtopics[MQTT_SUNSET], String(sunset).c_str(), true);
publish("comm_start", String(sunrise - offs).c_str(), true); publish(subtopics[MQTT_COMM_START], String(sunrise - offs).c_str(), true);
publish("comm_stop", String(sunset + offs).c_str(), true); publish(subtopics[MQTT_COMM_STOP], String(sunset + offs).c_str(), true);
publish("dis_night_comm", ((disNightCom) ? "true" : "false"), true); publish(subtopics[MQTT_DIS_NIGHT_COMM], ((disNightCom) ? dict[STR_TRUE] : dict[STR_FALSE]), true);
return true; return true;
} }
@ -122,8 +122,8 @@ class PubMqtt {
if (!mClient.connected()) if (!mClient.connected())
return false; return false;
publish("comm_disabled", ((disabled) ? "true" : "false"), true); publish(subtopics[MQTT_COMM_DISABLED], ((disabled) ? dict[STR_TRUE] : dict[STR_FALSE]), true);
publish("comm_dis_ts", String(*mUtcTimestamp).c_str(), true); publish(subtopics[MQTT_COMM_DIS_TS], String(*mUtcTimestamp).c_str(), true);
return true; return true;
} }
@ -138,7 +138,6 @@ class PubMqtt {
} }
void payloadEventListener(uint8_t cmd) { void payloadEventListener(uint8_t cmd) {
connect();
if(mClient.connected()) { // prevent overflow if MQTT broker is not reachable but set 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 if((0 == mCfgMqtt->interval) || (RealTimeRunData_Debug != cmd)) // no interval or no live data
mSendList.push(cmd); mSendList.push(cmd);
@ -290,7 +289,7 @@ class PubMqtt {
if (NULL != iv) { if (NULL != iv) {
char topic[7 + MQTT_TOPIC_LEN]; 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); publish(topic, "true", true);
} }
} }
@ -299,18 +298,18 @@ class PubMqtt {
void onConnect(bool sessionPreset) { void onConnect(bool sessionPreset) {
DPRINTLN(DBG_INFO, F("MQTT connected")); DPRINTLN(DBG_INFO, F("MQTT connected"));
publish("version", mVersion, true); publish(subtopics[MQTT_VERSION], mVersion, true);
publish("device", mDevName, true); publish(subtopics[MQTT_DEVICE], mDevName, true);
publish("ip_addr", WiFi.localIP().toString().c_str(), true); publish(subtopics[MQTT_IP_ADDR], WiFi.localIP().toString().c_str(), true);
tickerMinute(); tickerMinute();
publish(mLwtTopic, mLwtOnline, true, false); publish(mLwtTopic, mqttStr[MQTT_STR_LWT_CONN], true, false);
subscribe("ctrl/limit_persistent_relative"); subscribe(subscr[MQTT_SUBS_LMT_PERI_REL]);
subscribe("ctrl/limit_persistent_absolute"); subscribe(subscr[MQTT_SUBS_LMT_PERI_ABS]);
subscribe("ctrl/limit_nonpersistent_relative"); subscribe(subscr[MQTT_SUBS_LMT_NONPERI_REL]);
subscribe("ctrl/limit_nonpersistent_absolute"); subscribe(subscr[MQTT_SUBS_LMT_NONPERI_ABS]);
subscribe("setup/set_time"); subscribe(subscr[MQTT_SUBS_SET_TIME]);
subscribe("setup/sync_ntp"); subscribe(subscr[MQTT_SUBS_SYNC_NTP]);
//subscribe("status/#"); //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) { 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) if(NULL == mSubscriptionCb)
return; return;
char *tpc = new char[strlen(topic) + 1];
uint8_t cnt = 0;
DynamicJsonDocument json(128); DynamicJsonDocument json(128);
JsonObject root = json.to<JsonObject>(); JsonObject root = json.to<JsonObject>();
strncpy(tpc, topic, strlen(topic) + 1);
if(len > 0) { if(len > 0) {
char *pyld = new char[len + 1]; char *pyld = new char[len + 1];
strncpy(pyld, (const char*)payload, len); strncpy(pyld, (const char*)payload, len);
@ -359,34 +356,28 @@ class PubMqtt {
delete[] pyld; delete[] pyld;
} }
char *p = strtok(tpc, "/"); const char *p = topic;
p = strtok(NULL, "/"); // remove mCfgMqtt->topic uint8_t pos = 0;
while(NULL != p) { uint8_t elm = 0;
if(0 == cnt) { char tmp[30];
if(0 == strncmp(p, "ctrl", 4)) {
if(NULL != (p = strtok(NULL, "/"))) { while(1) {
root[F("path")] = F("ctrl"); if(('/' == p[pos]) || ('\0' == p[pos])) {
root[F("cmd")] = p; strncpy(tmp, p, pos);
} tmp[pos] = '\0';
} else if(0 == strncmp(p, "setup", 5)) { switch(elm++) {
if(NULL != (p = strtok(NULL, "/"))) { case 1: root[F("path")] = String(tmp); break;
root[F("path")] = F("setup"); case 2: root[F("cmd")] = String(tmp); break;
root[F("cmd")] = p; case 3: root[F("id")] = atoi(tmp); break;
} default: break;
} else if(0 == strncmp(p, "status", 6)) { }
if(NULL != (p = strtok(NULL, "/"))) { if('\0' == p[pos])
root[F("path")] = F("status"); break;
root[F("cmd")] = p; p = p + pos + 1;
} pos = 0;
}
}
else if(1 == cnt) {
root[F("id")] = atoi(p);
} }
p = strtok(NULL, "/"); pos++;
cnt++;
} }
delete[] tpc;
/*char out[128]; /*char out[128];
serializeJson(root, out, 128); serializeJson(root, out, 128);
@ -447,11 +438,11 @@ class PubMqtt {
mLastIvState[id] = status; mLastIvState[id] = status;
changed = true; 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); snprintf(val, 40, "%d", status);
publish(topic, val, true); 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)); snprintf(val, 40, "%d", iv->getLastTs(rec));
publish(topic, val, true); publish(topic, val, true);
} }
@ -459,7 +450,7 @@ class PubMqtt {
if(changed) { if(changed) {
snprintf(val, 32, "%d", ((allAvail) ? MQTT_STATUS_ONLINE : ((anyAvail) ? MQTT_STATUS_PARTIAL : MQTT_STATUS_OFFLINE))); 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; return anyAvail;
@ -471,9 +462,9 @@ class PubMqtt {
Inverter<> *iv = mSys->getInverterByPos(0, false); Inverter<> *iv = mSys->getInverterByPos(0, false);
while(!mAlarmList.empty()) { while(!mAlarmList.empty()) {
alarm_t alarm = mAlarmList.front(); alarm_t alarm = mAlarmList.front();
publish("alarm", iv->getAlarmStr(alarm.code).c_str()); publish(subtopics[MQTT_ALARM], iv->getAlarmStr(alarm.code).c_str());
publish("alarm_start", String(alarm.start).c_str()); publish(subtopics[MQTT_ALARM_START], String(alarm.start).c_str());
publish("alarm_end", String(alarm.end).c_str()); publish(subtopics[MQTT_ALARM_END], String(alarm.end).c_str());
mAlarmList.pop(); mAlarmList.pop();
} }
} }
@ -573,7 +564,7 @@ class PubMqtt {
fieldId = FLD_PDC; fieldId = FLD_PDC;
break; 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])); snprintf(val, 40, "%g", ah::round3(total[i]));
publish(topic, val, true); publish(topic, val, true);
} }
@ -606,8 +597,6 @@ class PubMqtt {
// last will topic and payload must be available trough lifetime of 'espMqttClient' // last will topic and payload must be available trough lifetime of 'espMqttClient'
char mLwtTopic[MQTT_TOPIC_LEN+5]; char mLwtTopic[MQTT_TOPIC_LEN+5];
const char* mLwtOnline = "connected";
const char* mLwtOffline = "not connected";
const char *mDevName, *mVersion; const char *mDevName, *mVersion;
}; };

104
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 <Arduino.h>
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__*/

56
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/ // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -19,6 +19,8 @@
#define DBG_DEBUG 4 #define DBG_DEBUG 4
#define DBG_VERBOSE 5 #define DBG_VERBOSE 5
//#define LOG_MAX_MSG_LEN 100
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// globally used level // globally used level
@ -58,7 +60,7 @@
mCb(String(b, HEX)); mCb(String(b, HEX));
} }
} }
inline void DHEX(uint16_t b) { /*inline void DHEX(uint16_t b) {
if( b<0x10 ) DSERIAL.print(F("000")); if( b<0x10 ) DSERIAL.print(F("000"));
else if( b<0x100 ) DSERIAL.print(F("00")); else if( b<0x100 ) DSERIAL.print(F("00"));
else if( b<0x1000 ) DSERIAL.print(F("0")); else if( b<0x1000 ) DSERIAL.print(F("0"));
@ -89,7 +91,7 @@
else if( b<0x10000000 ) mCb(F("0")); else if( b<0x10000000 ) mCb(F("0"));
mCb(String(b, HEX)); mCb(String(b, HEX));
} }
} }*/
#endif #endif
#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__*/ #endif /*__DBG_H__*/

18
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/ // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -7,15 +7,15 @@
namespace ah { namespace ah {
void ip2Arr(uint8_t ip[], const char *ipStr) { void ip2Arr(uint8_t ip[], const char *ipStr) {
char tmp[16]; uint8_t p = 1;
memset(ip, 0, 4); memset(ip, 0, 4);
memset(tmp, 0, 16); for(uint8_t i = 0; i < 16; i++) {
snprintf(tmp, 16, ipStr); if(ipStr[i] == 0)
char *p = strtok(tmp, "."); return;
uint8_t i = 0; if(0 == i)
while(NULL != p) { ip[0] = atoi(ipStr);
ip[i++] = atoi(p); else if(ipStr[i] == '.')
p = strtok(NULL, "."); ip[p++] = atoi(&ipStr[i+1]);
} }
} }

8
src/utils/scheduler.h

@ -1,5 +1,5 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// 2022 Ahoy, https://ahoydtu.de // 2023 Ahoy, https://ahoydtu.de
// Lukas Pusch, lukas@lpusch.de // Lukas Pusch, lukas@lpusch.de
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -106,7 +106,11 @@ namespace ah {
void printSchedulers() { void printSchedulers() {
for (uint8_t i = 0; i < MAX_NUM_TICKER; i++) { for (uint8_t i = 0; i < MAX_NUM_TICKER; i++) {
if (mTickerInUse[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));
} }
} }
} }

29
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__ #ifndef __WEB_API_H__
#define __WEB_API_H__ #define __WEB_API_H__
@ -24,7 +29,9 @@ class RestApi {
public: public:
RestApi() { RestApi() {
mTimezoneOffset = 0; mTimezoneOffset = 0;
mFreeHeap = 0; mHeapFree = 0;
mHeapFreeBlk = 0;
mHeapFrag = 0;
nr = 0; nr = 0;
} }
@ -49,7 +56,7 @@ class RestApi {
serializeJson(obj, out, 128); serializeJson(obj, out, 128);
DPRINTLN(DBG_INFO, "RestApi: " + String(out));*/ DPRINTLN(DBG_INFO, "RestApi: " + String(out));*/
DynamicJsonDocument json(128); DynamicJsonDocument json(128);
JsonObject dummy = json.to<JsonObject>(); JsonObject dummy = json.as<JsonObject>();
if(obj[F("path")] == "ctrl") if(obj[F("path")] == "ctrl")
setCtrl(obj, dummy); setCtrl(obj, dummy);
else if(obj[F("path")] == "setup") else if(obj[F("path")] == "setup")
@ -58,7 +65,11 @@ class RestApi {
private: private:
void onApi(AsyncWebServerRequest *request) { 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); AsyncJsonResponse* response = new AsyncJsonResponse(false, 8192);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
@ -84,6 +95,7 @@ class RestApi {
else else
getNotFound(root, F("http://") + request->host() + F("/api/")); 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-Origin", "*");
response->addHeader("Access-Control-Allow-Headers", "content-type"); response->addHeader("Access-Control-Allow-Headers", "content-type");
response->setLength(); response->setLength();
@ -194,7 +206,7 @@ class RestApi {
obj[F("sdk")] = ESP.getSdkVersion(); obj[F("sdk")] = ESP.getSdkVersion();
obj[F("cpu_freq")] = ESP.getCpuFreqMHz(); 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_total")] = ESP.getFreeSketchSpace();
obj[F("sketch_used")] = ESP.getSketchSize() / 1024; // in kb obj[F("sketch_used")] = ESP.getSketchSize() / 1024; // in kb
getGeneric(obj); getGeneric(obj);
@ -219,8 +231,8 @@ class RestApi {
//obj[F("chip_cores")] = F("n/a"); //obj[F("chip_cores")] = F("n/a");
obj[F("core_version")] = ESP.getCoreVersion(); obj[F("core_version")] = ESP.getCoreVersion();
obj[F("flash_size")] = ESP.getFlashChipRealSize() / 1024; // in kb obj[F("flash_size")] = ESP.getFlashChipRealSize() / 1024; // in kb
obj[F("heap_frag")] = ESP.getHeapFragmentation(); obj[F("heap_frag")] = mHeapFrag;
obj[F("max_free_blk")] = ESP.getMaxFreeBlockSize(); obj[F("max_free_blk")] = mHeapFreeBlk;
obj[F("reboot_reason")] = ESP.getResetReason(); obj[F("reboot_reason")] = ESP.getResetReason();
#endif #endif
//obj[F("littlefs_total")] = LittleFS.totalBytes(); //obj[F("littlefs_total")] = LittleFS.totalBytes();
@ -498,7 +510,7 @@ class RestApi {
obj2[F("name")] = String(iv->config->name); obj2[F("name")] = String(iv->config->name);
obj2[F("channels")] = iv->channels; obj2[F("channels")] = iv->channels;
obj2[F("power_limit_read")] = ah::round3(iv->actPowerLimit); 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; obj2[F("ts_last_success")] = rec->ts;
JsonArray ch = obj2.createNestedArray("ch"); JsonArray ch = obj2.createNestedArray("ch");
@ -625,7 +637,8 @@ class RestApi {
settings_t *mConfig; settings_t *mConfig;
uint32_t mTimezoneOffset; uint32_t mTimezoneOffset;
uint32_t mFreeHeap; uint32_t mHeapFree, mHeapFreeBlk;
uint8_t mHeapFrag;
uint16_t nr; uint16_t nr;
}; };

2
src/web/html/visualization.html

@ -67,7 +67,7 @@
var limit = iv["power_limit_read"] + "%"; var limit = iv["power_limit_read"] + "%";
if(limit == "65535%") if(limit == "65535%")
limit = "n/a"; 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++) { for(var j = 0; j < root.ch0_fld_names.length; j++) {
var val = Math.round(iv["ch"][0][j] * 100) / 100; var val = Math.round(iv["ch"][0][j] * 100) / 100;

24
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/ // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -72,7 +72,7 @@ void ahoywifi::tickWifiLoop() {
mScanActive = false; mScanActive = false;
} }
DBGPRINTLN(F("AP client connected")); DBGPRINTLN(F("AP client connected"));
welcome(mApIp.toString()); welcome(mApIp.toString(), "");
WiFi.mode(WIFI_AP); WiFi.mode(WIFI_AP);
mDns.start(53, "*", mApIp); mDns.start(53, "*", mApIp);
mAppWifiCb(true); mAppWifiCb(true);
@ -118,7 +118,7 @@ void ahoywifi::tickWifiLoop() {
if(mStaConn != CONNECTED) if(mStaConn != CONNECTED)
mStaConn = CONNECTING; mStaConn = CONNECTING;
if(mBSSIDList.size() > 0) { // get first BSSID in list 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]; uint8_t bssid[6];
for (int j = 0; j < 6; j++) { for (int j = 0; j < 6; j++) {
bssid[j] = mBSSIDList.front(); bssid[j] = mBSSIDList.front();
@ -142,7 +142,11 @@ void ahoywifi::setupAp(void) {
DBGPRINTLN(F("\n---------\nAhoyDTU Info:")); DBGPRINTLN(F("\n---------\nAhoyDTU Info:"));
DBGPRINT(F("Version: ")); 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: ")); DBGPRINT(F("Github Hash: "));
DBGPRINTLN(String(AUTO_GIT_HASH)); DBGPRINTLN(String(AUTO_GIT_HASH));
@ -150,7 +154,8 @@ void ahoywifi::setupAp(void) {
DBGPRINTLN(WIFI_AP_SSID); DBGPRINTLN(WIFI_AP_SSID);
DBGPRINT(F("PWD: ")); DBGPRINT(F("PWD: "));
DBGPRINTLN(WIFI_AP_PWD); DBGPRINTLN(WIFI_AP_PWD);
DBGPRINTLN("IP Address: http://" + mApIp.toString()); DBGPRINT(F("IP Address: http://"));
DBGPRINTLN(mApIp.toString());
DBGPRINTLN(F("---------\n")); DBGPRINTLN(F("---------\n"));
WiFi.mode(WIFI_AP_STA); WiFi.mode(WIFI_AP_STA);
@ -327,9 +332,11 @@ void ahoywifi::connectionEvent(WiFiStatus_t status) {
WiFi.scanDelete(); WiFi.scanDelete();
mScanActive = false; mScanActive = false;
} }
welcome(WiFi.localIP().toString() + F(" (Station)")); welcome(WiFi.localIP().toString(), F(" (Station)"));
WiFi.softAPdisconnect();
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
DBGPRINTLN(F("[WiFi] AP disabled")); DBGPRINTLN(F("[WiFi] AP disabled"));
delay(100);
mAppWifiCb(true); mAppWifiCb(true);
break; 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("\n\n--------------------------------"));
DBGPRINTLN(F("Welcome to AHOY!")); DBGPRINTLN(F("Welcome to AHOY!"));
DBGPRINT(F("\npoint your browser to http://")); DBGPRINT(F("\npoint your browser to http://"));
DBGPRINTLN(msg); DBGPRINT(ip);
DBGPRINTLN(mode);
DBGPRINTLN(F("to configure your device")); DBGPRINTLN(F("to configure your device"));
DBGPRINTLN(F("--------------------------------\n")); DBGPRINTLN(F("--------------------------------\n"));
} }

4
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/ // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -52,7 +52,7 @@ class ahoywifi {
#else #else
void onWiFiEvent(WiFiEvent_t event); void onWiFiEvent(WiFiEvent_t event);
#endif #endif
void welcome(String msg); void welcome(String ip, String mode);
settings_t *mConfig; settings_t *mConfig;

Loading…
Cancel
Save