Browse Source

moved payload related stuff to other file

fix line break for inverter list on index
update release workflow action
fix #405 renamed binary for ESP8285 (platformio.ini)
pull/421/head
lumapu 2 years ago
parent
commit
9c68ad1467
  1. 14
      .github/workflows/compile_esp8266.yml
  2. 4
      Getting_Started.md
  3. 0
      src/.vscode/settings.json
  4. 208
      src/app.cpp
  5. 23
      src/app.h
  6. 250
      src/hm/payload.h
  7. 4
      src/platformio.ini
  8. 33
      src/utils/handler.h
  9. 4
      src/web/html/index.html

14
.github/workflows/compile_esp8266.yml

@ -16,7 +16,7 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
ref: main ref: main
- uses: benjlevesque/short-sha@v2.0 - uses: benjlevesque/short-sha@v2.1
id: short-sha id: short-sha
with: with:
length: 7 length: 7
@ -46,16 +46,17 @@ jobs:
pip install --upgrade platformio pip install --upgrade platformio
- name: Convert HTML files - name: Convert HTML files
working-directory: tools/esp8266/html working-directory: src/web/html
run: python convert.py run: python convert.py
- name: Run PlatformIO - name: Run PlatformIO
run: pio run -d tools/esp8266 --environment esp8266-release --environment esp8266-1m-release --environment esp32-wroom32-release run: pio run -d tools/esp8266 --environment esp8266-release --environment esp8266-1m-release --environment esp32-wroom32-release
- name: Rename Binary files - name: Rename Binary files
id: rename-binary-files id: rename-binary-files
working-directory: tools/esp8266/scripts working-directory: src
run: python getVersion.py run: python ../scripts/getVersion.py >> $GITHUB_OUTPUT
- name: create-release
- name: Create Release
id: create-release id: create-release
uses: actions/create-release@v1 uses: actions/create-release@v1
with: with:
@ -74,7 +75,8 @@ jobs:
VERSION: ${{ steps.rename-binary-files.outputs.name }} VERSION: ${{ steps.rename-binary-files.outputs.name }}
- name: create-artifact - name: create-artifact
run: zip --junk-paths ${{ steps.rename-binary-files.outputs.name }}.zip tools/esp8266/.pio/build/out/* tools/esp8266/User_Manual.md 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 id: upload-release
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1
env: env:

4
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.<br/> If nothing connects to it and that time runs up, it will retry to connect to the configured network an so on.<br/>
<br/> <br/>
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.<br/> 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.<br/>
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/).<br/> 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/).<br/>
Just open the IP-Address in your browser.<br/> Just open the IP-Address in your browser.<br/>
<br/> <br/>
The webinterface has the following abilities: 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 ##### 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) ).<br/> 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) ).<br/>
| page | use | output | default availability | | page | use | output | default availability |
| ---- | ------ | ------ | ------ | | ---- | ------ | ------ | ------ |

0
.vscode/settings.json → src/.vscode/settings.json

208
src/app.cpp

@ -38,6 +38,9 @@ void app::setup(uint32_t timeout) {
mWifi->setup(timeout, mWifiSettingsValid); mWifi->setup(timeout, mWifiSettingsValid);
mSys->setup(mConfig.amplifierPower, mConfig.pinIrq, mConfig.pinCe, mConfig.pinCs); 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 #ifndef AP_ONLY
setupMqtt(); setupMqtt();
#endif #endif
@ -108,42 +111,7 @@ void app::loop(void) {
mStat.frmCnt++; mStat.frmCnt++;
if (0 != len) { if (0 != len) {
Inverter<> *iv = mSys->findInverter(&p->packet[1]); mPayload.add(p, len);
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;
}
} }
} }
mSys->BufCtrl.popBack(); mSys->BufCtrl.popBack();
@ -151,7 +119,7 @@ void app::loop(void) {
yield(); yield();
if (rxRdy) { if (rxRdy) {
processPayload(true); mPayload.process(true, mConfig.maxRetransPerPyld, &mStat);
} }
} }
@ -212,18 +180,16 @@ void app::loop(void) {
int8_t maxLoop = MAX_NUM_INVERTERS; int8_t maxLoop = MAX_NUM_INVERTERS;
Inverter<> *iv = mSys->getInverterByPos(mSendLastIvId); Inverter<> *iv = mSys->getInverterByPos(mSendLastIvId);
do { do {
// if(NULL != iv)
// mPayload[iv->id].requested = false;
mSendLastIvId = ((MAX_NUM_INVERTERS - 1) == mSendLastIvId) ? 0 : mSendLastIvId + 1; mSendLastIvId = ((MAX_NUM_INVERTERS - 1) == mSendLastIvId) ? 0 : mSendLastIvId + 1;
iv = mSys->getInverterByPos(mSendLastIvId); iv = mSys->getInverterByPos(mSendLastIvId);
} while ((NULL == iv) && ((maxLoop--) > 0)); } while ((NULL == iv) && ((maxLoop--) > 0));
if (NULL != iv) { if (NULL != iv) {
if (!mPayload[iv->id].complete) if (!mPayload.isComplete(iv))
processPayload(false); mPayload.process(false, mConfig.maxRetransPerPyld, &mStat);
if (!mPayload[iv->id].complete) { if (!mPayload.isComplete(iv)) {
if (0 == mPayload[iv->id].maxPackId) if (0 == mPayload.getMaxPacketId(iv))
mStat.rxFailNoAnser++; mStat.rxFailNoAnser++;
else else
mStat.rxFail++; mStat.rxFail++;
@ -233,12 +199,12 @@ void app::loop(void) {
DPRINTLN(DBG_INFO, F("enqueued cmd failed/timeout")); DPRINTLN(DBG_INFO, F("enqueued cmd failed/timeout"));
if (mConfig.serialDebug) { if (mConfig.serialDebug) {
DPRINT(DBG_INFO, F("(#") + String(iv->id) + ") "); 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.reset(iv, mUtcTimestamp);
mPayload[iv->id].requested = true; mPayload.request(iv);
yield(); yield();
if (mConfig.serialDebug) { if (mConfig.serialDebug) {
@ -250,14 +216,14 @@ void app::loop(void) {
if (mConfig.serialDebug) if (mConfig.serialDebug)
DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") Devcontrol request ") + String(iv->devControlCmd) + F(" power limit ") + String(iv->powerLimit[0])); 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); mSys->Radio.sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit);
mPayload[iv->id].txCmd = iv->devControlCmd; mPayload.setTxCmd(iv, iv->devControlCmd);
iv->clearCmdQueue(); iv->clearCmdQueue();
iv->enqueCommand<InfoCommand>(SystemConfigPara); iv->enqueCommand<InfoCommand>(SystemConfigPara);
} else { } else {
uint8_t cmd = iv->getQueuedCmd(); uint8_t cmd = iv->getQueuedCmd();
DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") sendTimePacket")); DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") sendTimePacket"));
mSys->Radio.sendTimePacket(iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmMesIndex); mSys->Radio.sendTimePacket(iv->radioId.u64, cmd, mPayload.getTs(iv), iv->alarmMesIndex);
mPayload[iv->id].txCmd = cmd; mPayload.setTxCmd(iv, cmd);
mRxTicker = 0; mRxTicker = 0;
} }
} }
@ -276,134 +242,6 @@ void app::handleIntr(void) {
mSys->Radio.handleIntr(); 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) { bool app::getWifiApActive(void) {
return mWifi->getApActive(); return mWifi->getApActive();
@ -451,9 +289,7 @@ void app::resetSystem(void) {
mShowRebootRequest = false; mShowRebootRequest = false;
memset(mPayload, 0, (MAX_NUM_INVERTERS * sizeof(invPayload_t)));
memset(&mStat, 0, sizeof(statistics_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++) { for (uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
iv = mSys->getInverterByPos(i, false); iv = mSys->getInverterByPos(i, false);
if (NULL != iv) 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;
}

23
src/app.h

@ -21,6 +21,7 @@
#include "hm/CircularBuffer.h" #include "hm/CircularBuffer.h"
#include "hm/hmSystem.h" #include "hm/hmSystem.h"
#include "hm/payload.h"
#include "wifi/ahoywifi.h" #include "wifi/ahoywifi.h"
#include "web/mqtt.h" #include "web/mqtt.h"
#include "web/web.h" #include "web/web.h"
@ -33,19 +34,7 @@
typedef HmSystem<MAX_NUM_INVERTERS> HmSystemType; typedef HmSystem<MAX_NUM_INVERTERS> HmSystemType;
typedef mqtt<HmSystemType> MqttType; typedef mqtt<HmSystemType> MqttType;
typedef payload<HmSystemType> PayloadType;
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;
class ahoywifi; class ahoywifi;
class web; class web;
@ -65,6 +54,10 @@ class app {
void scanAvailNetworks(void); void scanAvailNetworks(void);
void getAvailNetworks(JsonObject obj); void getAvailNetworks(JsonObject obj);
void payloadEventListener(uint8_t cmd) {
mMqttSendList.push(cmd);
}
uint8_t getIrqPin(void) { uint8_t getIrqPin(void) {
return mConfig.pinIrq; return mConfig.pinIrq;
} }
@ -183,7 +176,6 @@ class app {
void setupLed(void); void setupLed(void);
void updateLed(void); void updateLed(void);
bool buildPayload(uint8_t id);
void processPayload(bool retransmit); void processPayload(bool retransmit);
inline uint16_t buildEEpCrc(uint32_t start, uint32_t length) { inline uint16_t buildEEpCrc(uint32_t start, uint32_t length) {
@ -268,13 +260,12 @@ class app {
sysConfig_t mSysConfig; sysConfig_t mSysConfig;
config_t mConfig; config_t mConfig;
char mVersion[12]; char mVersion[12];
PayloadType mPayload;
uint16_t mSendTicker; uint16_t mSendTicker;
uint8_t mSendLastIvId; uint8_t mSendLastIvId;
invPayload_t mPayload[MAX_NUM_INVERTERS];
statistics_t mStat; statistics_t mStat;
uint8_t mLastPacketId;
// timer // timer
uint32_t mTicker; uint32_t mTicker;

250
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 <Arduino.h>
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<void(uint8_t)> payloadListenerType;
template<class HMSYSTEM>
class payload : public Handler<payloadListenerType> {
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<payloadListenerType>::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_*/

4
src/platformio.ini

@ -64,7 +64,7 @@ monitor_filters =
time ; Add timestamp with milliseconds for each new line 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 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 platform = espressif8266
board = esp8285 board = esp8285
board_build.ldscript = eagle.flash.1m64.ld board_build.ldscript = eagle.flash.1m64.ld
@ -75,7 +75,7 @@ monitor_filters =
time ; Add timestamp with milliseconds for each new line 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 ;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 platform = espressif8266
board = esp8285 board = esp8285
board_build.ldscript = eagle.flash.1m64.ld board_build.ldscript = eagle.flash.1m64.ld

33
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 <memory>
#include <functional>
#include <list>
template<class TYPE>
class Handler {
public:
Handler() {}
void addListener(TYPE f) {
mList.push_back(f);
}
virtual void notify(void) {
for(typename std::list<TYPE>::iterator it = mList.begin(); it != mList.end(); ++it) {
(*it);
}
}
protected:
std::list<TYPE> mList;
};
#endif /*__HANDLER_H__*/

4
src/web/html/index.html

@ -54,7 +54,7 @@
<h3>Support this project:</h3> <h3>Support this project:</h3>
<ul> <ul>
<li>Report <a href="https://github.com/lumapu/ahoy/issues" target="_blank">issues</a></li> <li>Report <a href="https://github.com/lumapu/ahoy/issues" target="_blank">issues</a></li>
<li>Contribute to <a href="https://github.com/lumapu/ahoy/blob/main/tools/esp8266/User_Manual.md" target="_blank">documentation</a></li> <li>Contribute to <a href="https://github.com/lumapu/ahoy/blob/main/User_Manual.md" target="_blank">documentation</a></li>
<li>Test <a href="https://github.com/lumapu/ahoy/actions/workflows/compile_development.yml" target="_blank">development firmware</a></li> <li>Test <a href="https://github.com/lumapu/ahoy/actions/workflows/compile_development.yml" target="_blank">development firmware</a></li>
<li>make a <a href="https://paypal.me/lupusch" target="_blank">donation</a></li> <li>make a <a href="https://paypal.me/lupusch" target="_blank">donation</a></li>
</ul> </ul>
@ -153,7 +153,7 @@
for(var i of obj) { for(var i of obj) {
html += "Inverter #" + i["id"] + ": " + i["name"] + " (v" + i["version"] + ") is "; html += "Inverter #" + i["id"] + ": " + i["name"] + " (v" + i["version"] + ") is ";
if(false == i["is_avail"]) if(false == i["is_avail"])
html += "not yet available"; html += "not yet available\n";
else { else {
html += "available and is "; html += "available and is ";
if(false == i["is_producing"]) if(false == i["is_producing"])

Loading…
Cancel
Save