diff --git a/.github/workflows/compile_esp8266.yml b/.github/workflows/compile_esp8266.yml index e989235c..168779b4 100644 --- a/.github/workflows/compile_esp8266.yml +++ b/.github/workflows/compile_esp8266.yml @@ -16,7 +16,7 @@ jobs: - uses: actions/checkout@v3 with: ref: main - - uses: benjlevesque/short-sha@v2.0 + - uses: benjlevesque/short-sha@v2.1 id: short-sha with: length: 7 @@ -46,16 +46,17 @@ jobs: pip install --upgrade platformio - name: Convert HTML files - working-directory: tools/esp8266/html + working-directory: src/web/html run: python convert.py - name: Run PlatformIO run: pio run -d tools/esp8266 --environment esp8266-release --environment esp8266-1m-release --environment esp32-wroom32-release - name: Rename Binary files id: rename-binary-files - working-directory: tools/esp8266/scripts - run: python getVersion.py - - name: create-release + working-directory: src + run: python ../scripts/getVersion.py >> $GITHUB_OUTPUT + + - name: Create Release id: create-release uses: actions/create-release@v1 with: @@ -74,7 +75,8 @@ jobs: VERSION: ${{ steps.rename-binary-files.outputs.name }} - name: create-artifact run: zip --junk-paths ${{ steps.rename-binary-files.outputs.name }}.zip tools/esp8266/.pio/build/out/* tools/esp8266/User_Manual.md - - name: upload-release + + - name: Upload Release id: upload-release uses: actions/upload-release-asset@v1 env: diff --git a/Getting_Started.md b/Getting_Started.md index bca334d3..15985081 100644 --- a/Getting_Started.md +++ b/Getting_Started.md @@ -204,7 +204,7 @@ When everything is wired up and the firmware is flashed, it is time to connect t If nothing connects to it and that time runs up, it will retry to connect to the configured network an so on.

If connected to your local Network, you just have to find out the used IP Address or try the default name [http://ahoy-dtu/](http://ahoy-dtu/). In most cases your Router will give you a hint.
- If you connect to the WiFi the Ahoy DTU opens in case it could not connect to any other Network, the IP-Address of your Ahoy DTU is [http://192.168.1.1/](http://192.168.1.1/).
+ If you connect to the WiFi the Ahoy DTU opens in case it could not connect to any other Network, the IP-Address of your Ahoy DTU is [http://192.168.4.1/](http://192.168.4.1/).
Just open the IP-Address in your browser.

The webinterface has the following abilities: @@ -216,7 +216,7 @@ When everything is wired up and the firmware is flashed, it is time to connect t ##### HTTP based Pages - To take control of your Ahoy DTU, you can directly call one of the following sub-pages (e.g. [http://ahoy-dtu/setup](http://ahoy-dtu/setup) or [http://192.168.1.1/setup](http://192.168.1.1/setup) ).
+ To take control of your Ahoy DTU, you can directly call one of the following sub-pages (e.g. [http://ahoy-dtu/setup](http://ahoy-dtu/setup) or [http://192.168.4.1/setup](http://192.168.4.1/setup) ).
| page | use | output | default availability | | ---- | ------ | ------ | ------ | diff --git a/.vscode/settings.json b/src/.vscode/settings.json similarity index 100% rename from .vscode/settings.json rename to src/.vscode/settings.json diff --git a/src/app.cpp b/src/app.cpp index 71d60f42..1f894519 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -38,6 +38,9 @@ void app::setup(uint32_t timeout) { mWifi->setup(timeout, mWifiSettingsValid); mSys->setup(mConfig.amplifierPower, mConfig.pinIrq, mConfig.pinCe, mConfig.pinCs); + mPayload.setup(mSys); + mPayload.enableSerialDebug(mConfig.serialDebug); + mPayload.addListener(std::bind(&app::payloadEventListener, this, std::placeholders::_1)); #ifndef AP_ONLY setupMqtt(); #endif @@ -108,42 +111,7 @@ void app::loop(void) { mStat.frmCnt++; if (0 != len) { - Inverter<> *iv = mSys->findInverter(&p->packet[1]); - if ((NULL != iv) && (p->packet[0] == (TX_REQ_INFO + ALL_FRAMES))) { // response from get information command - mPayload[iv->id].txId = p->packet[0]; - DPRINTLN(DBG_DEBUG, F("Response from info request received")); - uint8_t *pid = &p->packet[9]; - if (*pid == 0x00) { - DPRINT(DBG_DEBUG, F("fragment number zero received and ignored")); - } else { - DPRINTLN(DBG_DEBUG, "PID: 0x" + String(*pid, HEX)); - if ((*pid & 0x7F) < 5) { - memcpy(mPayload[iv->id].data[(*pid & 0x7F) - 1], &p->packet[10], len - 11); - mPayload[iv->id].len[(*pid & 0x7F) - 1] = len - 11; - } - - if ((*pid & ALL_FRAMES) == ALL_FRAMES) { - // Last packet - if ((*pid & 0x7f) > mPayload[iv->id].maxPackId) { - mPayload[iv->id].maxPackId = (*pid & 0x7f); - if (*pid > 0x81) - mLastPacketId = *pid; - } - } - } - } - if ((NULL != iv) && (p->packet[0] == (TX_REQ_DEVCONTROL + ALL_FRAMES))) { // response from dev control command - DPRINTLN(DBG_DEBUG, F("Response from devcontrol request received")); - - mPayload[iv->id].txId = p->packet[0]; - iv->devControlRequest = false; - - if ((p->packet[12] == ActivePowerContr) && (p->packet[13] == 0x00)) { - String msg = (p->packet[10] == 0x00 && p->packet[11] == 0x00) ? "" : "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])); - } - iv->devControlCmd = Init; - } + mPayload.add(p, len); } } mSys->BufCtrl.popBack(); @@ -151,7 +119,7 @@ void app::loop(void) { yield(); if (rxRdy) { - processPayload(true); + mPayload.process(true, mConfig.maxRetransPerPyld, &mStat); } } @@ -212,18 +180,16 @@ void app::loop(void) { int8_t maxLoop = MAX_NUM_INVERTERS; Inverter<> *iv = mSys->getInverterByPos(mSendLastIvId); do { - // if(NULL != iv) - // mPayload[iv->id].requested = false; mSendLastIvId = ((MAX_NUM_INVERTERS - 1) == mSendLastIvId) ? 0 : mSendLastIvId + 1; iv = mSys->getInverterByPos(mSendLastIvId); } while ((NULL == iv) && ((maxLoop--) > 0)); if (NULL != iv) { - if (!mPayload[iv->id].complete) - processPayload(false); + if (!mPayload.isComplete(iv)) + mPayload.process(false, mConfig.maxRetransPerPyld, &mStat); - if (!mPayload[iv->id].complete) { - if (0 == mPayload[iv->id].maxPackId) + if (!mPayload.isComplete(iv)) { + if (0 == mPayload.getMaxPacketId(iv)) mStat.rxFailNoAnser++; else mStat.rxFail++; @@ -233,12 +199,12 @@ void app::loop(void) { DPRINTLN(DBG_INFO, F("enqueued cmd failed/timeout")); if (mConfig.serialDebug) { DPRINT(DBG_INFO, F("(#") + String(iv->id) + ") "); - DPRINTLN(DBG_INFO, F("no Payload received! (retransmits: ") + String(mPayload[iv->id].retransmits) + ")"); + DPRINTLN(DBG_INFO, F("no Payload received! (retransmits: ") + String(mPayload.getRetransmits(iv)) + ")"); } } - resetPayload(iv); - mPayload[iv->id].requested = true; + mPayload.reset(iv, mUtcTimestamp); + mPayload.request(iv); yield(); if (mConfig.serialDebug) { @@ -250,14 +216,14 @@ void app::loop(void) { if (mConfig.serialDebug) DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") Devcontrol request ") + String(iv->devControlCmd) + F(" power limit ") + String(iv->powerLimit[0])); mSys->Radio.sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit); - mPayload[iv->id].txCmd = iv->devControlCmd; + mPayload.setTxCmd(iv, iv->devControlCmd); iv->clearCmdQueue(); iv->enqueCommand(SystemConfigPara); } else { uint8_t cmd = iv->getQueuedCmd(); DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") sendTimePacket")); - mSys->Radio.sendTimePacket(iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmMesIndex); - mPayload[iv->id].txCmd = cmd; + mSys->Radio.sendTimePacket(iv->radioId.u64, cmd, mPayload.getTs(iv), iv->alarmMesIndex); + mPayload.setTxCmd(iv, cmd); mRxTicker = 0; } } @@ -276,134 +242,6 @@ void app::handleIntr(void) { mSys->Radio.handleIntr(); } -//----------------------------------------------------------------------------- -bool app::buildPayload(uint8_t id) { - DPRINTLN(DBG_VERBOSE, F("app::buildPayload")); - uint16_t crc = 0xffff, crcRcv = 0x0000; - if (mPayload[id].maxPackId > MAX_PAYLOAD_ENTRIES) - mPayload[id].maxPackId = MAX_PAYLOAD_ENTRIES; - - for (uint8_t i = 0; i < mPayload[id].maxPackId; i++) { - if (mPayload[id].len[i] > 0) { - if (i == (mPayload[id].maxPackId - 1)) { - crc = ah::crc16(mPayload[id].data[i], mPayload[id].len[i] - 2, crc); - crcRcv = (mPayload[id].data[i][mPayload[id].len[i] - 2] << 8) | (mPayload[id].data[i][mPayload[id].len[i] - 1]); - } else - crc = ah::crc16(mPayload[id].data[i], mPayload[id].len[i], crc); - } - yield(); - } - - return (crc == crcRcv) ? true : false; -} - -//----------------------------------------------------------------------------- -void app::processPayload(bool retransmit) { - for (uint8_t id = 0; id < mSys->getNumInverters(); id++) { - Inverter<> *iv = mSys->getInverterByPos(id); - if (NULL == iv) - continue; // skip to next inverter - - if ((mPayload[iv->id].txId != (TX_REQ_INFO + ALL_FRAMES)) && (0 != mPayload[iv->id].txId)) { - // no processing needed if txId is not 0x95 - // DPRINTLN(DBG_INFO, F("processPayload - set complete, txId: ") + String(mPayload[iv->id].txId, HEX)); - mPayload[iv->id].complete = true; - } - - if (!mPayload[iv->id].complete) { - if (!buildPayload(iv->id)) { // payload not complete - if ((mPayload[iv->id].requested) && (retransmit)) { - if (iv->devControlCmd == Restart || iv->devControlCmd == CleanState_LockAndAlarm) { - // This is required to prevent retransmissions without answer. - DPRINTLN(DBG_INFO, F("Prevent retransmit on Restart / CleanState_LockAndAlarm...")); - mPayload[iv->id].retransmits = mConfig.maxRetransPerPyld; - } else { - if (mPayload[iv->id].retransmits < mConfig.maxRetransPerPyld) { - mPayload[iv->id].retransmits++; - if (mPayload[iv->id].maxPackId != 0) { - for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId - 1); i++) { - if (mPayload[iv->id].len[i] == 0) { - if (mConfig.serialDebug) - DPRINTLN(DBG_WARN, F("while retrieving data: Frame ") + String(i + 1) + F(" missing: Request Retransmit")); - mSys->Radio.sendCmdPacket(iv->radioId.u64, TX_REQ_INFO, (SINGLE_FRAME + i), true); - break; // only retransmit one frame per loop - } - yield(); - } - } else { - if (mConfig.serialDebug) - DPRINTLN(DBG_WARN, F("while retrieving data: last frame missing: Request Retransmit")); - if (0x00 != mLastPacketId) - mSys->Radio.sendCmdPacket(iv->radioId.u64, TX_REQ_INFO, mLastPacketId, true); - else { - mPayload[iv->id].txCmd = iv->getQueuedCmd(); - DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") sendTimePacket")); - mSys->Radio.sendTimePacket(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex); - } - } - mSys->Radio.switchRxCh(100); - } - } - } - } else { // payload complete - DPRINTLN(DBG_INFO, F("procPyld: cmd: ") + String(mPayload[iv->id].txCmd)); - DPRINTLN(DBG_INFO, F("procPyld: txid: 0x") + 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; - - uint8_t payload[128]; - uint8_t payloadLen = 0; - - memset(payload, 0, 128); - - for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId); i++) { - memcpy(&payload[payloadLen], mPayload[iv->id].data[i], (mPayload[iv->id].len[i])); - payloadLen += (mPayload[iv->id].len[i]); - yield(); - } - payloadLen -= 2; - - if (mConfig.serialDebug) { - DPRINT(DBG_INFO, F("Payload (") + String(payloadLen) + "): "); - mSys->Radio.dumpBuf(NULL, payload, payloadLen); - } - - if (NULL == rec) { - DPRINTLN(DBG_ERROR, F("record is NULL!")); - } else if ((rec->pyldLen == payloadLen) || (0 == rec->pyldLen)) { - if (mPayload[iv->id].txId == (TX_REQ_INFO + 0x80)) - mStat.rxSuccess++; - - rec->ts = mPayload[iv->id].ts; - for (uint8_t i = 0; i < rec->length; i++) { - iv->addValue(i, payload, rec); - yield(); - } - iv->doCalculations(); - - mMqttSendList.push(mPayload[iv->id].txCmd); - } else { - DPRINTLN(DBG_ERROR, F("plausibility check failed, expected ") + String(rec->pyldLen) + F(" bytes")); - mStat.rxFail++; - } - - iv->setQueuedCmdFinished(); - } - } - - yield(); - - } - - // ist MQTT aktiviert und es wurden Daten vom einem oder mehreren WR aufbereitet - // dann die den mMqttTicker auf mMqttIntervall -2 setzen, also - // MQTT aussenden in 2 sek aktivieren - if ((mMqttInterval != 0xffff) && (!mMqttSendList.empty())) { - mMqttTicker = mMqttInterval - 2; - } -} - //----------------------------------------------------------------------------- bool app::getWifiApActive(void) { return mWifi->getApActive(); @@ -451,9 +289,7 @@ void app::resetSystem(void) { mShowRebootRequest = false; - memset(mPayload, 0, (MAX_NUM_INVERTERS * sizeof(invPayload_t))); memset(&mStat, 0, sizeof(statistics_t)); - mLastPacketId = 0x00; } //----------------------------------------------------------------------------- @@ -545,7 +381,7 @@ void app::loadEEpconfig(void) { for (uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { iv = mSys->getInverterByPos(i, false); if (NULL != iv) - resetPayload(iv); + mPayload.reset(iv, mUtcTimestamp); } } } @@ -627,15 +463,3 @@ void app::updateLed(void) { } } } - -//----------------------------------------------------------------------------- -void app::resetPayload(Inverter<> *iv) { - DPRINTLN(DBG_INFO, "resetPayload: id: " + String(iv->id)); - memset(mPayload[iv->id].len, 0, MAX_PAYLOAD_ENTRIES); - mPayload[iv->id].txCmd = 0; - mPayload[iv->id].retransmits = 0; - mPayload[iv->id].maxPackId = 0; - mPayload[iv->id].complete = false; - mPayload[iv->id].requested = false; - mPayload[iv->id].ts = mUtcTimestamp; -} diff --git a/src/app.h b/src/app.h index 1b1ae31e..167ef306 100644 --- a/src/app.h +++ b/src/app.h @@ -21,6 +21,7 @@ #include "hm/CircularBuffer.h" #include "hm/hmSystem.h" +#include "hm/payload.h" #include "wifi/ahoywifi.h" #include "web/mqtt.h" #include "web/web.h" @@ -33,19 +34,7 @@ typedef HmSystem HmSystemType; typedef mqtt MqttType; - -typedef struct { - uint8_t txCmd; - uint8_t txId; - uint8_t invId; - uint32_t ts; - uint8_t data[MAX_PAYLOAD_ENTRIES][MAX_RF_PAYLOAD_SIZE]; - uint8_t len[MAX_PAYLOAD_ENTRIES]; - bool complete; - uint8_t maxPackId; - uint8_t retransmits; - bool requested; -} invPayload_t; +typedef payload PayloadType; class ahoywifi; class web; @@ -65,6 +54,10 @@ class app { void scanAvailNetworks(void); void getAvailNetworks(JsonObject obj); + void payloadEventListener(uint8_t cmd) { + mMqttSendList.push(cmd); + } + uint8_t getIrqPin(void) { return mConfig.pinIrq; } @@ -183,7 +176,6 @@ class app { void setupLed(void); void updateLed(void); - bool buildPayload(uint8_t id); void processPayload(bool retransmit); inline uint16_t buildEEpCrc(uint32_t start, uint32_t length) { @@ -268,13 +260,12 @@ class app { sysConfig_t mSysConfig; config_t mConfig; char mVersion[12]; + PayloadType mPayload; uint16_t mSendTicker; uint8_t mSendLastIvId; - invPayload_t mPayload[MAX_NUM_INVERTERS]; statistics_t mStat; - uint8_t mLastPacketId; // timer uint32_t mTicker; diff --git a/src/hm/payload.h b/src/hm/payload.h new file mode 100644 index 00000000..3905ce04 --- /dev/null +++ b/src/hm/payload.h @@ -0,0 +1,250 @@ +//----------------------------------------------------------------------------- +// 2022 Ahoy, https://ahoydtu.de +// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ +//----------------------------------------------------------------------------- + +#ifndef __PAYLOAD_H__ +#define __PAYLOAD_H__ + +#include "../utils/dbg.h" +#include "../utils/crc.h" +#include "../utils/handler.h" +#include + +typedef struct { + uint8_t txCmd; + uint8_t txId; + uint8_t invId; + uint32_t ts; + uint8_t data[MAX_PAYLOAD_ENTRIES][MAX_RF_PAYLOAD_SIZE]; + uint8_t len[MAX_PAYLOAD_ENTRIES]; + bool complete; + uint8_t maxPackId; + uint8_t retransmits; + bool requested; +} invPayload_t; + + +typedef std::function payloadListenerType; + + +template +class payload : public Handler { + public: + payload() : Handler() {} + + void setup(HMSYSTEM *sys) { + mSys = sys; + memset(mPayload, 0, (MAX_NUM_INVERTERS * sizeof(invPayload_t))); + mLastPacketId = 0x00; + mSerialDebug = false; + } + + void enableSerialDebug(bool enable) { + mSerialDebug = enable; + } + + bool isComplete(Inverter<> *iv) { + return mPayload[iv->id].complete; + } + + uint8_t getMaxPacketId(Inverter<> *iv) { + return mPayload[iv->id].maxPackId; + } + + uint8_t getRetransmits(Inverter<> *iv) { + return mPayload[iv->id].retransmits; + } + + uint32_t getTs(Inverter<> *iv) { + return mPayload[iv->id].ts; + } + + void request(Inverter<> *iv) { + mPayload[iv->id].requested = true; + } + + void setTxCmd(Inverter<> *iv, uint8_t cmd) { + mPayload[iv->id].txCmd = cmd; + } + + void notify(uint8_t val) { + for(typename std::list::iterator it = mList.begin(); it != mList.end(); ++it) { + (*it)(val); + } + } + + void add(packet_t *p, uint8_t len) { + Inverter<> *iv = mSys->findInverter(&p->packet[1]); + if ((NULL != iv) && (p->packet[0] == (TX_REQ_INFO + ALL_FRAMES))) { // response from get information command + mPayload[iv->id].txId = p->packet[0]; + DPRINTLN(DBG_DEBUG, F("Response from info request received")); + uint8_t *pid = &p->packet[9]; + if (*pid == 0x00) { + DPRINT(DBG_DEBUG, F("fragment number zero received and ignored")); + } else { + DPRINTLN(DBG_DEBUG, "PID: 0x" + String(*pid, HEX)); + if ((*pid & 0x7F) < 5) { + memcpy(mPayload[iv->id].data[(*pid & 0x7F) - 1], &p->packet[10], len - 11); + mPayload[iv->id].len[(*pid & 0x7F) - 1] = len - 11; + } + + if ((*pid & ALL_FRAMES) == ALL_FRAMES) { + // Last packet + if ((*pid & 0x7f) > mPayload[iv->id].maxPackId) { + mPayload[iv->id].maxPackId = (*pid & 0x7f); + if (*pid > 0x81) + mLastPacketId = *pid; + } + } + } + } + if ((NULL != iv) && (p->packet[0] == (TX_REQ_DEVCONTROL + ALL_FRAMES))) { // response from dev control command + DPRINTLN(DBG_DEBUG, F("Response from devcontrol request received")); + + mPayload[iv->id].txId = p->packet[0]; + iv->devControlRequest = false; + + if ((p->packet[12] == ActivePowerContr) && (p->packet[13] == 0x00)) { + String msg = (p->packet[10] == 0x00 && p->packet[11] == 0x00) ? "" : "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])); + } + iv->devControlCmd = Init; + } + } + + bool build(uint8_t id) { + DPRINTLN(DBG_VERBOSE, F("build")); + uint16_t crc = 0xffff, crcRcv = 0x0000; + if (mPayload[id].maxPackId > MAX_PAYLOAD_ENTRIES) + mPayload[id].maxPackId = MAX_PAYLOAD_ENTRIES; + + for (uint8_t i = 0; i < mPayload[id].maxPackId; i++) { + if (mPayload[id].len[i] > 0) { + if (i == (mPayload[id].maxPackId - 1)) { + crc = ah::crc16(mPayload[id].data[i], mPayload[id].len[i] - 2, crc); + crcRcv = (mPayload[id].data[i][mPayload[id].len[i] - 2] << 8) | (mPayload[id].data[i][mPayload[id].len[i] - 1]); + } else + crc = ah::crc16(mPayload[id].data[i], mPayload[id].len[i], crc); + } + yield(); + } + + return (crc == crcRcv) ? true : false; + } + + void process(bool retransmit, uint8_t maxRetransmits, statistics_t *stat) { + for (uint8_t id = 0; id < mSys->getNumInverters(); id++) { + Inverter<> *iv = mSys->getInverterByPos(id); + if (NULL == iv) + continue; // skip to next inverter + + if ((mPayload[iv->id].txId != (TX_REQ_INFO + ALL_FRAMES)) && (0 != mPayload[iv->id].txId)) { + // no processing needed if txId is not 0x95 + // DPRINTLN(DBG_INFO, F("processPayload - set complete, txId: ") + String(mPayload[iv->id].txId, HEX)); + mPayload[iv->id].complete = true; + } + + if (!mPayload[iv->id].complete) { + if (!build(iv->id)) { // payload not complete + if ((mPayload[iv->id].requested) && (retransmit)) { + if (iv->devControlCmd == Restart || iv->devControlCmd == CleanState_LockAndAlarm) { + // This is required to prevent retransmissions without answer. + DPRINTLN(DBG_INFO, F("Prevent retransmit on Restart / CleanState_LockAndAlarm...")); + mPayload[iv->id].retransmits = maxRetransmits; + } else { + if (mPayload[iv->id].retransmits < maxRetransmits) { + mPayload[iv->id].retransmits++; + if (mPayload[iv->id].maxPackId != 0) { + for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId - 1); i++) { + if (mPayload[iv->id].len[i] == 0) { + DPRINTLN(DBG_WARN, F("while retrieving data: Frame ") + String(i + 1) + F(" missing: Request Retransmit")); + mSys->Radio.sendCmdPacket(iv->radioId.u64, TX_REQ_INFO, (SINGLE_FRAME + i), true); + break; // only retransmit one frame per loop + } + yield(); + } + } else { + DPRINTLN(DBG_WARN, F("while retrieving data: last frame missing: Request Retransmit")); + if (0x00 != mLastPacketId) + mSys->Radio.sendCmdPacket(iv->radioId.u64, TX_REQ_INFO, mLastPacketId, true); + else { + mPayload[iv->id].txCmd = iv->getQueuedCmd(); + DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") sendTimePacket")); + mSys->Radio.sendTimePacket(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex); + } + } + mSys->Radio.switchRxCh(100); + } + } + } + } else { // payload complete + DPRINTLN(DBG_INFO, F("procPyld: cmd: ") + String(mPayload[iv->id].txCmd)); + DPRINTLN(DBG_INFO, F("procPyld: txid: 0x") + 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; + + uint8_t payload[128]; + uint8_t payloadLen = 0; + + memset(payload, 0, 128); + + for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId); i++) { + memcpy(&payload[payloadLen], mPayload[iv->id].data[i], (mPayload[iv->id].len[i])); + payloadLen += (mPayload[iv->id].len[i]); + yield(); + } + payloadLen -= 2; + + if (mSerialDebug) { + DPRINT(DBG_INFO, F("Payload (") + String(payloadLen) + "): "); + mSys->Radio.dumpBuf(NULL, payload, payloadLen); + } + + if (NULL == rec) { + DPRINTLN(DBG_ERROR, F("record is NULL!")); + } else if ((rec->pyldLen == payloadLen) || (0 == rec->pyldLen)) { + if (mPayload[iv->id].txId == (TX_REQ_INFO + 0x80)) + stat->rxSuccess++; + + rec->ts = mPayload[iv->id].ts; + for (uint8_t i = 0; i < rec->length; i++) { + iv->addValue(i, payload, rec); + yield(); + } + iv->doCalculations(); + notify(mPayload[iv->id].txCmd); + } else { + DPRINTLN(DBG_ERROR, F("plausibility check failed, expected ") + String(rec->pyldLen) + F(" bytes")); + stat->rxFail++; + } + + iv->setQueuedCmdFinished(); + } + } + + yield(); + + } + } + + void reset(Inverter<> *iv, uint32_t utcTs) { + DPRINTLN(DBG_INFO, "resetPayload: id: " + String(iv->id)); + memset(mPayload[iv->id].len, 0, MAX_PAYLOAD_ENTRIES); + mPayload[iv->id].txCmd = 0; + mPayload[iv->id].retransmits = 0; + mPayload[iv->id].maxPackId = 0; + mPayload[iv->id].complete = false; + mPayload[iv->id].requested = false; + mPayload[iv->id].ts = utcTs; + } + + private: + HMSYSTEM *mSys; + invPayload_t mPayload[MAX_NUM_INVERTERS]; + uint8_t mLastPacketId; + bool mSerialDebug; +}; + +#endif /*__PAYLOAD_H_*/ diff --git a/src/platformio.ini b/src/platformio.ini index 71e5f329..73c42da7 100644 --- a/src/platformio.ini +++ b/src/platformio.ini @@ -64,7 +64,7 @@ monitor_filters = time ; Add timestamp with milliseconds for each new line log2file ; Log data to a file “platformio-device-monitor-*.log” located in the current working directory -[env:esp8266-1m-release] +[env:esp8285-release] platform = espressif8266 board = esp8285 board_build.ldscript = eagle.flash.1m64.ld @@ -75,7 +75,7 @@ monitor_filters = time ; Add timestamp with milliseconds for each new line ;log2file ; Log data to a file “platformio-device-monitor-*.log” located in the current working directory -[env:esp8266-1m-debug] +[env:esp8285-debug] platform = espressif8266 board = esp8285 board_build.ldscript = eagle.flash.1m64.ld diff --git a/src/utils/handler.h b/src/utils/handler.h new file mode 100644 index 00000000..10dcfef5 --- /dev/null +++ b/src/utils/handler.h @@ -0,0 +1,33 @@ +//----------------------------------------------------------------------------- +// 2022 Ahoy, https://ahoydtu.de +// Lukas Pusch, lukas@lpusch.de +// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ +//----------------------------------------------------------------------------- + +#ifndef __HANDLER_H__ +#define __HANDLER_H__ + +#include +#include +#include + +template +class Handler { + public: + Handler() {} + + void addListener(TYPE f) { + mList.push_back(f); + } + + virtual void notify(void) { + for(typename std::list::iterator it = mList.begin(); it != mList.end(); ++it) { + (*it); + } + } + + protected: + std::list mList; +}; + +#endif /*__HANDLER_H__*/ diff --git a/src/web/html/index.html b/src/web/html/index.html index 2e37ace9..51ee0e18 100644 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -54,7 +54,7 @@

Support this project:

@@ -153,7 +153,7 @@ for(var i of obj) { html += "Inverter #" + i["id"] + ": " + i["name"] + " (v" + i["version"] + ") is "; if(false == i["is_avail"]) - html += "not yet available"; + html += "not yet available\n"; else { html += "available and is "; if(false == i["is_producing"])