mirror of
https://github.com/lumapu/ahoy.git
synced 2025-05-22 21:36:11 +02:00
improved payload handling (retransmit all fragments on CRC error)
improved `isAvailable`, checkes all record structs, inverter becomes available more early because version is check first fix tickers were not set if NTP is not available disabled annoying `FlashStringHelper` it gives randomly Expeptions during development, feels more stable since then moved erase button to the bottom in settings, not nice but more functional split `tx_count` to `tx_cnt` and `retransmits` in `system.html` fix mqtt retransmit IP address #602 added debug infos for `scheduler` (web -> `/debug` as trigger prints list of tickers to serial console)
This commit is contained in:
parent
3d3e3dc8c6
commit
3adcb68d98
19 changed files with 191 additions and 115 deletions
|
@ -2,6 +2,16 @@
|
|||
|
||||
(starting from release version `0.5.66`)
|
||||
|
||||
## 0.5.74
|
||||
* improved payload handling (retransmit all fragments on CRC error)
|
||||
* improved `isAvailable`, checkes all record structs, inverter becomes available more early because version is check first
|
||||
* fix tickers were not set if NTP is not available
|
||||
* disabled annoying `FlashStringHelper` it gives randomly Expeptions during development, feels more stable since then
|
||||
* moved erase button to the bottom in settings, not nice but more functional
|
||||
* split `tx_count` to `tx_cnt` and `retransmits` in `system.html`
|
||||
* fix mqtt retransmit IP address #602
|
||||
* added debug infos for `scheduler` (web -> `/debug` as trigger prints list of tickers to serial console)
|
||||
|
||||
## 0.5.73
|
||||
* improved payload handling (request / retransmit) #464
|
||||
* included alarm ID parse to serial console (in development)
|
||||
|
|
45
src/app.cpp
45
src/app.cpp
|
@ -3,11 +3,6 @@
|
|||
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if defined(ESP32) && defined(F)
|
||||
#undef F
|
||||
#define F(sl) (sl)
|
||||
#endif
|
||||
|
||||
#include "app.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include "utils/sun.h"
|
||||
|
@ -43,7 +38,7 @@ void app::setup() {
|
|||
|
||||
mWifi.setup(mConfig, &mTimestamp, std::bind(&app::onWifi, this, std::placeholders::_1));
|
||||
#if !defined(AP_ONLY)
|
||||
everySec(std::bind(&ahoywifi::tickWifiLoop, &mWifi));
|
||||
everySec(std::bind(&ahoywifi::tickWifiLoop, &mWifi), "wifiL");
|
||||
#endif
|
||||
|
||||
mSys->addInverters(&mConfig->inst);
|
||||
|
@ -136,48 +131,49 @@ void app::onWifi(bool gotIp) {
|
|||
regularTickers(); // reinstall regular tickers
|
||||
if (gotIp) {
|
||||
mInnerLoopCb = std::bind(&app::loopStandard, this);
|
||||
mSendTickerId = every(std::bind(&app::tickSend, this), mConfig->nrf.sendInterval);
|
||||
mSendTickerId = every(std::bind(&app::tickSend, this), mConfig->nrf.sendInterval, "tSend");
|
||||
mMqttReconnect = true;
|
||||
once(std::bind(&app::tickNtpUpdate, this), 2);
|
||||
once(std::bind(&app::tickNtpUpdate, this), 2, "ntp2");
|
||||
}
|
||||
else {
|
||||
mInnerLoopCb = std::bind(&app::loopWifi, this);
|
||||
everySec(std::bind(&ahoywifi::tickWifiLoop, &mWifi));
|
||||
everySec(std::bind(&ahoywifi::tickWifiLoop, &mWifi), "wifiL");
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void app::regularTickers(void) {
|
||||
DPRINTLN(DBG_DEBUG, F("regularTickers"));
|
||||
everySec(std::bind(&WebType::tickSecond, &mWeb));
|
||||
everySec(std::bind(&WebType::tickSecond, &mWeb), "webSc");
|
||||
// Plugins
|
||||
#if defined(ENA_NOKIA) || defined(ENA_SSD1306) || defined(ENA_SH1106)
|
||||
everySec(std::bind(&MonoDisplayType::tickerSecond, &mMonoDisplay));
|
||||
#endif
|
||||
every(std::bind(&PubSerialType::tick, &mPubSerial), mConfig->serial.interval);
|
||||
every(std::bind(&PubSerialType::tick, &mPubSerial), mConfig->serial.interval, "uart");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void app::tickNtpUpdate(void) {
|
||||
uint32_t nxtTrig = 5; // default: check again in 5 sec
|
||||
if (mWifi.getNtpTime()) {
|
||||
if (mWifi.getNtpTime(&nxtTrig)) {
|
||||
if (mMqttReconnect && mMqttEnabled) {
|
||||
mMqtt.connect();
|
||||
everySec(std::bind(&PubMqttType::tickerSecond, &mMqtt));
|
||||
everyMin(std::bind(&PubMqttType::tickerMinute, &mMqtt));
|
||||
everySec(std::bind(&PubMqttType::tickerSecond, &mMqtt), "mqttS");
|
||||
everyMin(std::bind(&PubMqttType::tickerMinute, &mMqtt), "mqttM");
|
||||
uint32_t nxtTrig = mTimestamp - ((mTimestamp - 1) % 86400) + 86400; // next midnight
|
||||
if(mConfig->mqtt.rstYieldMidNight)
|
||||
onceAt(std::bind(&app::tickMidnight, this), nxtTrig);
|
||||
onceAt(std::bind(&app::tickMidnight, this), nxtTrig, "midNi");
|
||||
mMqttReconnect = false;
|
||||
}
|
||||
|
||||
nxtTrig = 43200; // check again in 12 h
|
||||
nxtTrig = 43200;
|
||||
|
||||
if((mSunrise == 0) && (mConfig->sun.lat) && (mConfig->sun.lon)) {
|
||||
mCalculatedTimezoneOffset = (int8_t)((mConfig->sun.lon >= 0 ? mConfig->sun.lon + 7.5 : mConfig->sun.lon - 7.5) / 15) * 3600;
|
||||
tickCalcSunrise();
|
||||
}
|
||||
}
|
||||
once(std::bind(&app::tickNtpUpdate, this), nxtTrig);
|
||||
once(std::bind(&app::tickNtpUpdate, this), nxtTrig, "ntp");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -191,7 +187,7 @@ void app::tickCalcSunrise(void) {
|
|||
tickIVCommunication();
|
||||
|
||||
uint32_t nxtTrig = mSunset + mConfig->sun.offsetSec + 60; // set next trigger to communication stop, +60 for safety that it is certain past communication stop
|
||||
onceAt(std::bind(&app::tickCalcSunrise, this), nxtTrig);
|
||||
onceAt(std::bind(&app::tickCalcSunrise, this), nxtTrig, "Sunri");
|
||||
if (mMqttEnabled)
|
||||
tickSun();
|
||||
}
|
||||
|
@ -212,7 +208,7 @@ void app::tickIVCommunication(void) {
|
|||
}
|
||||
}
|
||||
if (nxtTrig != 0)
|
||||
onceAt(std::bind(&app::tickIVCommunication, this), nxtTrig);
|
||||
onceAt(std::bind(&app::tickIVCommunication, this), nxtTrig, "ivCom");
|
||||
}
|
||||
if (mMqttEnabled)
|
||||
tickComm();
|
||||
|
@ -222,14 +218,14 @@ void app::tickIVCommunication(void) {
|
|||
void app::tickSun(void) {
|
||||
// only used and enabled by MQTT (see setup())
|
||||
if (!mMqtt.tickerSun(mSunrise, mSunset, mConfig->sun.offsetSec, mConfig->sun.disNightCom))
|
||||
once(std::bind(&app::tickSun, this), 1); // MQTT not connected, retry
|
||||
once(std::bind(&app::tickSun, this), 1, "mqSun"); // MQTT not connected, retry
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void app::tickComm(void) {
|
||||
// only used and enabled by MQTT (see setup())
|
||||
if (!mMqtt.tickerComm(!mIVCommunicationOn))
|
||||
once(std::bind(&app::tickComm, this), 1); // MQTT not connected, retry
|
||||
once(std::bind(&app::tickComm, this), 1, "mqCom"); // MQTT not connected, retry
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -268,7 +264,7 @@ void app::tickSend(void) {
|
|||
void app::tickMidnight(void) {
|
||||
// only used and enabled by MQTT (see setup())
|
||||
uint32_t nxtTrig = mTimestamp - ((mTimestamp - 1) % 86400) + 86400; // next midnight
|
||||
onceAt(std::bind(&app::tickMidnight, this), nxtTrig);
|
||||
onceAt(std::bind(&app::tickMidnight, this), nxtTrig, "mid2");
|
||||
|
||||
mMqtt.tickerMidnight();
|
||||
}
|
||||
|
@ -285,8 +281,6 @@ void app::resetSystem(void) {
|
|||
|
||||
#ifdef AP_ONLY
|
||||
mTimestamp = 1;
|
||||
#else
|
||||
mTimestamp = 0;
|
||||
#endif
|
||||
|
||||
mSunrise = 0;
|
||||
|
@ -329,8 +323,7 @@ void app::updateLed(void) {
|
|||
if(mConfig->led.led0 != 0xff) {
|
||||
Inverter<> *iv = mSys->getInverterByPos(0);
|
||||
if (NULL != iv) {
|
||||
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
|
||||
if(iv->isProducing(mTimestamp, rec))
|
||||
if(iv->isProducing(mTimestamp))
|
||||
digitalWrite(mConfig->led.led0, LOW); // LED on
|
||||
else
|
||||
digitalWrite(mConfig->led.led0, HIGH); // LED off
|
||||
|
|
14
src/app.h
14
src/app.h
|
@ -102,7 +102,7 @@ class app : public IApp, public ah::Scheduler {
|
|||
}
|
||||
|
||||
void setRebootFlag() {
|
||||
once(std::bind(&app::tickReboot, this), 3);
|
||||
once(std::bind(&app::tickReboot, this), 3, "rboot");
|
||||
}
|
||||
|
||||
const char *getVersion() {
|
||||
|
@ -126,7 +126,7 @@ class app : public IApp, public ah::Scheduler {
|
|||
}
|
||||
|
||||
void setMqttDiscoveryFlag() {
|
||||
once(std::bind(&PubMqttType::sendDiscoveryConfig, &mMqtt), 1);
|
||||
once(std::bind(&PubMqttType::sendDiscoveryConfig, &mMqtt), 1, "disCf");
|
||||
}
|
||||
|
||||
void setMqttPowerLimitAck(Inverter<> *iv) {
|
||||
|
@ -174,10 +174,16 @@ class app : public IApp, public ah::Scheduler {
|
|||
getStat(max);
|
||||
}
|
||||
|
||||
void getSchedulerNames(void) {
|
||||
printSchedulers();
|
||||
}
|
||||
|
||||
void setTimestamp(uint32_t newTime) {
|
||||
DPRINTLN(DBG_DEBUG, F("setTimestamp: ") + String(newTime));
|
||||
if(0 == newTime)
|
||||
mWifi.getNtpTime();
|
||||
if(0 == newTime) {
|
||||
uint32_t tmp;
|
||||
mWifi.getNtpTime(&tmp);
|
||||
}
|
||||
else
|
||||
Scheduler::setTimestamp(newTime);
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ class IApp {
|
|||
virtual String getTimeStr(uint32_t offset) = 0;
|
||||
virtual uint32_t getTimezoneOffset() = 0;
|
||||
virtual void getSchedulerInfo(uint8_t *max) = 0;
|
||||
virtual void getSchedulerNames() = 0;
|
||||
|
||||
virtual bool getRebootRequestState() = 0;
|
||||
virtual bool getSettingsValid() = 0;
|
||||
|
|
|
@ -162,10 +162,11 @@ class settings {
|
|||
if(!LittleFS.begin(LITTLFS_FALSE)) {
|
||||
DPRINTLN(DBG_INFO, F(".. format .."));
|
||||
LittleFS.format();
|
||||
if(LittleFS.begin(LITTLFS_TRUE))
|
||||
if(LittleFS.begin(LITTLFS_TRUE)) {
|
||||
DPRINTLN(DBG_INFO, F(".. success"));
|
||||
else
|
||||
} else {
|
||||
DPRINTLN(DBG_INFO, F(".. failed"));
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
//-------------------------------------
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 5
|
||||
#define VERSION_PATCH 73
|
||||
#define VERSION_PATCH 74
|
||||
|
||||
//-------------------------------------
|
||||
typedef struct {
|
||||
|
|
|
@ -164,7 +164,7 @@ class Inverter {
|
|||
if (getFwVersion() == 0)
|
||||
enqueCommand<InfoCommand>(InverterDevInform_All); // firmware version
|
||||
enqueCommand<InfoCommand>(RealTimeRunData_Debug); // live data
|
||||
if (actPowerLimit == 0xffff)
|
||||
if ((actPowerLimit == 0xffff) && isConnected)
|
||||
enqueCommand<InfoCommand>(SystemConfigPara); // power limit info
|
||||
}
|
||||
return _commandQueue.front().get()->getCmd();
|
||||
|
@ -220,9 +220,11 @@ class Inverter {
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool setDevControlRequest() {
|
||||
if(isConnected)
|
||||
bool setDevControlRequest(uint8_t cmd) {
|
||||
if(isConnected) {
|
||||
mDevControlRequest = true;
|
||||
devControlCmd = cmd;
|
||||
}
|
||||
return isConnected;
|
||||
}
|
||||
|
||||
|
@ -333,16 +335,23 @@ class Inverter {
|
|||
}
|
||||
}
|
||||
|
||||
bool isAvailable(uint32_t timestamp, record_t<> *rec) {
|
||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:isAvailable"));
|
||||
return ((timestamp - rec->ts) < INACT_THRES_SEC);
|
||||
bool isAvailable(uint32_t timestamp) {
|
||||
if((timestamp - recordMeas.ts) < INACT_THRES_SEC)
|
||||
return true;
|
||||
if((timestamp - recordInfo.ts) < INACT_THRES_SEC)
|
||||
return true;
|
||||
if((timestamp - recordConfig.ts) < INACT_THRES_SEC)
|
||||
return true;
|
||||
if((timestamp - recordAlarm.ts) < INACT_THRES_SEC)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isProducing(uint32_t timestamp, record_t<> *rec) {
|
||||
bool isProducing(uint32_t timestamp) {
|
||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:isProducing"));
|
||||
if(isAvailable(timestamp, rec)) {
|
||||
uint8_t pos = getPosByChFld(CH0, FLD_PAC, rec);
|
||||
return (getValue(pos, rec) > INACT_PWR_THRESH);
|
||||
if(isAvailable(timestamp)) {
|
||||
uint8_t pos = getPosByChFld(CH0, FLD_PAC, &recordMeas);
|
||||
return (getValue(pos, &recordMeas) > INACT_PWR_THRESH);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -89,6 +89,7 @@ class HmRadio {
|
|||
mRxLoopCnt = RF_LOOP_CNT;
|
||||
|
||||
mSendCnt = 0;
|
||||
mRetransmits = 0;
|
||||
|
||||
mSerialDebug = false;
|
||||
mIrqRcvd = false;
|
||||
|
@ -194,7 +195,7 @@ class HmRadio {
|
|||
return mRfChLst[mTxChIdx];
|
||||
}
|
||||
|
||||
void sendControlPacket(uint64_t invId, uint8_t cmd, uint16_t *data) {
|
||||
void sendControlPacket(uint64_t invId, uint8_t cmd, uint16_t *data, bool isRetransmit) {
|
||||
DPRINTLN(DBG_INFO, F("sendControlPacket cmd: 0x") + String(cmd, HEX));
|
||||
sendCmdPacket(invId, TX_REQ_DEVCONTROL, SINGLE_FRAME, false);
|
||||
uint8_t cnt = 0;
|
||||
|
@ -215,12 +216,12 @@ class HmRadio {
|
|||
// crc over all
|
||||
mTxBuf[10 + cnt] = ah::crc8(mTxBuf, 10 + cnt);
|
||||
|
||||
sendPacket(invId, mTxBuf, 10 + cnt + 1, true);
|
||||
sendPacket(invId, mTxBuf, 10 + cnt + 1, isRetransmit, true);
|
||||
}
|
||||
|
||||
void sendTimePacket(uint64_t invId, uint8_t cmd, uint32_t ts, uint16_t alarmMesId) {
|
||||
void sendTimePacket(uint64_t invId, uint8_t cmd, uint32_t ts, uint16_t alarmMesId, bool isRetransmit) {
|
||||
DPRINTLN(DBG_DEBUG, F("sendTimePacket 0x") + String(cmd, HEX));
|
||||
sendCmdPacket(invId, TX_REQ_INFO, ALL_FRAMES, false);
|
||||
sendCmdPacket(invId, TX_REQ_INFO, ALL_FRAMES, isRetransmit, false);
|
||||
mTxBuf[10] = cmd; // cid
|
||||
mTxBuf[11] = 0x00;
|
||||
CP_U32_LittleEndian(&mTxBuf[12], ts);
|
||||
|
@ -233,10 +234,10 @@ class HmRadio {
|
|||
mTxBuf[25] = (crc ) & 0xff;
|
||||
mTxBuf[26] = ah::crc8(mTxBuf, 26);
|
||||
|
||||
sendPacket(invId, mTxBuf, 27, true);
|
||||
sendPacket(invId, mTxBuf, 27, isRetransmit, true);
|
||||
}
|
||||
|
||||
void sendCmdPacket(uint64_t invId, uint8_t mid, uint8_t pid, bool calcCrc = true) {
|
||||
void sendCmdPacket(uint64_t invId, uint8_t mid, uint8_t pid, bool isRetransmit, bool calcCrc = true) {
|
||||
DPRINTLN(DBG_VERBOSE, F("sendCmdPacket, mid: ") + String(mid, HEX) + F(" pid: ") + String(pid, HEX));
|
||||
memset(mTxBuf, 0, MAX_RF_PAYLOAD_SIZE);
|
||||
mTxBuf[0] = mid; // message id
|
||||
|
@ -245,7 +246,7 @@ class HmRadio {
|
|||
mTxBuf[9] = pid;
|
||||
if(calcCrc) {
|
||||
mTxBuf[10] = ah::crc8(mTxBuf, 10);
|
||||
sendPacket(invId, mTxBuf, 11, false);
|
||||
sendPacket(invId, mTxBuf, 11, isRetransmit, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -308,11 +309,12 @@ class HmRadio {
|
|||
|
||||
|
||||
uint32_t mSendCnt;
|
||||
uint32_t mRetransmits;
|
||||
|
||||
bool mSerialDebug;
|
||||
|
||||
private:
|
||||
void sendPacket(uint64_t invId, uint8_t buf[], uint8_t len, bool clear=false) {
|
||||
void sendPacket(uint64_t invId, uint8_t buf[], uint8_t len, bool isRetransmit, bool clear=false) {
|
||||
//DPRINTLN(DBG_VERBOSE, F("hmRadio.h:sendPacket"));
|
||||
//DPRINTLN(DBG_VERBOSE, "sent packet: #" + String(mSendCnt));
|
||||
//dumpBuf("SEN ", buf, len);
|
||||
|
@ -347,6 +349,9 @@ class HmRadio {
|
|||
mNrf24.startListening();
|
||||
|
||||
RESTORE_IRQ;
|
||||
if(isRetransmit)
|
||||
mRetransmits++;
|
||||
else
|
||||
mSendCnt++;
|
||||
}
|
||||
|
||||
|
|
|
@ -103,14 +103,14 @@ class Payload : public Handler<payloadListenerType> {
|
|||
if (iv->getDevControlRequest()) {
|
||||
if (mSerialDebug)
|
||||
DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") Devcontrol request 0x") + String(iv->devControlCmd, HEX) + F(" power limit ") + String(iv->powerLimit[0]));
|
||||
mSys->Radio.sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit);
|
||||
mSys->Radio.sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit, false);
|
||||
mPayload[iv->id].txCmd = iv->devControlCmd;
|
||||
//iv->clearCmdQueue();
|
||||
//iv->enqueCommand<InfoCommand>(SystemConfigPara); // read back power limit
|
||||
} else {
|
||||
uint8_t cmd = iv->getQueuedCmd();
|
||||
DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") sendTimePacket")); // + String(cmd, HEX));
|
||||
mSys->Radio.sendTimePacket(iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmMesIndex);
|
||||
mSys->Radio.sendTimePacket(iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmMesIndex, false);
|
||||
mPayload[iv->id].txCmd = cmd;
|
||||
}
|
||||
}
|
||||
|
@ -177,7 +177,9 @@ class Payload : public Handler<payloadListenerType> {
|
|||
}
|
||||
|
||||
if (!mPayload[iv->id].complete) {
|
||||
if (!build(iv->id)) { // payload not complete
|
||||
bool crcPass, pyldComplete;
|
||||
crcPass = build(iv->id, &pyldComplete);
|
||||
if (!crcPass && !pyldComplete) { // 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.
|
||||
|
@ -185,7 +187,7 @@ class Payload : public Handler<payloadListenerType> {
|
|||
mPayload[iv->id].retransmits = mMaxRetrans;
|
||||
} else if(iv->devControlCmd == ActivePowerContr) {
|
||||
DPRINTLN(DBG_INFO, F("retransmit power limit"));
|
||||
mSys->Radio.sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit);
|
||||
mSys->Radio.sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit, true);
|
||||
} else {
|
||||
if (mPayload[iv->id].retransmits < mMaxRetrans) {
|
||||
mPayload[iv->id].retransmits++;
|
||||
|
@ -193,12 +195,12 @@ class Payload : public Handler<payloadListenerType> {
|
|||
DPRINTLN(DBG_WARN, F("nothing received: Request Complete Retransmit"));
|
||||
mPayload[iv->id].txCmd = iv->getQueuedCmd();
|
||||
DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") sendTimePacket 0x") + String(mPayload[iv->id].txCmd, HEX));
|
||||
mSys->Radio.sendTimePacket(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex);
|
||||
mSys->Radio.sendTimePacket(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex, true);
|
||||
} else {
|
||||
for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId - 1); i++) {
|
||||
if (mPayload[iv->id].len[i] == 0) {
|
||||
DPRINTLN(DBG_WARN, F("Frame ") + String(i + 1) + F(" missing: Request Retransmit"));
|
||||
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, true);
|
||||
break; // only request retransmit one frame per loop
|
||||
}
|
||||
yield();
|
||||
|
@ -208,6 +210,14 @@ class Payload : public Handler<payloadListenerType> {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if(!crcPass && pyldComplete) { // crc error on complete Payload
|
||||
if (mPayload[iv->id].retransmits < mMaxRetrans) {
|
||||
mPayload[iv->id].retransmits++;
|
||||
DPRINTLN(DBG_WARN, F("CRC Error: Request Complete Retransmit"));
|
||||
mPayload[iv->id].txCmd = iv->getQueuedCmd();
|
||||
DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") sendTimePacket 0x") + String(mPayload[iv->id].txCmd, HEX));
|
||||
mSys->Radio.sendTimePacket(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex, true);
|
||||
}
|
||||
} else { // payload complete
|
||||
DPRINTLN(DBG_INFO, F("procPyld: cmd: 0x") + String(mPayload[iv->id].txCmd, HEX));
|
||||
DPRINTLN(DBG_INFO, F("procPyld: txid: 0x") + String(mPayload[iv->id].txId, HEX));
|
||||
|
@ -269,12 +279,21 @@ class Payload : public Handler<payloadListenerType> {
|
|||
}
|
||||
|
||||
private:
|
||||
bool build(uint8_t id) {
|
||||
bool build(uint8_t id, bool *complete) {
|
||||
DPRINTLN(DBG_VERBOSE, F("build"));
|
||||
uint16_t crc = 0xffff, crcRcv = 0x0000;
|
||||
if (mPayload[id].maxPackId > MAX_PAYLOAD_ENTRIES)
|
||||
mPayload[id].maxPackId = MAX_PAYLOAD_ENTRIES;
|
||||
|
||||
// check if all fragments are there
|
||||
*complete = true;
|
||||
for (uint8_t i = 0; i < mPayload[id].maxPackId; i++) {
|
||||
if(mPayload[id].len[i] == 0)
|
||||
*complete = false;
|
||||
}
|
||||
if(!*complete)
|
||||
return false;
|
||||
|
||||
for (uint8_t i = 0; i < mPayload[id].maxPackId; i++) {
|
||||
if (mPayload[id].len[i] > 0) {
|
||||
if (i == (mPayload[id].maxPackId - 1)) {
|
||||
|
|
|
@ -45,7 +45,6 @@ class PubMqtt {
|
|||
mVersion = version;
|
||||
mSys = sys;
|
||||
mUtcTimestamp = utcTs;
|
||||
mExeOnce = true;
|
||||
mIntervalTimeout = 1;
|
||||
|
||||
snprintf(mLwtTopic, MQTT_TOPIC_LEN + 5, "%s/mqtt", mCfgMqtt->topic);
|
||||
|
@ -243,12 +242,9 @@ class PubMqtt {
|
|||
void onConnect(bool sessionPreset) {
|
||||
DPRINTLN(DBG_INFO, F("MQTT connected"));
|
||||
|
||||
if(mExeOnce) {
|
||||
publish("version", mVersion, true);
|
||||
publish("device", mDevName, true);
|
||||
publish("ip_addr", WiFi.localIP().toString().c_str(), true);
|
||||
mExeOnce = false;
|
||||
}
|
||||
tickerMinute();
|
||||
publish(mLwtTopic, mLwtOnline, true, false);
|
||||
|
||||
|
@ -381,7 +377,7 @@ class PubMqtt {
|
|||
|
||||
// inverter status
|
||||
uint8_t status = MQTT_STATUS_AVAIL_PROD;
|
||||
if ((!iv->isAvailable(*mUtcTimestamp, rec)) || (!iv->config->enabled)) {
|
||||
if ((!iv->isAvailable(*mUtcTimestamp)) || (!iv->config->enabled)) {
|
||||
status = MQTT_STATUS_NOT_AVAIL_NOT_PROD;
|
||||
if(iv->config->enabled) { // only change all-avail if inverter is enabled!
|
||||
totalComplete = false;
|
||||
|
@ -390,7 +386,7 @@ class PubMqtt {
|
|||
}
|
||||
else {
|
||||
mIvAvail = true;
|
||||
if (!iv->isProducing(*mUtcTimestamp, rec)) {
|
||||
if (!iv->isProducing(*mUtcTimestamp)) {
|
||||
if (MQTT_STATUS_AVAIL_PROD == status)
|
||||
status = MQTT_STATUS_AVAIL_NOT_PROD;
|
||||
}
|
||||
|
@ -566,7 +562,6 @@ class PubMqtt {
|
|||
subscriptionCb mSubscriptionCb;
|
||||
bool mIvAvail; // shows if at least one inverter is available
|
||||
uint8_t mLastIvState[MAX_NUM_INVERTERS];
|
||||
bool mExeOnce;
|
||||
uint16_t mIntervalTimeout;
|
||||
|
||||
// last will topic and payload must be available trough lifetime of 'espMqttClient'
|
||||
|
|
|
@ -28,8 +28,8 @@ class PubSerial {
|
|||
Inverter<> *iv = mSys->getInverterByPos(id);
|
||||
if (NULL != iv) {
|
||||
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
|
||||
if (iv->isAvailable(*mUtcTimestamp, rec)) {
|
||||
DPRINTLN(DBG_INFO, F("Inverter: ") + String(id));
|
||||
if (iv->isAvailable(*mUtcTimestamp)) {
|
||||
DPRINTLN(DBG_INFO, "Iv: " + String(id));
|
||||
for (uint8_t i = 0; i < rec->length; i++) {
|
||||
if (0.0f != iv->getValue(i, rec)) {
|
||||
snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/ch%d/%s", iv->config->name, rec->assign[i].ch, iv->getFieldName(i, rec));
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#ifndef __DBG_H__
|
||||
#define __DBG_H__
|
||||
#if defined(ESP32) && defined(F)
|
||||
#if defined(F) //defined(ESP32) &&
|
||||
#undef F
|
||||
#define F(sl) (sl)
|
||||
#endif
|
||||
|
|
|
@ -20,8 +20,9 @@ namespace ah {
|
|||
uint32_t timeout;
|
||||
uint32_t reload;
|
||||
bool isTimestamp;
|
||||
sP() : c(NULL), timeout(0), reload(0), isTimestamp(false) {}
|
||||
sP(scdCb a, uint32_t tmt, uint32_t rl, bool its) : c(a), timeout(tmt), reload(rl), isTimestamp(its) {}
|
||||
char name[6];
|
||||
sP() : c(NULL), timeout(0), reload(0), isTimestamp(false), name("\n") {}
|
||||
sP(scdCb a, uint32_t tmt, uint32_t rl, bool its) : c(a), timeout(tmt), reload(rl), isTimestamp(its), name("\n") {}
|
||||
};
|
||||
|
||||
#define MAX_NUM_TICKER 30
|
||||
|
@ -64,15 +65,15 @@ namespace ah {
|
|||
|
||||
}
|
||||
|
||||
void once(scdCb c, uint32_t timeout) { addTicker(c, timeout, 0, false); }
|
||||
void onceAt(scdCb c, uint32_t timestamp) { addTicker(c, timestamp, 0, true); }
|
||||
uint8_t every(scdCb c, uint32_t interval){ return addTicker(c, interval, interval, false); }
|
||||
void once(scdCb c, uint32_t timeout, const char *name) { addTicker(c, timeout, 0, false, name); }
|
||||
void onceAt(scdCb c, uint32_t timestamp, const char *name) { addTicker(c, timestamp, 0, true, name); }
|
||||
uint8_t every(scdCb c, uint32_t interval, const char *name){ return addTicker(c, interval, interval, false, name); }
|
||||
|
||||
void everySec(scdCb c) { every(c, SCD_SEC); }
|
||||
void everyMin(scdCb c) { every(c, SCD_MIN); }
|
||||
void everyHour(scdCb c) { every(c, SCD_HOUR); }
|
||||
void every12h(scdCb c) { every(c, SCD_12H); }
|
||||
void everyDay(scdCb c) { every(c, SCD_DAY); }
|
||||
void everySec(scdCb c, const char *name) { every(c, SCD_SEC, name); }
|
||||
void everyMin(scdCb c, const char *name) { every(c, SCD_MIN, name); }
|
||||
void everyHour(scdCb c, const char *name) { every(c, SCD_HOUR, name); }
|
||||
void every12h(scdCb c, const char *name) { every(c, SCD_12H, name); }
|
||||
void everyDay(scdCb c, const char *name) { every(c, SCD_DAY, name); }
|
||||
|
||||
virtual void setTimestamp(uint32_t ts) {
|
||||
mTimestamp = ts;
|
||||
|
@ -102,11 +103,19 @@ namespace ah {
|
|||
*max = mMax;
|
||||
}
|
||||
|
||||
void printSchedulers() {
|
||||
for (uint8_t i = 0; i < MAX_NUM_TICKER; i++) {
|
||||
if (mTickerInUse[i]) {
|
||||
DPRINTLN(DBG_INFO, String(mTicker[i].name) + ", tmt: " + String(mTicker[i].timeout) + ", rel: " + String(mTicker[i].reload));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t mTimestamp;
|
||||
|
||||
private:
|
||||
inline uint8_t addTicker(scdCb c, uint32_t timeout, uint32_t reload, bool isTimestamp) {
|
||||
inline uint8_t addTicker(scdCb c, uint32_t timeout, uint32_t reload, bool isTimestamp, const char *name) {
|
||||
for (uint8_t i = 0; i < MAX_NUM_TICKER; i++) {
|
||||
if (!mTickerInUse[i]) {
|
||||
mTickerInUse[i] = true;
|
||||
|
@ -114,6 +123,8 @@ namespace ah {
|
|||
mTicker[i].timeout = timeout;
|
||||
mTicker[i].reload = reload;
|
||||
mTicker[i].isTimestamp = isTimestamp;
|
||||
memset(mTicker[i].name, 0, 6);
|
||||
strncpy(mTicker[i].name, name, (strlen(name) < 6) ? strlen(name) : 5);
|
||||
if(mMax == i)
|
||||
mMax = i + 1;
|
||||
return i;
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
|
||||
#include "../appInterface.h"
|
||||
|
||||
#if defined(F) //defined(ESP32) &&
|
||||
#undef F
|
||||
#define F(sl) (sl)
|
||||
#endif
|
||||
|
||||
template<class HMSYSTEM>
|
||||
class RestApi {
|
||||
public:
|
||||
|
@ -265,6 +270,7 @@ class RestApi {
|
|||
obj[F("rx_fail_answer")] = stat->rxFailNoAnser;
|
||||
obj[F("frame_cnt")] = stat->frmCnt;
|
||||
obj[F("tx_cnt")] = mSys->Radio.mSendCnt;
|
||||
obj[F("retransmits")] = mSys->Radio.mRetransmits;
|
||||
}
|
||||
|
||||
void getInverterList(JsonObject obj) {
|
||||
|
@ -413,8 +419,8 @@ class RestApi {
|
|||
invObj[F("id")] = i;
|
||||
invObj[F("name")] = String(iv->config->name);
|
||||
invObj[F("version")] = String(iv->getFwVersion());
|
||||
invObj[F("is_avail")] = iv->isAvailable(mApp->getTimestamp(), rec);
|
||||
invObj[F("is_producing")] = iv->isProducing(mApp->getTimestamp(), rec);
|
||||
invObj[F("is_avail")] = iv->isAvailable(mApp->getTimestamp());
|
||||
invObj[F("is_producing")] = iv->isProducing(mApp->getTimestamp());
|
||||
invObj[F("ts_last_success")] = iv->getLastTs(rec);
|
||||
}
|
||||
}
|
||||
|
@ -546,13 +552,10 @@ class RestApi {
|
|||
return false;
|
||||
}
|
||||
|
||||
if(F("power") == jsonIn[F("cmd")]) {
|
||||
iv->devControlCmd = (jsonIn[F("val")] == 1) ? TurnOn : TurnOff;
|
||||
accepted = iv->setDevControlRequest();
|
||||
} else if(F("restart") == jsonIn[F("restart")]) {
|
||||
iv->devControlCmd = Restart;
|
||||
accepted = iv->setDevControlRequest();
|
||||
}
|
||||
if(F("power") == jsonIn[F("cmd")])
|
||||
accepted = iv->setDevControlRequest((jsonIn[F("val")] == 1) ? TurnOn : TurnOff);
|
||||
else if(F("restart") == jsonIn[F("restart")])
|
||||
accepted = iv->setDevControlRequest(Restart);
|
||||
else if(0 == strncmp("limit_", jsonIn[F("cmd")].as<const char*>(), 6)) {
|
||||
iv->powerLimit[0] = jsonIn["val"];
|
||||
if(F("limit_persistent_relative") == jsonIn[F("cmd")])
|
||||
|
@ -563,10 +566,8 @@ class RestApi {
|
|||
iv->powerLimit[1] = RelativNonPersistent;
|
||||
else if(F("limit_nonpersistent_absolute") == jsonIn[F("cmd")])
|
||||
iv->powerLimit[1] = AbsolutNonPersistent;
|
||||
iv->devControlCmd = ActivePowerContr;
|
||||
accepted = iv->setDevControlRequest();
|
||||
if(accepted)
|
||||
mApp->ivSendHighPrio(iv);
|
||||
|
||||
accepted = iv->setDevControlRequest(ActivePowerContr);
|
||||
}
|
||||
else if(F("dev") == jsonIn[F("cmd")]) {
|
||||
DPRINTLN(DBG_INFO, F("dev cmd"));
|
||||
|
@ -580,7 +581,8 @@ class RestApi {
|
|||
if(!accepted) {
|
||||
jsonOut[F("error")] = F("inverter does not accept dev control request at this moment");
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
mApp->ivSendHighPrio(iv);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -30,14 +30,6 @@
|
|||
</div>
|
||||
<div id="wrapper">
|
||||
<div id="content">
|
||||
<a class="btn" href="/erase">ERASE SETTINGS (not WiFi)</a>
|
||||
<fieldset>
|
||||
<legend class="des">Upload JSON Settings</legend>
|
||||
<form id="form" method="POST" action="/upload" enctype="multipart/form-data" accept-charset="utf-8">
|
||||
<input type="file" name="upload">
|
||||
<input type="button" class="btn" value="Upload" onclick="hide()">
|
||||
</form>
|
||||
</fieldset>
|
||||
<form method="post" action="/save">
|
||||
<fieldset>
|
||||
<legend class="des">Device Host Name</legend>
|
||||
|
@ -196,7 +188,15 @@
|
|||
</div>
|
||||
<div class="hr mb-3 mt-3"></div>
|
||||
<div class="mb-4">
|
||||
<a href="/get_setup" target="_blank">Download your settings (JSON file)</a> (only saved values, passwords will be removed!)
|
||||
<a class="btn" href="/erase">ERASE SETTINGS (not WiFi)</a>
|
||||
<fieldset>
|
||||
<legend class="des">Upload / Store JSON Settings</legend>
|
||||
<form id="form" method="POST" action="/upload" enctype="multipart/form-data" accept-charset="utf-8">
|
||||
<input type="file" name="upload">
|
||||
<input type="button" class="btn" value="Upload" onclick="hide()">
|
||||
</form>
|
||||
</fieldset>
|
||||
<a class="btn" href="/get_setup" target="_blank">Download settings (JSON file)</a> (only saved values, passwords will be removed!)
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -94,11 +94,12 @@
|
|||
}
|
||||
|
||||
main.append(
|
||||
genTabRow("TX count", stat["tx_cnt"]),
|
||||
genTabRow("RX success", stat["rx_success"]),
|
||||
genTabRow("RX fail", stat["rx_fail"]),
|
||||
genTabRow("RX no answer", stat["rx_fail_answer"]),
|
||||
genTabRow("RX frames received", stat["frame_cnt"]),
|
||||
genTabRow("TX count", stat["tx_cnt"])
|
||||
genTabRow("RX fragments", stat["frame_cnt"]),
|
||||
genTabRow("TX retransmits", stat["retransmits"])
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -86,6 +86,7 @@ class Web {
|
|||
mWeb.on("/upload", HTTP_POST, std::bind(&Web::onUpload, this, std::placeholders::_1),
|
||||
std::bind(&Web::onUpload2, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6));
|
||||
mWeb.on("/serial", HTTP_GET, std::bind(&Web::onSerial, this, std::placeholders::_1));
|
||||
mWeb.on("/debug", HTTP_GET, std::bind(&Web::onDebug, this, std::placeholders::_1));
|
||||
|
||||
|
||||
mEvts.onConnect(std::bind(&Web::onConnect, this, std::placeholders::_1));
|
||||
|
@ -189,12 +190,15 @@ class Web {
|
|||
msg.replace("\r\n", "<rn>");
|
||||
if(mSerialAddTime) {
|
||||
if((9 + mSerialBufFill) <= WEB_SERIAL_BUF_SIZE) {
|
||||
if(mApp->getTimestamp() > 0) {
|
||||
strncpy(&mSerialBuf[mSerialBufFill], mApp->getTimeStr(mApp->getTimezoneOffset()).c_str(), 9);
|
||||
mSerialBufFill += 9;
|
||||
}
|
||||
}
|
||||
else {
|
||||
mSerialBufFill = 0;
|
||||
mEvts.send("webSerial, buffer overflow!", "serial", millis());
|
||||
mEvts.send("webSerial, buffer overflow!<rn>", "serial", millis());
|
||||
return;
|
||||
}
|
||||
mSerialAddTime = false;
|
||||
}
|
||||
|
@ -209,7 +213,7 @@ class Web {
|
|||
}
|
||||
else {
|
||||
mSerialBufFill = 0;
|
||||
mEvts.send("webSerial, buffer overflow!", "serial", millis());
|
||||
mEvts.send("webSerial, buffer overflow!<rn>", "serial", millis());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -649,6 +653,12 @@ class Web {
|
|||
request->send(200, "text/json", "{success:true}");
|
||||
}*/
|
||||
|
||||
void onDebug(AsyncWebServerRequest *request) {
|
||||
mApp->getSchedulerNames();
|
||||
AsyncWebServerResponse *response = request->beginResponse(200, F("text/html"), "ok");
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
void onSerial(AsyncWebServerRequest *request) {
|
||||
DPRINTLN(DBG_VERBOSE, F("onSerial"));
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
// NTP CONFIG
|
||||
#define NTP_PACKET_SIZE 48
|
||||
|
||||
#define NTP_RETRIES 5
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
ahoywifi::ahoywifi() : mApIp(192, 168, 4, 1) {}
|
||||
|
@ -26,6 +26,7 @@ void ahoywifi::setup(settings_t *config, uint32_t *utcTimestamp, appWifiCb cb) {
|
|||
mStaConn = DISCONNECTED;
|
||||
mCnt = 0;
|
||||
mScanActive = false;
|
||||
mRetries = NTP_RETRIES;
|
||||
|
||||
#if defined(ESP8266)
|
||||
wifiConnectHandler = WiFi.onStationModeConnected(std::bind(&ahoywifi::onConnect, this, std::placeholders::_1));
|
||||
|
@ -148,7 +149,17 @@ void ahoywifi::setupStation(void) {
|
|||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool ahoywifi::getNtpTime(void) {
|
||||
bool ahoywifi::getNtpTime(uint32_t *nxtTrig) {
|
||||
if(0 != mRetries) {
|
||||
DPRINTLN(DBG_INFO, "try to getNtpTime");
|
||||
*nxtTrig = 43200; // check again in 12h (if NTP was successful)
|
||||
mRetries--;
|
||||
} else if(0 != *mUtcTimestamp) { // time is availabe, but NTP not
|
||||
*nxtTrig = 5; // check again 5s
|
||||
mRetries = NTP_RETRIES;
|
||||
return true; // true is necessary to enable all timers even if NTP was not reachable
|
||||
}
|
||||
|
||||
if(GOT_IP != mStaConn)
|
||||
return false;
|
||||
|
||||
|
@ -267,6 +278,7 @@ void ahoywifi::connectionEvent(WiFiStatus_t status) {
|
|||
setupWifi(); // reconnect with AP / Station setup
|
||||
mAppWifiCb(false);
|
||||
DPRINTLN(DBG_INFO, "[WiFi] Connection Lost");
|
||||
mRetries = NTP_RETRIES;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ class ahoywifi {
|
|||
|
||||
void setup(settings_t *config, uint32_t *utcTimestamp, appWifiCb cb);
|
||||
void tickWifiLoop(void);
|
||||
bool getNtpTime(void);
|
||||
bool getNtpTime(uint32_t *nxtTrig);
|
||||
void scanAvailNetworks(void);
|
||||
void getAvailNetworks(JsonObject obj);
|
||||
|
||||
|
@ -68,6 +68,7 @@ class ahoywifi {
|
|||
|
||||
uint8_t mLoopCnt;
|
||||
bool mScanActive;
|
||||
uint8_t mRetries;
|
||||
};
|
||||
|
||||
#endif /*__AHOYWIFI_H__*/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue