diff --git a/src/CHANGES.md b/src/CHANGES.md index aad3b75a..89f334cc 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -1,5 +1,12 @@ # Development Changes +## 0.7.61 - 2023-10-01 +* merged `hmPayload` and `hmsPayload` into single class +* merged generic radio functions into new parent class `radio.h` +* moved radio statistics into the inverter - each inverter has now seperate statistics which can be accessed by click on the footer in `/live` +* fix compiler warnings #1191 +* fix ePaper logo during night time #1151 + ## 0.7.60 - 2023-09-27 * fixed typos in changelog #1172 * fixed MqTT manual clientId storage #1174 diff --git a/src/app.cpp b/src/app.cpp index b7ce04ae..49205dc0 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -34,12 +34,12 @@ void app::setup() { DBGPRINTLN(F("false")); if(mConfig->nrf.enabled) { - mNrfRadio.setup(&mNrfStat, mConfig->nrf.amplifierPower, mConfig->nrf.pinIrq, mConfig->nrf.pinCe, mConfig->nrf.pinCs, mConfig->nrf.pinSclk, mConfig->nrf.pinMosi, mConfig->nrf.pinMiso); + mNrfRadio.setup(mConfig->nrf.amplifierPower, mConfig->nrf.pinIrq, mConfig->nrf.pinCe, mConfig->nrf.pinCs, mConfig->nrf.pinSclk, mConfig->nrf.pinMosi, mConfig->nrf.pinMiso); mNrfRadio.enableDebug(); } #if defined(ESP32) if(mConfig->cmt.enabled) { - mCmtRadio.setup(&mCmtStat, mConfig->cmt.pinSclk, mConfig->cmt.pinSdio, mConfig->cmt.pinCsb, mConfig->cmt.pinFcsb, false); + mCmtRadio.setup(mConfig->cmt.pinSclk, mConfig->cmt.pinSdio, mConfig->cmt.pinCsb, mConfig->cmt.pinFcsb, false); mCmtRadio.enableDebug(); } #endif @@ -59,24 +59,27 @@ void app::setup() { #endif #endif /* defined(ETHERNET) */ - mSys.setup(&mTimestamp); - mSys.addInverters(&mConfig->inst); - if (mConfig->nrf.enabled) { - mPayload.setup(this, &mSys, &mNrfRadio, &mNrfStat, mConfig->nrf.maxRetransPerPyld, &mTimestamp); - mPayload.enableSerialDebug(mConfig->serial.debug); - mPayload.addPayloadListener(std::bind(&app::payloadEventListener, this, std::placeholders::_1, std::placeholders::_2)); + mSys.setup(&mTimestamp, &mConfig->inst); + for (uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { + mSys.addInverter(i, [this](Inverter<> *iv) { + if((IV_MI == iv->ivGen) || (IV_HM == iv->ivGen)) + iv->radio = &mNrfRadio; + #if defined(ESP32) + else if((IV_HMS == iv->ivGen) || (IV_HMT == iv->ivGen)) + iv->radio = &mCmtRadio; + #endif + }); + } - mMiPayload.setup(this, &mSys, &mNrfRadio, &mNrfStat, mConfig->nrf.maxRetransPerPyld, &mTimestamp); + mPayload.setup(this, &mSys, mConfig->nrf.maxRetransPerPyld, &mTimestamp); + mPayload.enableSerialDebug(mConfig->serial.debug); + mPayload.addPayloadListener(std::bind(&app::payloadEventListener, this, std::placeholders::_1, std::placeholders::_2)); + if (mConfig->nrf.enabled) { + mMiPayload.setup(this, &mSys, mConfig->nrf.maxRetransPerPyld, &mTimestamp); mMiPayload.enableSerialDebug(mConfig->serial.debug); mMiPayload.addPayloadListener(std::bind(&app::payloadEventListener, this, std::placeholders::_1, std::placeholders::_2)); } - #if defined(ESP32) - mHmsPayload.setup(this, &mSys, &mCmtRadio, &mCmtStat, 5, &mTimestamp); - mHmsPayload.enableSerialDebug(mConfig->serial.debug); - mHmsPayload.addPayloadListener(std::bind(&app::payloadEventListener, this, std::placeholders::_1, std::placeholders::_2)); - #endif - if(mConfig->nrf.enabled) { if (!mNrfRadio.isChipConnected()) DPRINTLN(DBG_WARN, F("WARNING! your NRF24 module can't be reached, check the wiring")); @@ -90,9 +93,6 @@ void app::setup() { mMqtt.setSubscriptionCb(std::bind(&app::mqttSubRxCb, this, std::placeholders::_1)); mPayload.addAlarmListener([this](Inverter<> *iv) { mMqtt.alarmEvent(iv); }); mMiPayload.addAlarmListener([this](Inverter<> *iv) { mMqtt.alarmEvent(iv); }); - #if defined(ESP32) - mHmsPayload.addAlarmListener([this](Inverter<> *iv) { mMqtt.alarmEvent(iv); }); - #endif } #endif setupLed(); @@ -123,11 +123,11 @@ void app::setup() { //----------------------------------------------------------------------------- void app::loop(void) { ah::Scheduler::loop(); + bool processPayload = false; if (mNrfRadio.loop() && mConfig->nrf.enabled) { while (!mNrfRadio.mBufCtrl.empty()) { packet_t *p = &mNrfRadio.mBufCtrl.front(); - if (mConfig->serial.debug) { DPRINT(DBG_INFO, F("RX ")); DBGPRINT(String(p->len)); @@ -142,47 +142,48 @@ void app::loop(void) { Inverter<> *iv = mSys.findInverter(&p->packet[1]); if (NULL != iv) { - if (IV_HM == iv->ivGen) - mPayload.add(iv, p); - else + if (IV_MI == iv->ivGen) mMiPayload.add(iv, p); + else + mPayload.add(iv, p); } mNrfRadio.mBufCtrl.pop(); yield(); } - mPayload.process(true); + processPayload = true; mMiPayload.process(true); } #if defined(ESP32) if (mCmtRadio.loop() && mConfig->cmt.enabled) { while (!mCmtRadio.mBufCtrl.empty()) { - hmsPacket_t *p = &mCmtRadio.mBufCtrl.front(); + packet_t *p = &mCmtRadio.mBufCtrl.front(); if (mConfig->serial.debug) { DPRINT(DBG_INFO, F("RX ")); - DBGPRINT(String(p->data[0])); + DBGPRINT(String(p->len)); DBGPRINT(F(", ")); DBGPRINT(String(p->rssi)); DBGPRINT(F("dBm | ")); - ah::dumpBuf(&p->data[1], p->data[0]); + ah::dumpBuf(p->packet, p->len); } mCmtStat.frmCnt++; - Inverter<> *iv = mSys.findInverter(&p->data[2]); + Inverter<> *iv = mSys.findInverter(&p->packet[1]); if(NULL != iv) { if((iv->ivGen == IV_HMS) || (iv->ivGen == IV_HMT)) - mHmsPayload.add(iv, p); + mPayload.add(iv, p); } mCmtRadio.mBufCtrl.pop(); yield(); } - mHmsPayload.process(false); //true + processPayload = true; } #endif + + if(processPayload) + mPayload.process(true); + mPayload.loop(); mMiPayload.loop(); - #if defined(ESP32) - mHmsPayload.loop(); - #endif if (mMqttEnabled && mNetworkConnected) mMqtt.loop(); @@ -195,10 +196,6 @@ void app::onNetwork(bool gotIp) { ah::Scheduler::resetTicker(); regularTickers(); //reinstall regular tickers every(std::bind(&app::tickSend, this), mConfig->nrf.sendInterval, "tSend"); - #if defined(ESP32) - if(mConfig->cmt.enabled) - everySec(std::bind(&CmtRadioType::tickSecond, &mCmtRadio), "tsCmt"); - #endif mMqttReconnect = true; mSunrise = 0; // needs to be set to 0, to reinstall sunrise and ivComm tickers! //once(std::bind(&app::tickNtpUpdate, this), 2, "ntp2"); @@ -439,18 +436,10 @@ void app::tickSend(void) { if (NULL != iv) { if (iv->config->enabled) { - if(mConfig->nrf.enabled) { - if (iv->ivGen == IV_HM) - mPayload.ivSend(iv); - else if(iv->ivGen == IV_MI) - mMiPayload.ivSend(iv); - } - #if defined(ESP32) - if(mConfig->cmt.enabled) { - if((iv->ivGen == IV_HMS) || (iv->ivGen == IV_HMT)) - mHmsPayload.ivSend(iv); - } - #endif + if((iv->ivGen == IV_MI) && mConfig->nrf.enabled) + mMiPayload.ivSend(iv); + else + mPayload.ivSend(iv); } #if defined(ESP32) diff --git a/src/app.h b/src/app.h index 89af4bf9..e3cba12c 100644 --- a/src/app.h +++ b/src/app.h @@ -16,7 +16,6 @@ #include "hm/hmSystem.h" #include "hm/hmRadio.h" #include "hms/hmsRadio.h" -#include "hms/hmsPayload.h" #include "hm/hmPayload.h" #include "hm/miPayload.h" #include "publisher/pubMqtt.h" @@ -42,11 +41,10 @@ #define ACOS(x) (degrees(acos(x))) typedef HmSystem HmSystemType; -typedef HmPayload> PayloadType; -typedef MiPayload> MiPayloadType; +typedef HmPayload PayloadType; +typedef MiPayload MiPayloadType; #ifdef ESP32 typedef CmtRadio CmtRadioType; -typedef HmsPayload HmsPayloadType; #endif typedef Web WebType; typedef RestApi RestApiType; @@ -184,14 +182,10 @@ class app : public IApp, public ah::Scheduler { void ivSendHighPrio(Inverter<> *iv) { if(mIVCommunicationOn) { // only send commands if communication is enabled - if (iv->ivGen == IV_HM) - mPayload.ivSendHighPrio(iv); - else if (iv->ivGen == IV_MI) + if (iv->ivGen == IV_MI) mMiPayload.ivSendHighPrio(iv); - #if defined(ESP32) - else if((iv->ivGen == IV_HMS) || (iv->ivGen == IV_HMT)) - mHmsPayload.ivSendHighPrio(iv); - #endif + else + mPayload.ivSendHighPrio(iv); } } @@ -342,7 +336,6 @@ class app : public IApp, public ah::Scheduler { #endif #ifdef ESP32 CmtRadioType mCmtRadio; - HmsPayloadType mHmsPayload; #endif char mVersion[12]; diff --git a/src/defines.h b/src/defines.h index ed2bba60..fa2069cf 100644 --- a/src/defines.h +++ b/src/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 7 -#define VERSION_PATCH 60 +#define VERSION_PATCH 61 //------------------------------------- typedef struct { diff --git a/src/hm/hmInverter.h b/src/hm/hmInverter.h index 54355da5..17ad6cc5 100644 --- a/src/hm/hmInverter.h +++ b/src/hm/hmInverter.h @@ -17,6 +17,7 @@ #include #include "../config/settings.h" +#include "radio.h" /** * For values which are of interest and not transmitted by the inverter can be * calculated automatically. @@ -24,10 +25,6 @@ * automatically. Their result does not differ from original read values. */ -// forward declaration of class -template -class Inverter; - // prototypes template @@ -88,7 +85,7 @@ class CommandAbstract { }; virtual ~CommandAbstract() {}; - const uint8_t getCmd() { + uint8_t getCmd() const { return _Cmd; } @@ -142,7 +139,7 @@ class Inverter { uint8_t channels; // number of PV channels (1-4) record_t recordMeas; // structure for measured values record_t recordInfo; // structure for info values - record_t recordHwInfo; // structure for simple (hardware) info values + record_t recordHwInfo; // structure for simple (hardware) info values record_t recordConfig; // structure for system config values record_t recordAlarm; // structure for alarm values bool initialized; // needed to check if the inverter was correctly added (ESP32 specific - union types are never null) @@ -152,7 +149,9 @@ class Inverter { uint8_t alarmNxtWrPos; // indicates the position in array (rolling buffer) uint16_t alarmCnt; // counts the total number of occurred alarms uint16_t alarmLastId; // lastId which was received - int8_t rssi; // HMS and HMT inverters only + int8_t rssi; // RSSI + Radio *radio; // pointer to associated radio class + statistics_t radioStatistics; // information about transmitted, failed, ... packets static uint32_t *timestamp; // system timestamp @@ -167,7 +166,6 @@ class Inverter { mDevControlRequest = false; devControlCmd = InitDataState; initialized = false; - //lastAlarmMsg = "nothing"; alarmMesIndex = 0; isConnected = false; status = InverterStatus::OFF; @@ -362,9 +360,6 @@ class Inverter { } else if (rec->assign == AlarmDataAssignment) { DPRINTLN(DBG_DEBUG, "add alarm"); - //if (getPosByChFld(0, FLD_LAST_ALARM_CODE, rec) == pos){ - // lastAlarmMsg = getAlarmStr(rec->record[pos]); - //} } else DPRINTLN(DBG_WARN, F("add with unknown assignment")); @@ -497,8 +492,8 @@ class Inverter { record_t<> *getRecordStruct(uint8_t cmd) { switch (cmd) { case RealTimeRunData_Debug: return &recordMeas; // 11 = 0x0b - case InverterDevInform_Simple: return &recordHwInfo; // 0 = 0x00 - case InverterDevInform_All: return &recordInfo; // 1 = 0x01 + case InverterDevInform_Simple: return &recordHwInfo; // 0 = 0x00 + case InverterDevInform_All: return &recordInfo; // 1 = 0x01 case SystemConfigPara: return &recordConfig; // 5 = 0x05 case AlarmData: return &recordAlarm; // 17 = 0x11 default: break; diff --git a/src/hm/hmPayload.h b/src/hm/hmPayload.h index 8693cb00..1f6e0468 100644 --- a/src/hm/hmPayload.h +++ b/src/hm/hmPayload.h @@ -1,6 +1,6 @@ //----------------------------------------------------------------------------- // 2023 Ahoy, https://ahoydtu.de -// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ +// Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed //----------------------------------------------------------------------------- #ifndef __HM_PAYLOAD_H__ @@ -10,12 +10,16 @@ #include "../utils/crc.h" #include "../config/config.h" #include "hmRadio.h" +#if defined(ESP32) +#include "../hms/cmt2300a.h" +#endif #include +#define HMS_TIMEOUT_SEC 30 + typedef struct { uint8_t txCmd; uint8_t txId; - uint8_t invId; uint32_t ts; uint8_t data[MAX_PAYLOAD_ENTRIES][MAX_RF_PAYLOAD_SIZE]; int8_t rssi[MAX_PAYLOAD_ENTRIES]; @@ -34,20 +38,19 @@ typedef std::function *)> payloadListenerType; typedef std::function *)> alarmListenerType; -template +template class HmPayload { public: HmPayload() {} - void setup(IApp *app, HMSYSTEM *sys, HMRADIO *radio, statistics_t *stat, uint8_t maxRetransmits, uint32_t *timestamp) { + void setup(IApp *app, HMSYSTEM *sys, uint8_t maxRetransmits, uint32_t *timestamp) { mApp = app; mSys = sys; - mRadio = radio; - mStat = stat; mMaxRetrans = maxRetransmits; mTimestamp = timestamp; for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { reset(i); + mIvCmd56Cnt[i] = 0; } mSerialDebug = false; mHighPrioIv = NULL; @@ -69,7 +72,7 @@ class HmPayload { void loop() { if (NULL != mHighPrioIv) { - ivSend(mHighPrioIv, true); + ivSend(mHighPrioIv, true); // for e.g. devcontrol commands mHighPrioIv = NULL; } } @@ -111,11 +114,11 @@ class HmPayload { if (mSerialDebug) DPRINT_IVID(DBG_INFO, iv->id); if (MAX_PAYLOAD_ENTRIES == mPayload[iv->id].maxPackId) { - mStat->rxFailNoAnser++; // got nothing + iv->radioStatistics.rxFailNoAnser++; // got nothing if (mSerialDebug) DBGPRINTLN(F("enqueued cmd failed/timeout")); } else { - mStat->rxFail++; // got fragments but not complete response + iv->radioStatistics.rxFail++; // got fragments but not complete response if (mSerialDebug) { DBGPRINT(F("no complete Payload received! (retransmits: ")); DBGPRINT(String(mPayload[iv->id].retransmits)); @@ -141,23 +144,34 @@ class HmPayload { if (mSerialDebug) { DPRINT_IVID(DBG_INFO, iv->id); DBGPRINT(F("Devcontrol request 0x")); - DBGPRINT(String(iv->devControlCmd, HEX)); + DHEX(iv->devControlCmd); DBGPRINT(F(" power limit ")); DBGPRINTLN(String(iv->powerLimit[0])); } iv->powerLimitAck = false; - mRadio->sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit, false); + iv->radio->sendControlPacket(iv, iv->devControlCmd, iv->powerLimit, false); mPayload[iv->id].txCmd = iv->devControlCmd; //iv->clearCmdQueue(); //iv->enqueCommand(SystemConfigPara); // read back power limit } else { + #if defined(ESP32) + if((IV_HMS == iv->ivGen) || (IV_HMT == iv->ivGen)) { + record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); + if(((rec->ts + HMS_TIMEOUT_SEC) < *mTimestamp) && (mIvCmd56Cnt[iv->id] < 3)) { + iv->radio->switchFrequency(iv, HOY_BOOT_FREQ_KHZ, WORK_FREQ_KHZ); + mIvCmd56Cnt[iv->id]++; + return; + } else if(++mIvCmd56Cnt[iv->id] == 10) + mIvCmd56Cnt[iv->id] = 0; + } + #endif uint8_t cmd = iv->getQueuedCmd(); if (mSerialDebug) { DPRINT_IVID(DBG_INFO, iv->id); DBGPRINT(F("prepareDevInformCmd 0x")); DBGHEXLN(cmd); } - mRadio->prepareDevInformCmd(iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmLastId, false); + iv->radio->prepareDevInformCmd(iv, cmd, mPayload[iv->id].ts, iv->alarmLastId, false); mPayload[iv->id].txCmd = cmd; } } @@ -227,9 +241,6 @@ class HmPayload { if (NULL == iv) continue; // skip to next inverter - if (IV_HM != iv->ivGen) // only process HM inverters - 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 mPayload[iv->id].complete = true; @@ -251,7 +262,7 @@ class HmPayload { } else if(iv->devControlCmd == ActivePowerContr) { DPRINT_IVID(DBG_INFO, iv->id); DPRINTLN(DBG_INFO, F("retransmit power limit")); - mRadio->sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit, true); + iv->radio->sendControlPacket(iv, iv->devControlCmd, iv->powerLimit, true); } else { if(false == mPayload[iv->id].gotFragment) { DPRINT_IVID(DBG_WARN, iv->id); @@ -262,7 +273,7 @@ class HmPayload { DBGPRINTLN(F("nothing received: complete retransmit")); mPayload[iv->id].txCmd = iv->getQueuedCmd(); DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") prepareDevInformCmd 0x") + String(mPayload[iv->id].txCmd, HEX)); - mRadio->prepareDevInformCmd(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex, true); + iv->radio->prepareDevInformCmd(iv, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex, true); } } else { for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId - 1); i++) { @@ -273,7 +284,7 @@ class HmPayload { DBGPRINT(String(i + 1)); DBGPRINTLN(F(" missing: Request Retransmit")); } - mRadio->sendCmdPacket(iv->radioId.u64, TX_REQ_INFO, (SINGLE_FRAME + i), true); + iv->radio->sendCmdPacket(iv, TX_REQ_INFO, (SINGLE_FRAME + i), true); break; // only request retransmit one frame per loop } yield(); @@ -290,12 +301,13 @@ class HmPayload { mPayload[iv->id].retransmits++; mPayload[iv->id].txCmd = iv->getQueuedCmd(); if (mSerialDebug) { - DPRINTLN(DBG_WARN, F("CRC Error: Request Complete Retransmit")); + DPRINT_IVID(DBG_WARN, iv->id); + DBGPRINTLN(F("CRC Error: Request Complete Retransmit")); DPRINT_IVID(DBG_INFO, iv->id); DBGPRINT(F("prepareDevInformCmd 0x")); DBGHEXLN(mPayload[iv->id].txCmd); } - mRadio->prepareDevInformCmd(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmLastId, true); + iv->radio->prepareDevInformCmd(iv, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmLastId, true); } } else { // payload complete if (mSerialDebug) { @@ -345,7 +357,7 @@ class HmPayload { DPRINTLN(DBG_ERROR, F("record is NULL!")); } else if ((rec->pyldLen == payloadLen) || (0 == rec->pyldLen)) { if (mPayload[iv->id].txId == (TX_REQ_INFO + ALL_FRAMES)) - mStat->rxSuccess++; + iv->radioStatistics.rxSuccess++; rec->ts = mPayload[iv->id].ts; for (uint8_t i = 0; i < rec->length; i++) { @@ -375,8 +387,8 @@ class HmPayload { DBGPRINT(F("prepareDevInformCmd 0x")); DBGHEXLN(cmd); } - mStat->rxSuccess++; - mRadio->prepareDevInformCmd(iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmLastId, false); + iv->radioStatistics.rxSuccess++; + iv->radio->prepareDevInformCmd(iv, cmd, mPayload[iv->id].ts, iv->alarmLastId, false); mPayload[iv->id].txCmd = cmd; */ mHighPrioIv = iv; @@ -388,7 +400,7 @@ class HmPayload { DBGPRINT(String(rec->pyldLen)); DBGPRINTLN(F(" bytes")); } - mStat->rxFail++; + iv->radioStatistics.rxFail++; } iv->setQueuedCmdFinished(); @@ -461,11 +473,10 @@ class HmPayload { IApp *mApp; HMSYSTEM *mSys; - HMRADIO *mRadio; - statistics_t *mStat; uint8_t mMaxRetrans; uint32_t *mTimestamp; invPayload_t mPayload[MAX_NUM_INVERTERS]; + uint8_t mIvCmd56Cnt[MAX_NUM_INVERTERS]; bool mSerialDebug; Inverter<> *mHighPrioIv; diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h index 870ef457..a808bd88 100644 --- a/src/hm/hmRadio.h +++ b/src/hm/hmRadio.h @@ -3,32 +3,25 @@ // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/4.0/deed //----------------------------------------------------------------------------- -#ifndef __RADIO_H__ -#define __RADIO_H__ +#ifndef __HM_RADIO_H__ +#define __HM_RADIO_H__ -#include "../utils/dbg.h" #include -#include "../utils/crc.h" -#include "../config/config.h" #include "SPI.h" +#include "radio.h" #define SPI_SPEED 1000000 #define RF_CHANNELS 5 -#define TX_REQ_INFO 0x15 -#define TX_REQ_DEVCONTROL 0x51 -#define ALL_FRAMES 0x80 -#define SINGLE_FRAME 0x81 - const char* const rf24AmpPowerNames[] = {"MIN", "LOW", "HIGH", "MAX"}; //----------------------------------------------------------------------------- // HM Radio class //----------------------------------------------------------------------------- -template -class HmRadio { +template +class HmRadio : public Radio { public: HmRadio() : mNrf24(CE_PIN, CS_PIN, SPI_SPEED) { if(mSerialDebug) { @@ -40,6 +33,7 @@ class HmRadio { DBGPRINT(String(SPI_SPEED)); DBGPRINTLN(F(")")); } + mDtuSn = DTU_SN; // Depending on the program, the module can work on 2403, 2423, 2440, 2461 or 2475MHz. // Channel List 2403, 2423, 2440, 2461, 2475MHz @@ -58,28 +52,11 @@ class HmRadio { } ~HmRadio() {} - void setup(statistics_t *stat, uint8_t ampPwr = RF24_PA_LOW, uint8_t irq = IRQ_PIN, uint8_t ce = CE_PIN, uint8_t cs = CS_PIN, uint8_t sclk = SCLK_PIN, uint8_t mosi = MOSI_PIN, uint8_t miso = MISO_PIN) { + void setup(uint8_t ampPwr = RF24_PA_LOW, uint8_t irq = IRQ_PIN, uint8_t ce = CE_PIN, uint8_t cs = CS_PIN, uint8_t sclk = SCLK_PIN, uint8_t mosi = MOSI_PIN, uint8_t miso = MISO_PIN) { DPRINTLN(DBG_VERBOSE, F("hmRadio.h:setup")); pinMode(irq, INPUT_PULLUP); - mStat = stat; - uint32_t dtuSn = 0x87654321; - uint32_t chipID = 0; // will be filled with last 3 bytes of MAC - #ifdef ESP32 - uint64_t MAC = ESP.getEfuseMac(); - chipID = ((MAC >> 8) & 0xFF0000) | ((MAC >> 24) & 0xFF00) | ((MAC >> 40) & 0xFF); - #else - chipID = ESP.getChipId(); - #endif - if(chipID) { - dtuSn = 0x80000000; // the first digit is an 8 for DTU production year 2022, the rest is filled with the ESP chipID in decimal - for(int i = 0; i < 7; i++) { - dtuSn |= (chipID % 10) << (i * 4); - chipID /= 10; - } - } - // change the byte order of the DTU serial number and append the required 0x01 at the end - DTU_RADIO_ID = ((uint64_t)(((dtuSn >> 24) & 0xFF) | ((dtuSn >> 8) & 0xFF00) | ((dtuSn << 8) & 0xFF0000) | ((dtuSn << 24) & 0xFF000000)) << 8) | 0x01; + generateDtuSn(); #ifdef ESP32 #if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 @@ -104,7 +81,7 @@ class HmRadio { mNrf24.enableDynamicPayloads(); mNrf24.setCRCLength(RF24_CRC_16); mNrf24.setAddressWidth(5); - mNrf24.openReadingPipe(1, reinterpret_cast(&DTU_RADIO_ID)); + mNrf24.openReadingPipe(1, reinterpret_cast(&mDtuSn)); // enable all receiving interrupts mNrf24.maskIRQ(false, false, false); @@ -117,7 +94,7 @@ class HmRadio { DPRINTLN(DBG_INFO, F("Radio Config:")); mNrf24.printPrettyDetails(); DPRINT(DBG_INFO, F("DTU_SN: 0x")); - DBGPRINTLN(String(DTU_RADIO_ID, HEX)); + DBGPRINTLN(String(mDtuSn, HEX)); } else DPRINTLN(DBG_WARN, F("WARNING! your NRF24 module can't be reached, check the wiring")); } @@ -157,22 +134,15 @@ class HmRadio { return true; } - void handleIntr(void) { - mIrqRcvd = true; - } - bool isChipConnected(void) { //DPRINTLN(DBG_VERBOSE, F("hmRadio.h:isChipConnected")); return mNrf24.isChipConnected(); } - void enableDebug() { - mSerialDebug = true; - } - void sendControlPacket(uint64_t invId, uint8_t cmd, uint16_t *data, bool isRetransmit, bool isNoMI = true, bool is4chMI = false) { + void sendControlPacket(Inverter<> *iv, uint8_t cmd, uint16_t *data, bool isRetransmit, bool isNoMI = true, bool is4chMI = false) { DPRINT(DBG_INFO, F("sendControlPacket cmd: 0x")); DBGHEXLN(cmd); - initPacket(invId, TX_REQ_DEVCONTROL, SINGLE_FRAME); + initPacket(iv->radioId.u64, TX_REQ_DEVCONTROL, SINGLE_FRAME); uint8_t cnt = 10; if (isNoMI) { mTxBuf[cnt++] = cmd; // cmd -> 0 on, 1 off, 2 restart, 11 active power, 12 reactive power, 13 power factor @@ -216,28 +186,7 @@ class HmRadio { } cnt++; } - sendPacket(invId, cnt, isRetransmit, isNoMI); - } - - void prepareDevInformCmd(uint64_t invId, uint8_t cmd, uint32_t ts, uint16_t alarmMesId, bool isRetransmit, uint8_t reqfld=TX_REQ_INFO) { // might not be necessary to add additional arg. - if(mSerialDebug) { - DPRINT(DBG_DEBUG, F("prepareDevInformCmd 0x")); - DPRINTLN(DBG_DEBUG,String(cmd, HEX)); - } - initPacket(invId, reqfld, ALL_FRAMES); - mTxBuf[10] = cmd; // cid - mTxBuf[11] = 0x00; - CP_U32_LittleEndian(&mTxBuf[12], ts); - if (cmd == AlarmData ) { //cmd == RealTimeRunData_Debug || - mTxBuf[18] = (alarmMesId >> 8) & 0xff; - mTxBuf[19] = (alarmMesId ) & 0xff; - } - sendPacket(invId, 24, isRetransmit); - } - - void sendCmdPacket(uint64_t invId, uint8_t mid, uint8_t pid, bool isRetransmit, bool appendCrc16=true) { - initPacket(invId, mid, pid); - sendPacket(invId, 10, isRetransmit, appendCrc16); + sendPacket(iv, cnt, isRetransmit, isNoMI); } uint8_t getDataRate(void) { @@ -251,7 +200,6 @@ class HmRadio { } std::queue mBufCtrl; - bool mSerialDebug; private: bool getReceived(void) { @@ -283,34 +231,8 @@ class HmRadio { return isLastPackage; } - void initPacket(uint64_t invId, uint8_t mid, uint8_t pid) { - if(mSerialDebug) { - DPRINT(DBG_VERBOSE, F("initPacket, mid: ")); - DPRINT(DBG_VERBOSE, String(mid, HEX)); - DPRINT(DBG_VERBOSE,F(" pid: ")); - DPRINTLN(DBG_VERBOSE,String(pid, HEX)); - } - memset(mTxBuf, 0, MAX_RF_PAYLOAD_SIZE); - mTxBuf[0] = mid; // message id - CP_U32_BigEndian(&mTxBuf[1], (invId >> 8)); - CP_U32_BigEndian(&mTxBuf[5], (DTU_RADIO_ID >> 8)); - mTxBuf[9] = pid; - } - - void sendPacket(uint64_t invId, uint8_t len, bool isRetransmit, bool appendCrc16=true) { - //DPRINTLN(DBG_VERBOSE, F("hmRadio.h:sendPacket")); - //DPRINTLN(DBG_VERBOSE, "sent packet: #" + String(mStat->txCnt)); - - // append crc's - if (appendCrc16 && (len > 10)) { - // crc control data - uint16_t crc = ah::crc16(&mTxBuf[10], len - 10); - mTxBuf[len++] = (crc >> 8) & 0xff; - mTxBuf[len++] = (crc ) & 0xff; - } - // crc over all - mTxBuf[len] = ah::crc8(mTxBuf, len); - len++; + void sendPacket(Inverter<> *iv, uint8_t len, bool isRetransmit, bool appendCrc16=true) { + updateCrcs(len, appendCrc16); // set TX and RX channels mTxChIdx = (mTxChIdx + 1) % RF_CHANNELS; @@ -327,17 +249,18 @@ class HmRadio { mNrf24.stopListening(); mNrf24.setChannel(mRfChLst[mTxChIdx]); - mNrf24.openWritingPipe(reinterpret_cast(&invId)); + mNrf24.openWritingPipe(reinterpret_cast(&iv->radioId.u64)); mNrf24.startWrite(mTxBuf, len, false); // false = request ACK response if(isRetransmit) - mStat->retransmits++; + iv->radioStatistics.retransmits++; else - mStat->txCnt++; + iv->radioStatistics.txCnt++; } - volatile bool mIrqRcvd; - uint64_t DTU_RADIO_ID; + uint64_t getIvId(Inverter<> *iv) { + return iv->radioId.u64; + } uint8_t mRfChLst[RF_CHANNELS]; uint8_t mTxChIdx; @@ -345,8 +268,6 @@ class HmRadio { SPIClass* mSpi; RF24 mNrf24; - uint8_t mTxBuf[MAX_RF_PAYLOAD_SIZE]; - statistics_t *mStat; }; -#endif /*__RADIO_H__*/ +#endif /*__HM_RADIO_H__*/ diff --git a/src/hm/hmSystem.h b/src/hm/hmSystem.h index 25d6dd11..a8ca2371 100644 --- a/src/hm/hmSystem.h +++ b/src/hm/hmSystem.h @@ -7,70 +7,45 @@ #define __HM_SYSTEM_H__ #include "hmInverter.h" +#include template > class HmSystem { public: HmSystem() {} - void setup(uint32_t *timestamp) { + void setup(uint32_t *timestamp, cfgInst_t *config) { mInverter[0].timestamp = timestamp; - mNumInv = 0; - } - - void addInverters(cfgInst_t *config) { mInverter[0].generalConfig = config; - Inverter<> *iv; - for (uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { - iv = addInverter(&config->iv[i]); - if (0ULL != config->iv[i].serial.u64) { - if (NULL != iv) { - DPRINT(DBG_INFO, "added inverter "); - if(iv->config->serial.b[5] == 0x11) { - if((iv->config->serial.b[4] & 0x0f) == 0x04) - DBGPRINT("HMS"); - else - DBGPRINT("HM"); - } else if(iv->config->serial.b[5] == 0x13) - DBGPRINT("HMT"); - else - DBGPRINT(((iv->config->serial.b[4] & 0x03) == 0x01) ? " (2nd Gen) " : " (3rd Gen) "); - - DBGPRINTLN(String(iv->config->serial.u64, HEX)); - - if((iv->config->serial.b[5] == 0x10) && ((iv->config->serial.b[4] & 0x03) == 0x01)) - DPRINTLN(DBG_WARN, F("MI Inverter are not fully supported now!!!")); - } - } - } + mNumInv = 0; } - INVERTERTYPE *addInverter(cfgIv_t *config) { + void addInverter(uint8_t id, std::function *iv)> cb) { DPRINTLN(DBG_VERBOSE, F("hmSystem.h:addInverter")); if(MAX_INVERTER <= mNumInv) { DPRINT(DBG_WARN, F("max number of inverters reached!")); - return NULL; + return; } - INVERTERTYPE *p = &mInverter[mNumInv]; - p->id = mNumInv; - p->config = config; - DPRINT(DBG_VERBOSE, "SERIAL: " + String(p->config->serial.b[5], HEX)); - DPRINTLN(DBG_VERBOSE, " " + String(p->config->serial.b[4], HEX)); - if((p->config->serial.b[5] == 0x11) || (p->config->serial.b[5] == 0x10)) { - switch(p->config->serial.b[4]) { + INVERTERTYPE *iv = &mInverter[mNumInv]; + iv->id = mNumInv; + iv->config = &mInverter[0].generalConfig->iv[id]; + DPRINT(DBG_VERBOSE, "SERIAL: " + String(iv->config->serial.b[5], HEX)); + DPRINTLN(DBG_VERBOSE, " " + String(iv->config->serial.b[4], HEX)); + if((iv->config->serial.b[5] == 0x11) || (iv->config->serial.b[5] == 0x10)) { + switch(iv->config->serial.b[4]) { case 0x24: // HMS-500 case 0x22: - case 0x21: p->type = INV_TYPE_1CH; + case 0x21: iv->type = INV_TYPE_1CH; break; case 0x44: // HMS-1000 case 0x42: - case 0x41: p->type = INV_TYPE_2CH; + case 0x41: iv->type = INV_TYPE_2CH; break; case 0x64: // HMS-2000 case 0x62: - case 0x61: p->type = INV_TYPE_4CH; + case 0x61: iv->type = INV_TYPE_4CH; break; default: @@ -78,26 +53,47 @@ class HmSystem { break; } - if(p->config->serial.b[5] == 0x11) { - if((p->config->serial.b[4] & 0x0f) == 0x04) - p->ivGen = IV_HMS; + if(iv->config->serial.b[5] == 0x11) { + if((iv->config->serial.b[4] & 0x0f) == 0x04) + iv->ivGen = IV_HMS; else - p->ivGen = IV_HM; + iv->ivGen = IV_HM; } - else if((p->config->serial.b[4] & 0x03) == 0x02) // MI 3rd Gen -> same as HM - p->ivGen = IV_HM; + else if((iv->config->serial.b[4] & 0x03) == 0x02) // MI 3rd Gen -> same as HM + iv->ivGen = IV_HM; else // MI 2nd Gen - p->ivGen = IV_MI; - } else if(p->config->serial.b[5] == 0x13) { - p->ivGen = IV_HMT; - p->type = INV_TYPE_6CH; - } else if(p->config->serial.u64 != 0ULL) + iv->ivGen = IV_MI; + } else if(iv->config->serial.b[5] == 0x13) { + iv->ivGen = IV_HMT; + iv->type = INV_TYPE_6CH; + } else if(iv->config->serial.u64 != 0ULL) { DPRINTLN(DBG_ERROR, F("inverter type can't be detected!")); + return; + } else + iv->ivGen = IV_UNKNOWN; - p->init(); - + iv->init(); mNumInv ++; - return p; + if(IV_UNKNOWN == iv->ivGen) + return; // serial is 0 + + DPRINT(DBG_INFO, "added inverter "); + if(iv->config->serial.b[5] == 0x11) { + if((iv->config->serial.b[4] & 0x0f) == 0x04) + DBGPRINT("HMS"); + else + DBGPRINT("HM"); + } else if(iv->config->serial.b[5] == 0x13) + DBGPRINT("HMT"); + else + DBGPRINT(((iv->config->serial.b[4] & 0x03) == 0x01) ? " (2nd Gen) " : " (3rd Gen) "); + + DBGPRINTLN(String(iv->config->serial.u64, HEX)); + + if((iv->config->serial.b[5] == 0x10) && ((iv->config->serial.b[4] & 0x03) == 0x01)) + DPRINTLN(DBG_WARN, F("MI Inverter are not fully supported now!!!")); + + cb(iv); } INVERTERTYPE *findInverter(uint8_t buf[]) { diff --git a/src/hm/miPayload.h b/src/hm/miPayload.h index 716ea019..6273d571 100644 --- a/src/hm/miPayload.h +++ b/src/hm/miPayload.h @@ -1,12 +1,10 @@ //----------------------------------------------------------------------------- // 2023 Ahoy, https://ahoydtu.de -// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ +// Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed //----------------------------------------------------------------------------- #ifndef __MI_PAYLOAD_H__ #define __MI_PAYLOAD_H__ - -//#include "hmInverter.h" #include "../utils/dbg.h" #include "../utils/crc.h" #include "../config/config.h" @@ -35,16 +33,14 @@ typedef struct { typedef std::function *)> miPayloadListenerType; -template +template class MiPayload { public: MiPayload() {} - void setup(IApp *app, HMSYSTEM *sys, HMRADIO *radio, statistics_t *stat, uint8_t maxRetransmits, uint32_t *timestamp) { + void setup(IApp *app, HMSYSTEM *sys, uint8_t maxRetransmits, uint32_t *timestamp) { mApp = app; mSys = sys; - mRadio = radio; - mStat = stat; mMaxRetrans = maxRetransmits; mTimestamp = timestamp; for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { @@ -53,7 +49,7 @@ class MiPayload { } mSerialDebug = false; mHighPrioIv = NULL; - mCbMiPayload = NULL; + mCbPayload = NULL; } void enableSerialDebug(bool enable) { @@ -61,7 +57,7 @@ class MiPayload { } void addPayloadListener(miPayloadListenerType cb) { - mCbMiPayload = cb; + mCbPayload = cb; } void addAlarmListener(alarmListenerType cb) { @@ -89,11 +85,11 @@ class MiPayload { if (mSerialDebug) DPRINT_IVID(DBG_INFO, iv->id); if (!mPayload[iv->id].gotFragment) { - mStat->rxFailNoAnser++; // got nothing + iv->radioStatistics.rxFailNoAnser++; // got nothing if (mSerialDebug) DBGPRINTLN(F("enqueued cmd failed/timeout")); } else { - mStat->rxFail++; // got "fragments" (part of the required messages) + iv->radioStatistics.rxFail++; // got "fragments" (part of the required messages) // but no complete set of responses if (mSerialDebug) { DBGPRINT(F("no complete Payload received! (retransmits: ")); @@ -112,7 +108,7 @@ class MiPayload { mPayload[iv->id].requested = true; yield(); - if (mSerialDebug){ + if (mSerialDebug) { DPRINT_IVID(DBG_INFO, iv->id); DBGPRINT(F("Requesting Inv SN ")); DBGPRINTLN(String(iv->config->serial.u64, HEX)); @@ -127,7 +123,7 @@ class MiPayload { DBGPRINTLN(String(iv->powerLimit[0])); } iv->powerLimitAck = false; - mRadio->sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit, false, false, iv->type == INV_TYPE_4CH); + iv->radio->sendControlPacket(iv, iv->devControlCmd, iv->powerLimit, false, false, iv->type == INV_TYPE_4CH); mPayload[iv->id].txCmd = iv->devControlCmd; mPayload[iv->id].limitrequested = true; @@ -153,7 +149,7 @@ class MiPayload { DBGPRINT(F("prepareDevInformCmd 0x")); DBGHEXLN(cmd); } - mRadio->sendCmdPacket(iv->radioId.u64, cmd, cmd2, false, false); + iv->radio->sendCmdPacket(iv, cmd, cmd2, false, false); mPayload[iv->id].txCmd = cmd; if (iv->type == INV_TYPE_1CH || iv->type == INV_TYPE_2CH) { @@ -262,7 +258,7 @@ class MiPayload { DPRINTLN(DBG_ERROR, F("record is NULL!")); } else if ((rec->pyldLen == payloadLen) || (0 == rec->pyldLen)) { if (mPayload[iv->id].txId == (TX_REQ_INFO + ALL_FRAMES)) - mStat->rxSuccess++; + iv->radioStatistics.rxSuccess++; rec->ts = mPayload[iv->id].ts; for (uint8_t i = 0; i < rec->length; i++) { @@ -284,7 +280,7 @@ class MiPayload { } } else { DPRINTLN(DBG_ERROR, F("plausibility check failed, expected ") + String(rec->pyldLen) + F(" bytes")); - mStat->rxFail++; + iv->radioStatistics.rxFail++; } iv->setQueuedCmdFinished(); @@ -330,7 +326,7 @@ class MiPayload { } else if(iv->devControlCmd == ActivePowerContr) { DPRINT_IVID(DBG_INFO, iv->id); DBGPRINTLN(F("retransmit power limit")); - mRadio->sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit, true, false); + iv->radio->sendControlPacket(iv, iv->devControlCmd, iv->powerLimit, true, false); } else { uint8_t cmd = mPayload[iv->id].txCmd; if (mPayload[iv->id].retransmits < mMaxRetrans) { @@ -343,10 +339,10 @@ class MiPayload { DPRINT_IVID(DBG_INFO, iv->id); DBGPRINTLN(F("retransmit on failed first request")); mPayload[iv->id].rxTmo = true; - mRadio->sendCmdPacket(iv->radioId.u64, cmd, cmd, true, false); + iv->radio->sendCmdPacket(iv, cmd, cmd, true, false); } else if ( cmd == 0x0f ) { //hard/firmware request - mRadio->sendCmdPacket(iv->radioId.u64, 0x0f, 0x00, true, false); + iv->radio->sendCmdPacket(iv, 0x0f, 0x00, true, false); mPayload[id].multi_parts = 0; } else { bool change = false; @@ -384,7 +380,7 @@ class MiPayload { DBGPRINT(F(" 0x")); DBGHEXLN(cmd); mPayload[id].multi_parts = 0; - mRadio->sendCmdPacket(iv->radioId.u64, cmd, cmd, true, false); + iv->radio->sendCmdPacket(iv, cmd, cmd, true, false); yield(); } } else { @@ -404,7 +400,7 @@ class MiPayload { DBGPRINT(F("prepareDevInformCmd 0x")); DBGHEXLN(mPayload[iv->id].txCmd); } - mRadio->sendCmdPacket(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].txCmd, false, false); + iv->radio->sendCmdPacket(iv, mPayload[iv->id].txCmd, mPayload[iv->id].txCmd, false, false); } else { mPayload[iv->id].rxTmo = true; } @@ -420,9 +416,9 @@ class MiPayload { DBGPRINT(F("prepareDevInformCmd 0x")); DBGHEXLN(cmd); } - mStat->rxSuccess++; - //mRadio->prepareDevInformCmd(iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmMesIndex, false); - mRadio->prepareDevInformCmd(iv->radioId.u64, iv->getType(), + iv->radioStatistics.rxSuccess++; + //iv->radio->prepareDevInformCmd(iv, cmd, mPayload[iv->id].ts, iv->alarmMesIndex, false); + iv->radio->prepareDevInformCmd(iv, iv->getType(), iv->getNextTxChanIndex(), cmd, mPayload[iv->id].ts, iv->alarmMesIndex, false); mPayload[iv->id].txCmd = cmd; */ if (mHighPrioIv == NULL) @@ -438,8 +434,8 @@ class MiPayload { private: void notify(uint8_t val, Inverter<> *iv) { - if(NULL != mCbMiPayload) - (mCbMiPayload)(val, iv); + if(NULL != mCbPayload) + (mCbPayload)(val, iv); } void miStsDecode(Inverter<> *iv, packet_t *p, uint8_t stschan = CH1) { @@ -510,14 +506,11 @@ class MiPayload { } /*if(AlarmData == mPayload[iv->id].txCmd) { uint8_t i = 0; - uint16_t code; - uint32_t start, end; while(1) { - code = iv->parseAlarmLog(i++, payload, payloadLen, &start, &end); - if(0 == code) + if(0 == iv->parseAlarmLog(i++, payload, payloadLen)) break; if (NULL != mCbAlarm) - (mCbAlarm)(code, start, end); + (mCbAlarm)(iv); yield(); } }*/ @@ -600,7 +593,7 @@ class MiPayload { iv->isProducing(); iv->setQueuedCmdFinished(); - mStat->rxSuccess++; + iv->radioStatistics.rxSuccess++; yield(); notify(RealTimeRunData_Debug, iv); } @@ -756,7 +749,7 @@ const byteAssign_t InfoAssignment[] = { mPayload[iv->id].complete = true; mPayload[iv->id].rxTmo = true; mPayload[iv->id].requested= false; - mStat->rxSuccess++; + iv->radioStatistics.rxSuccess++; } } @@ -789,16 +782,14 @@ const byteAssign_t InfoAssignment[] = { IApp *mApp; HMSYSTEM *mSys; - HMRADIO *mRadio; - statistics_t *mStat; uint8_t mMaxRetrans; uint32_t *mTimestamp; miPayload_t mPayload[MAX_NUM_INVERTERS]; bool mSerialDebug; - Inverter<> *mHighPrioIv; + alarmListenerType mCbAlarm; - payloadListenerType mCbMiPayload; + payloadListenerType mCbPayload; }; #endif /*__MI_PAYLOAD_H__*/ diff --git a/src/hm/radio.h b/src/hm/radio.h new file mode 100644 index 00000000..e9b1e47b --- /dev/null +++ b/src/hm/radio.h @@ -0,0 +1,102 @@ +//----------------------------------------------------------------------------- +// 2023 Ahoy, https://github.com/lumpapu/ahoy +// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/4.0/deed +//----------------------------------------------------------------------------- + +#ifndef __RADIO_H__ +#define __RADIO_H__ + +#define TX_REQ_INFO 0x15 +#define TX_REQ_DEVCONTROL 0x51 +#define ALL_FRAMES 0x80 +#define SINGLE_FRAME 0x81 + +#include "../utils/dbg.h" +#include "../utils/crc.h" + +// forward declaration of class +template +class Inverter; + +// abstract radio interface +class Radio { + public: + virtual void sendControlPacket(Inverter<> *iv, uint8_t cmd, uint16_t *data, bool isRetransmit, bool isNoMI = true, bool is4chMI = false) = 0; + virtual bool switchFrequency(Inverter<> *iv, uint32_t fromkHz, uint32_t tokHz) { return true; } + + void handleIntr(void) { + mIrqRcvd = true; + } + + void enableDebug() { + mSerialDebug = true; + } + + void sendCmdPacket(Inverter<> *iv, uint8_t mid, uint8_t pid, bool isRetransmit, bool appendCrc16=true) { + initPacket(getIvId(iv), mid, pid); + sendPacket(iv, 10, isRetransmit, appendCrc16); + } + + void prepareDevInformCmd(Inverter<> *iv, uint8_t cmd, uint32_t ts, uint16_t alarmMesId, bool isRetransmit, uint8_t reqfld=TX_REQ_INFO) { // might not be necessary to add additional arg. + if(mSerialDebug) { + DPRINT(DBG_DEBUG, F("prepareDevInformCmd 0x")); + DPRINTLN(DBG_DEBUG,String(cmd, HEX)); + } + initPacket(getIvId(iv), reqfld, ALL_FRAMES); + mTxBuf[10] = cmd; + CP_U32_LittleEndian(&mTxBuf[12], ts); + if (cmd == AlarmData ) { //cmd == RealTimeRunData_Debug || + mTxBuf[18] = (alarmMesId >> 8) & 0xff; + mTxBuf[19] = (alarmMesId ) & 0xff; + } + sendPacket(iv, 24, isRetransmit); + } + + protected: + virtual void sendPacket(Inverter<> *iv, uint8_t len, bool isRetransmit, bool appendCrc16=true) = 0; + virtual uint64_t getIvId(Inverter<> *iv) = 0; + + void initPacket(uint64_t ivId, uint8_t mid, uint8_t pid) { + mTxBuf[0] = mid; + CP_U32_BigEndian(&mTxBuf[1], ivId >> 8); + CP_U32_LittleEndian(&mTxBuf[5], mDtuSn); + mTxBuf[9] = pid; + memset(&mTxBuf[10], 0x00, (MAX_RF_PAYLOAD_SIZE-10)); + } + + void updateCrcs(uint8_t len, bool appendCrc16=true) { + // append crc's + if (appendCrc16 && (len > 10)) { + // crc control data + uint16_t crc = ah::crc16(&mTxBuf[10], len - 10); + mTxBuf[len++] = (crc >> 8) & 0xff; + mTxBuf[len++] = (crc ) & 0xff; + } + // crc over all + mTxBuf[len] = ah::crc8(mTxBuf, len); + len++; + } + + void generateDtuSn(void) { + uint32_t chipID = 0; + #ifdef ESP32 + uint64_t MAC = ESP.getEfuseMac(); + chipID = ((MAC >> 8) & 0xFF0000) | ((MAC >> 24) & 0xFF00) | ((MAC >> 40) & 0xFF); + #else + chipID = ESP.getChipId(); + #endif + mDtuSn = 0x80000000; // the first digit is an 8 for DTU production year 2022, the rest is filled with the ESP chipID in decimal + for(int i = 0; i < 7; i++) { + mDtuSn |= (chipID % 10) << (i * 4); + chipID /= 10; + } + } + + uint32_t mDtuSn; + volatile bool mIrqRcvd; + bool mSerialDebug; + uint8_t mTxBuf[MAX_RF_PAYLOAD_SIZE]; + +}; + +#endif /*__RADIO_H__*/ diff --git a/src/hms/cmt2300a.h b/src/hms/cmt2300a.h index d99875b9..a4308519 100644 --- a/src/hms/cmt2300a.h +++ b/src/hms/cmt2300a.h @@ -239,7 +239,7 @@ class Cmt2300a { return CMT_SUCCESS; } - uint8_t getRx(uint8_t buf[], uint8_t len, int8_t *rssi) { + uint8_t getRx(uint8_t buf[], uint8_t *rxLen, uint8_t maxlen, int8_t *rssi) { if(mTxPending) return CMT_ERR_TX_PENDING; @@ -250,7 +250,7 @@ class Cmt2300a { if(!cmtSwitchStatus(CMT2300A_GO_STBY, CMT2300A_STA_STBY)) return CMT_ERR_SWITCH_STATE; - mSpi.readFifo(buf, len); + mSpi.readFifo(buf, rxLen, maxlen); *rssi = mSpi.readReg(CMT2300A_CUS_RSSI_DBM) - 128; if(!cmtSwitchStatus(CMT2300A_GO_SLEEP, CMT2300A_STA_SLEEP)) diff --git a/src/hms/esp32_3wSpi.h b/src/hms/esp32_3wSpi.h index 4fe47497..e1c4114b 100644 --- a/src/hms/esp32_3wSpi.h +++ b/src/hms/esp32_3wSpi.h @@ -141,7 +141,7 @@ class esp32_3wSpi { SPI_PARAM_UNLOCK(); } - void readFifo(uint8_t buf[], uint8_t len) { + void readFifo(uint8_t buf[], uint8_t *len, uint8_t maxlen) { if(!mInitialized) return; uint8_t rx_data; @@ -154,10 +154,13 @@ class esp32_3wSpi { }; SPI_PARAM_LOCK(); - for(uint8_t i = 0; i < len; i++) { + for(uint8_t i = 0; i < maxlen; i++) { ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); delayMicroseconds(4); // > 4 us - buf[i] = rx_data; + if(0 == i) + *len = rx_data; + else + buf[i-1] = rx_data; } SPI_PARAM_UNLOCK(); } diff --git a/src/hms/hmsPayload.h b/src/hms/hmsPayload.h deleted file mode 100644 index e082f059..00000000 --- a/src/hms/hmsPayload.h +++ /dev/null @@ -1,415 +0,0 @@ -//----------------------------------------------------------------------------- -// 2023 Ahoy, https://ahoydtu.de -// Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed -//----------------------------------------------------------------------------- - -#ifndef __HMS_PAYLOAD_H__ -#define __HMS_PAYLOAD_H__ - -#include "../utils/dbg.h" -#include "../utils/crc.h" -#include "../config/config.h" -#include - -#define HMS_TIMEOUT_SEC 30 // 30s * 1000 - -typedef struct { - uint8_t txCmd; - uint8_t txId; - //uint8_t invId; - uint32_t ts; - uint8_t data[MAX_PAYLOAD_ENTRIES][MAX_RF_PAYLOAD_SIZE]; - int8_t rssi[MAX_PAYLOAD_ENTRIES]; - uint8_t len[MAX_PAYLOAD_ENTRIES]; - bool complete; - uint8_t maxPackId; - bool lastFound; - uint8_t retransmits; - bool requested; - bool gotFragment; -} hmsPayload_t; - - -typedef std::function *)> payloadListenerType; -typedef std::function *)> alarmListenerType; - - -template -class HmsPayload { - public: - HmsPayload() {} - - void setup(IApp *app, HMSYSTEM *sys, RADIO *radio, statistics_t *stat, uint8_t maxRetransmits, uint32_t *timestamp) { - mApp = app; - mSys = sys; - mRadio = radio; - mStat = stat; - mMaxRetrans = maxRetransmits; - mTimestamp = timestamp; - for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { - reset(i); - mIvCmd56Cnt[i] = 0; - } - mSerialDebug = false; - mHighPrioIv = NULL; - mCbAlarm = NULL; - mCbPayload = NULL; - //mLastRx = 0; - } - - void enableSerialDebug(bool enable) { - mSerialDebug = enable; - } - - void addPayloadListener(payloadListenerType cb) { - mCbPayload = cb; - } - - void addAlarmListener(alarmListenerType cb) { - mCbAlarm = cb; - } - - void loop() { - if(NULL != mHighPrioIv) { - ivSend(mHighPrioIv, true); - mHighPrioIv = NULL; - } - } - - void ivSendHighPrio(Inverter<> *iv) { - mHighPrioIv = iv; - } - - void ivSend(Inverter<> *iv, bool highPrio = false) { - if ((IV_HMS != iv->ivGen) && (IV_HMT != iv->ivGen)) // only process HMS inverters - return; - - if(!highPrio) { - if (mPayload[iv->id].requested) { - if (!mPayload[iv->id].complete) - process(false); // no retransmit - - if (!mPayload[iv->id].complete) { - if (MAX_PAYLOAD_ENTRIES == mPayload[iv->id].maxPackId) - mStat->rxFailNoAnser++; // got nothing - else - mStat->rxFail++; // got fragments but not complete response - - iv->setQueuedCmdFinished(); // command failed - if (mSerialDebug) - DPRINTLN(DBG_INFO, F("enqueued cmd failed/timeout")); - /*if (mSerialDebug) { - DPRINT(DBG_INFO, F("(#")); - DBGPRINT(String(iv->id)); - DBGPRINT(F(") no Payload received! (retransmits: ")); - DBGPRINT(String(mPayload[iv->id].retransmits)); - DBGPRINTLN(F(")")); - }*/ - } - } - } - - reset(iv->id); - mPayload[iv->id].requested = true; - - yield(); - if (mSerialDebug) { - DPRINT_IVID(DBG_INFO, iv->id); - DBGPRINT(F("Requesting Inv SN ")); - DBGPRINTLN(String(iv->config->serial.u64, HEX)); - } - - record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); - if (iv->getDevControlRequest()) { - if (mSerialDebug) { - DPRINT_IVID(DBG_INFO, iv->id); - DBGPRINT(F("Devcontrol request 0x")); - DBGPRINT(String(iv->devControlCmd, HEX)); - DBGPRINT(F(" power limit ")); - DBGPRINTLN(String(iv->powerLimit[0])); - } - iv->powerLimitAck = false; - mRadio->sendControlPacket(&iv->radioId.u64, iv->devControlCmd, iv->powerLimit, false); - mPayload[iv->id].txCmd = iv->devControlCmd; - //iv->clearCmdQueue(); - //iv->enqueCommand(SystemConfigPara); // read back power limit - } else if(((rec->ts + HMS_TIMEOUT_SEC) < *mTimestamp) && (mIvCmd56Cnt[iv->id] < 3)) { - mRadio->switchFrequency(&iv->radioId.u64, HOY_BOOT_FREQ_KHZ, WORK_FREQ_KHZ); - mIvCmd56Cnt[iv->id]++; - } else { - if(++mIvCmd56Cnt[iv->id] == 10) - mIvCmd56Cnt[iv->id] = 0; - uint8_t cmd = iv->getQueuedCmd(); - DPRINT_IVID(DBG_INFO, iv->id); - DBGPRINT(F("prepareDevInformCmd 0x")); - DBGHEXLN(cmd); - mRadio->prepareDevInformCmd(&iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmLastId, false); - mPayload[iv->id].txCmd = cmd; - } - } - - void add(Inverter<> *iv, hmsPacket_t *p) { - if (p->data[1] == (TX_REQ_INFO + ALL_FRAMES)) { // response from get information command - mPayload[iv->id].txId = p->data[1]; - DPRINTLN(DBG_DEBUG, F("Response from info request received")); - uint8_t *pid = &p->data[10]; - 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) < MAX_PAYLOAD_ENTRIES) { - memcpy(mPayload[iv->id].data[(*pid & 0x7F) - 1], &p->data[11], p->data[0] - 11); - mPayload[iv->id].len[(*pid & 0x7F) - 1] = p->data[0] -11; - mPayload[iv->id].gotFragment = true; - mPayload[iv->id].rssi[(*pid & 0x7F) - 1] = p->rssi; - } - - if ((*pid & ALL_FRAMES) == ALL_FRAMES) { - // Last packet - if (((*pid & 0x7f) > mPayload[iv->id].maxPackId) || (MAX_PAYLOAD_ENTRIES == mPayload[iv->id].maxPackId)) { - mPayload[iv->id].maxPackId = (*pid & 0x7f); - if (*pid > 0x81) - mPayload[iv->id].lastFound = true; - } - } - } - } else if (p->data[1] == (TX_REQ_DEVCONTROL + ALL_FRAMES)) { // response from dev control command - DPRINTLN(DBG_DEBUG, F("Response from devcontrol request received")); - - mPayload[iv->id].txId = p->data[1]; - iv->clearDevControlRequest(); - - if ((p->data[13] == ActivePowerContr) && (p->data[14] == 0x00)) { - bool ok = true; - if((p->data[11] == 0x00) && (p->data[12] == 0x00)) { - mApp->setMqttPowerLimitAck(iv); - iv->powerLimitAck = true; - } else - ok = false; - DPRINT_IVID(DBG_INFO, 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 ")); - DBGPRINTLN(String(iv->powerLimit[1])); - - iv->clearCmdQueue(); - iv->enqueCommand(SystemConfigPara); // read back power limit - if(mHighPrioIv == NULL) // do it immediately if possible - mHighPrioIv = iv; - } - iv->devControlCmd = Init; - } - } - - void process(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 ((IV_HMS != iv->ivGen) && (IV_HMT != iv->ivGen)) // only process HMS inverters - 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 - mPayload[iv->id].complete = true; - continue; // skip to next inverter - } - - if (!mPayload[iv->id].complete) { - bool crcPass, pyldComplete; - crcPass = build(iv->id, &pyldComplete); - if (!crcPass && !pyldComplete) { // payload not complete - if ((mPayload[iv->id].requested) && (retransmit)) { - if (mPayload[iv->id].retransmits < mMaxRetrans) { - mPayload[iv->id].retransmits++; - 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 = mMaxRetrans; - } else if(iv->devControlCmd == ActivePowerContr) { - DPRINTLN(DBG_INFO, F("retransmit power limit")); - mRadio->sendControlPacket(&iv->radioId.u64, iv->devControlCmd, iv->powerLimit, true); - } else { - if(false == mPayload[iv->id].gotFragment) { - - //DPRINTLN(DBG_WARN, F("nothing received: Request Complete Retransmit")); - //mPayload[iv->id].txCmd = iv->getQueuedCmd(); - //DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") prepareDevInformCmd 0x") + String(mPayload[iv->id].txCmd, HEX)); - //mRadio->prepareDevInformCmd(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmLastId, true); - - DPRINT_IVID(DBG_INFO, iv->id); - DBGPRINTLN(F("nothing received")); - mPayload[iv->id].retransmits = mMaxRetrans; - } else { - for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId - 1); i++) { - if (mPayload[iv->id].len[i] == 0) { - DPRINT(DBG_WARN, F("Frame ")); - DBGPRINT(String(i + 1)); - DBGPRINTLN(F(" missing: Request Retransmit")); - //mRadio->sendCmdPacket(iv->radioId.u64, TX_REQ_INFO, (SINGLE_FRAME + i), true); - break; // only request retransmit one frame per loop - } - yield(); - } - } - } - } - } - } /*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(); - DPRINT(DBG_INFO, F("(#")); - DBGPRINT(String(iv->id)); - DBGPRINT(F(") prepareDevInformCmd 0x")); - DBGPRINTLN(String(mPayload[iv->id].txCmd, HEX)); - mRadio->prepareDevInformCmd(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmLastId, true); - } - }*/ else { // payload complete - DPRINT(DBG_INFO, F("procPyld: cmd: 0x")); - DBGPRINTLN(String(mPayload[iv->id].txCmd, HEX)); - //DPRINT(DBG_DEBUG, F("procPyld: txid: 0x")); - //DBGPRINTLN(String(mPayload[iv->id].txId, HEX)); - DPRINTLN(DBG_DEBUG, F("procPyld: max: ") + String(mPayload[iv->id].maxPackId)); - record_t<> *rec = iv->getRecordStruct(mPayload[iv->id].txCmd); // choose the parser - mPayload[iv->id].complete = true; - - uint8_t payload[150]; - uint8_t payloadLen = 0; - - memset(payload, 0, 150); - - int8_t rssi = -127; - - for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId); i++) { - if((mPayload[iv->id].len[i] + payloadLen) > 150) { - DPRINTLN(DBG_ERROR, F("payload buffer to small!")); - break; - } - memcpy(&payload[payloadLen], mPayload[iv->id].data[i], (mPayload[iv->id].len[i])); - payloadLen += (mPayload[iv->id].len[i]); - // get worst RSSI - if(mPayload[iv->id].rssi[i] > rssi) - rssi = mPayload[iv->id].rssi[i]; - yield(); - } - payloadLen -= 2; - - if (mSerialDebug) { - DPRINT_IVID(DBG_INFO, iv->id); - DBGPRINT(F("Payload (")); - DBGPRINT(String(payloadLen)); - DBGPRINT(F("): ")); - ah::dumpBuf(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 + ALL_FRAMES)) - mStat->rxSuccess++; - - rec->ts = mPayload[iv->id].ts; - for (uint8_t i = 0; i < rec->length; i++) { - iv->addValue(i, payload, rec); - yield(); - } - iv->rssi = rssi; - iv->doCalculations(); - notify(mPayload[iv->id].txCmd, iv); - - if(AlarmData == mPayload[iv->id].txCmd) { - uint8_t i = 0; - uint32_t start, end; - while(1) { - if(0 == iv->parseAlarmLog(i++, payload, payloadLen)) - break; - if (NULL != mCbAlarm) - (mCbAlarm)(iv); - yield(); - } - } - } else { - DPRINT(DBG_ERROR, F("plausibility check failed, expected ")); - DBGPRINT(String(rec->pyldLen)); - DBGPRINTLN(F(" bytes")); - mStat->rxFail++; - } - - iv->setQueuedCmdFinished(); - } - } - yield(); - } - } - - private: - void notify(uint8_t val, Inverter<> *iv) { - if(NULL != mCbPayload) - (mCbPayload)(val, iv); - } - - 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)) { - crc = ah::crc16(mPayload[id].data[i], mPayload[id].len[i] - 1, 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 reset(uint8_t id) { - //DPRINT(DBG_INFO, "resetPayload: id: "); - //DBGPRINTLN(String(id)); - memset(&mPayload[id], 0, sizeof(hmsPayload_t)); - mPayload[id].txCmd = 0; - mPayload[id].gotFragment = false; - //mPayload[id].retransmits = 0; - mPayload[id].maxPackId = MAX_PAYLOAD_ENTRIES; - mPayload[id].lastFound = false; - mPayload[id].complete = false; - mPayload[id].requested = false; - mPayload[id].ts = *mTimestamp; - } - - IApp *mApp; - HMSYSTEM *mSys; - RADIO *mRadio; - statistics_t *mStat; - uint8_t mMaxRetrans; - uint32_t *mTimestamp; - //uint32_t mLastRx; - hmsPayload_t mPayload[MAX_NUM_INVERTERS]; - uint8_t mIvCmd56Cnt[MAX_NUM_INVERTERS]; - bool mSerialDebug; - Inverter<> *mHighPrioIv; - - alarmListenerType mCbAlarm; - payloadListenerType mCbPayload; -}; - -#endif /*__HMS_PAYLOAD_H__*/ diff --git a/src/hms/hmsRadio.h b/src/hms/hmsRadio.h index e579deda..b6b6bb48 100644 --- a/src/hms/hmsRadio.h +++ b/src/hms/hmsRadio.h @@ -6,21 +6,11 @@ #ifndef __HMS_RADIO_H__ #define __HMS_RADIO_H__ -#include "../utils/dbg.h" #include "cmt2300a.h" - -typedef struct { - int8_t rssi; - uint8_t data[28]; -} hmsPacket_t; - -#define U32_B3(val) ((uint8_t)((val >> 24) & 0xff)) -#define U32_B2(val) ((uint8_t)((val >> 16) & 0xff)) -#define U32_B1(val) ((uint8_t)((val >> 8) & 0xff)) -#define U32_B0(val) ((uint8_t)((val ) & 0xff)) +#include "../hm/radio.h" template -class CmtRadio { +class CmtRadio : public Radio { typedef SPI SpiType; typedef Cmt2300a CmtType; public: @@ -29,9 +19,8 @@ class CmtRadio { mCmtAvail = false; } - void setup(statistics_t *stat, uint8_t pinSclk, uint8_t pinSdio, uint8_t pinCsb, uint8_t pinFcsb, bool genDtuSn = true) { + void setup(uint8_t pinSclk, uint8_t pinSdio, uint8_t pinCsb, uint8_t pinFcsb, bool genDtuSn = true) { mCmt.setup(pinSclk, pinSdio, pinCsb, pinFcsb); - mStat = stat; reset(genDtuSn); } @@ -54,25 +43,14 @@ class CmtRadio { return false; } - void tickSecond() { - } - - void handleIntr(void) { - mIrqRcvd = true; - } - - void enableDebug() { - mSerialDebug = true; - } - bool isConnected() { return mCmtAvail; } - void sendControlPacket(const uint64_t *ivId, uint8_t cmd, uint16_t *data, bool isRetransmit) { + void sendControlPacket(Inverter<> *iv, uint8_t cmd, uint16_t *data, bool isRetransmit, bool isNoMI = true, bool is4chMI = false) { DPRINT(DBG_INFO, F("sendControlPacket cmd: 0x")); DBGHEXLN(cmd); - initPacket(ivId, TX_REQ_DEVCONTROL, SINGLE_FRAME); + initPacket(iv->radioId.u64, TX_REQ_DEVCONTROL, SINGLE_FRAME); uint8_t cnt = 10; mTxBuf[cnt++] = cmd; // cmd -> 0 on, 1 off, 2 restart, 11 active power, 12 reactive power, 13 power factor @@ -84,10 +62,10 @@ class CmtRadio { mTxBuf[cnt++] = ((data[1] ) ) & 0xff; // setting for persistens handling } - sendPacket(cnt, isRetransmit); + sendPacket(iv, cnt, isRetransmit); } - bool switchFrequency(const uint64_t *ivId, uint32_t fromkHz, uint32_t tokHz) { + bool switchFrequency(Inverter<> *iv, uint32_t fromkHz, uint32_t tokHz) { uint8_t fromCh = mCmt.freq2Chan(fromkHz); uint8_t toCh = mCmt.freq2Chan(tokHz); @@ -95,30 +73,16 @@ class CmtRadio { return false; mCmt.switchChannel(fromCh); - sendSwitchChCmd(ivId, toCh); + sendSwitchChCmd(iv, toCh); mCmt.switchChannel(toCh); return true; } - void prepareDevInformCmd(const uint64_t *ivId, uint8_t cmd, uint32_t ts, uint16_t alarmMesId, bool isRetransmit, uint8_t reqfld=TX_REQ_INFO) { // might not be necessary to add additional arg. - initPacket(ivId, reqfld, ALL_FRAMES); - mTxBuf[10] = cmd; - CP_U32_LittleEndian(&mTxBuf[12], ts); - if (cmd == AlarmData ) { //cmd == RealTimeRunData_Debug || - mTxBuf[18] = (alarmMesId >> 8) & 0xff; - mTxBuf[19] = (alarmMesId ) & 0xff; - } - sendPacket(24, isRetransmit); - } + std::queue mBufCtrl; - void sendPacket(uint8_t len, bool isRetransmit) { - if (len > 14) { - uint16_t crc = ah::crc16(&mTxBuf[10], len - 10); - mTxBuf[len++] = (crc >> 8) & 0xff; - mTxBuf[len++] = (crc ) & 0xff; - } - mTxBuf[len] = ah::crc8(mTxBuf, len); - len++; + private: + void sendPacket(Inverter<> *iv, uint8_t len, bool isRetransmit, bool appendCrc16=true) { + updateCrcs(len, appendCrc16); if(mSerialDebug) { DPRINT(DBG_INFO, F("TX ")); @@ -136,14 +100,15 @@ class CmtRadio { } if(isRetransmit) - mStat->retransmits++; + iv->radioStatistics.retransmits++; else - mStat->txCnt++; + iv->radioStatistics.txCnt++; } - std::queue mBufCtrl; + uint64_t getIvId(Inverter<> *iv) { + return iv->radioId.u64; + } - private: inline void reset(bool genDtuSn) { if(genDtuSn) generateDtuSn(); @@ -160,7 +125,7 @@ class CmtRadio { mRqstGetRx = false; } - inline void sendSwitchChCmd(const uint64_t *ivId, uint8_t ch) { + inline void sendSwitchChCmd(Inverter<> *iv, uint8_t ch) { /** ch: * 0x00: 860.00 MHz * 0x01: 860.25 MHz @@ -170,51 +135,25 @@ class CmtRadio { * ... * 0x28: 870.00 MHz * */ - initPacket(ivId, 0x56, 0x02); + initPacket(iv->radioId.u64, 0x56, 0x02); mTxBuf[10] = 0x15; mTxBuf[11] = 0x21; mTxBuf[12] = ch; mTxBuf[13] = 0x14; - sendPacket(14, false); + sendPacket(iv, 14, false); mRqstGetRx = true; } - void initPacket(const uint64_t *ivId, uint8_t mid, uint8_t pid) { - mTxBuf[0] = mid; - CP_U32_BigEndian(&mTxBuf[1], (*ivId) >> 8); - CP_U32_LittleEndian(&mTxBuf[5], mDtuSn); - mTxBuf[9] = pid; - memset(&mTxBuf[10], 0x00, 17); - } - - inline void generateDtuSn(void) { - uint32_t chipID = 0; - #ifdef ESP32 - uint64_t MAC = ESP.getEfuseMac(); - chipID = ((MAC >> 8) & 0xFF0000) | ((MAC >> 24) & 0xFF00) | ((MAC >> 40) & 0xFF); - #endif - mDtuSn = 0x80000000; // the first digit is an 8 for DTU production year 2022, the rest is filled with the ESP chipID in decimal - for(int i = 0; i < 7; i++) { - mDtuSn |= (chipID % 10) << (i * 4); - chipID /= 10; - } - } - inline void getRx(void) { - hmsPacket_t p; - uint8_t status = mCmt.getRx(p.data, 28, &p.rssi); + packet_t p; + uint8_t status = mCmt.getRx(p.packet, &p.len, 28, &p.rssi); if(CMT_SUCCESS == status) mBufCtrl.push(p); } CmtType mCmt; - uint32_t mDtuSn; - uint8_t mTxBuf[27]; - bool mSerialDebug; - bool mIrqRcvd; bool mRqstGetRx; bool mCmtAvail; - statistics_t *mStat; }; #endif /*__HMS_RADIO_H__*/ diff --git a/src/plugins/Display/Display.h b/src/plugins/Display/Display.h index 8152f372..c4853bab 100644 --- a/src/plugins/Display/Display.h +++ b/src/plugins/Display/Display.h @@ -135,7 +135,7 @@ class Display { } #if defined(ESP32) else if (mCfg->type == 10) { - mEpaper.loop(totalPower, totalYieldDay, totalYieldTotal, nrprod); + mEpaper.loop(((allOff) ? 0.0 : totalPower), totalYieldDay, totalYieldTotal, nrprod); mRefreshCycle++; } diff --git a/src/utils/dbg.h b/src/utils/dbg.h index 6c861329..9e754ba6 100644 --- a/src/utils/dbg.h +++ b/src/utils/dbg.h @@ -148,7 +148,7 @@ #define PVERBLN(str) #endif -#define DPRINT(level, str) ({\ +#define DPRINT(level, str) do {\ switch(level) {\ case DBG_ERROR: PERR(str); break; \ case DBG_WARN: PWARN(str); break; \ @@ -156,13 +156,13 @@ case DBG_DEBUG: PDBG(str); break; \ default: PVERB(str); break; \ }\ -}) +} while (0) -#define DPRINT_IVID(level, id) ({\ +#define DPRINT_IVID(level, id) do {\ DPRINT(level, F("(#")); DBGPRINT(String(id)); DBGPRINT(F(") "));\ -}) +} while (0) -#define DPRINTLN(level, str) ({\ +#define DPRINTLN(level, str) do {\ switch(level) {\ case DBG_ERROR: PERRLN(str); break; \ case DBG_WARN: PWARNLN(str); break; \ @@ -170,7 +170,7 @@ case DBG_DEBUG: PDBGLN(str); break; \ default: PVERBLN(str); break; \ }\ -}) +} while (0) /*class ahoyLog { diff --git a/src/utils/helper.h b/src/utils/helper.h index e1702877..252ca9fe 100644 --- a/src/utils/helper.h +++ b/src/utils/helper.h @@ -20,21 +20,21 @@ static Timezone gTimezone(CEST, CET); #define CHECK_MASK(a,b) ((a & b) == b) -#define CP_U32_LittleEndian(buf, v) ({ \ +#define CP_U32_LittleEndian(buf, v) do { \ uint8_t *b = buf; \ b[0] = ((v >> 24) & 0xff); \ b[1] = ((v >> 16) & 0xff); \ b[2] = ((v >> 8) & 0xff); \ b[3] = ((v ) & 0xff); \ -}) +} while (0) -#define CP_U32_BigEndian(buf, v) ({ \ +#define CP_U32_BigEndian(buf, v) do { \ uint8_t *b = buf; \ b[3] = ((v >> 24) & 0xff); \ b[2] = ((v >> 16) & 0xff); \ b[1] = ((v >> 8) & 0xff); \ b[0] = ((v ) & 0xff); \ -}) +} while (0) namespace ah { void ip2Arr(uint8_t ip[], const char *ipStr); diff --git a/src/web/RestApi.h b/src/web/RestApi.h index 85003f94..22d9ee69 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -107,6 +107,8 @@ class RestApi { getIvAlarms(root, request->url().substring(20).toInt()); else if(path.substring(0, 17) == "inverter/version/") getIvVersion(root, request->url().substring(22).toInt()); + else if(path.substring(0, 19) == "inverter/radiostat/") + getIvStatistis(root, request->url().substring(24).toInt()); else getNotFound(root, F("http://") + request->host() + F("/api/")); } @@ -245,7 +247,6 @@ class RestApi { getRadioCmtInfo(obj.createNestedObject(F("radioCmt"))); #endif getMqttInfo(obj.createNestedObject(F("mqtt"))); - getStatistics(obj.createNestedArray(F("statistics"))); #if defined(ESP32) obj[F("chip_revision")] = ESP.getChipRevision(); @@ -310,23 +311,19 @@ class RestApi { obj[F("html")] = F("reboot. Autoreload after 10 seconds"); } - void getStatistics(JsonArray arr) { - statistics_t *stat; - #if defined(ESP32) - for(uint8_t i = 0; i < 2; i++) { - stat = (0 == i) ? mApp->getNrfStatistics() : mApp->getCmtStatistics(); - #else - { - stat = mApp->getNrfStatistics(); - #endif - JsonObject obj = arr.createNestedObject(); - obj[F("rx_success")] = stat->rxSuccess; - obj[F("rx_fail")] = stat->rxFail; - obj[F("rx_fail_answer")] = stat->rxFailNoAnser; - obj[F("frame_cnt")] = stat->frmCnt; - obj[F("tx_cnt")] = stat->txCnt; - obj[F("retransmits")] = stat->retransmits; + void getIvStatistis(JsonObject obj, uint8_t id) { + Inverter<> *iv = mSys->getInverterByPos(id); + if(NULL == iv) { + obj[F("error")] = F("inverter not found!"); + return; } + obj[F("name")] = String(iv->config->name); + obj[F("rx_success")] = iv->radioStatistics.rxSuccess; + obj[F("rx_fail")] = iv->radioStatistics.rxFail; + obj[F("rx_fail_answer")] = iv->radioStatistics.rxFailNoAnser; + obj[F("frame_cnt")] = iv->radioStatistics.frmCnt; + obj[F("tx_cnt")] = iv->radioStatistics.txCnt; + obj[F("retransmits")] = iv->radioStatistics.retransmits; } void getInverterList(JsonObject obj) { diff --git a/src/web/html/api.js b/src/web/html/api.js index 82405a71..c9590d89 100644 --- a/src/web/html/api.js +++ b/src/web/html/api.js @@ -172,6 +172,15 @@ function getAjax(url, ptr, method="GET", json=null) { * CREATE DOM FUNCTIONS */ +function tr(val1, val2) { + if(typeof val2 == "number") + val2 = String(val2); + return ml("tr", {}, [ + ml("th", {style: "width: 50%"}, val1), + ml("td", {}, val2) + ]); +} + function des(val) { e = document.createElement('p'); e.classList.add("subdes"); diff --git a/src/web/html/system.html b/src/web/html/system.html index 071ddb1d..27ad1801 100644 --- a/src/web/html/system.html +++ b/src/web/html/system.html @@ -48,26 +48,6 @@ return ml("div", {class: "head p-2 mt-3"}, ml("div", {class: "row"}, ml("div", {class: "col a-c"}, text))) } - function tr(val1, val2) { - if(typeof val2 == "number") - val2 = String(val2); - return ml("tr", {}, [ - ml("th", {style: "width: 50%"}, val1), - ml("td", {}, val2) - ]); - } - - function parseStat(stat) { - return [ - tr("TX count", stat.tx_cnt), - tr("RX success", stat.rx_success), - tr("RX fail", stat.rx_fail), - tr("RX no answer", stat.rx_fail_answer), - tr("RX fragments", stat.frame_cnt), - tr("TX retransmits", stat.retransmits) - ]; - } - function parseRadio(obj) { const pa = ["MIN (recommended)", "LOW", "HIGH", "MAX"]; const dr = ["1 M", "2 M", "250 k"] @@ -78,7 +58,6 @@ tr("NRF24 Power Level", pa[obj.radioNrf.power_level]), tr("NRF24 Data Rate", dr[obj.radioNrf.dataRate] + "bps") ]; - Array.prototype.push.apply(lines, parseStat(obj.statistics[0])); } else lines = [tr("NRF24L01", badge(false, "not enabled"))]; @@ -92,7 +71,6 @@ /*IF_ESP32*/ if(obj.radioCmt.en) { cmt = [tr("CMT2300A", badge(obj.radioCmt.isconnected, ((obj.radioCmt.isconnected) ? "" : "not ") + "connected"))]; - Array.prototype.push.apply(cmt, parseStat(obj.statistics[1])); } else cmt = [tr("CMT2300A", badge(false, "not enabled"))]; diff --git a/src/web/html/visualization.html b/src/web/html/visualization.html index 1629295e..3f098df5 100644 --- a/src/web/html/visualization.html +++ b/src/web/html/visualization.html @@ -178,20 +178,20 @@ ]); } - function tsInfo(ts, gen, rssi) { + function tsInfo(obj) { var ageInfo = "Last received data requested at: "; - if(ts > 0) { - var date = new Date(ts * 1000); + if(obj.ts_last_success > 0) { + var date = new Date(obj.ts_last_success * 1000); ageInfo += toIsoDateStr(date); } else ageInfo += "nothing received"; - if(rssi > -127) { - if(gen < 2) - ageInfo += " (RSSI: " + ((rssi == -64) ? ">=" : "<") + " -64dBm)"; + if(obj.rssi > -127) { + if(obj.generation < 2) + ageInfo += " (RSSI: " + ((obj.rssi == -64) ? ">=" : "<") + " -64dBm)"; else - ageInfo += " (RSSI: " + rssi + "dBm)"; + ageInfo += " (RSSI: " + obj.rssi + "dBm)"; } return ml("div", {class: "mb-5"}, [ @@ -199,7 +199,9 @@ ml("div", {class: "col"}, "") ), ml("div", {class: "row p-2 ts-bg mx-2"}, - ml("div", {class: "col mx-2"}, ageInfo) + ml("div", { class: "pointer col mx-2", onclick: function() { + getAjax("/api/inverter/radiostat/" + obj.id, parseIvRadioStats); + }}, ageInfo) ) ]); } @@ -219,7 +221,7 @@ ml("div", {}, [ ivHead(obj), ml("div", {class: "row mb-2"}, chn), - tsInfo(obj.ts_last_success, obj.generation, obj.rssi) + tsInfo(obj) ]) ); @@ -280,31 +282,30 @@ var html = ml("table", {class: "table"}, [ ml("tbody", {}, [ - ml("tr", {}, [ - ml("th", {}, "Model"), - ml("td", {}, model) - ]), - ml("tr", {}, [ - ml("th", {}, "Firmware Version / Build"), - ml("td", {}, String(obj.fw_ver) + " (build: " + String(obj.fw_date) + " " + String(obj.fw_time) + ")") - ]), - ml("tr", {}, [ - ml("th", {}, "Hardware Version / Build"), - ml("td", {}, (obj.hw_ver/100).toFixed(2) + " (build: " + String(obj.prod_cw) + "/" + String(obj.prod_year) + ")") - ]), - ml("tr", {}, [ - ml("th", {}, "Hardware Number"), - ml("td", {}, obj.part_num.toString(16)) - ]), - ml("tr", {}, [ - ml("th", {}, "Bootloader Version"), - ml("td", {}, (obj.boot_ver/100).toFixed(2)) - ]) + tr("Model", model), + tr("Firmware Version / Build", String(obj.fw_ver) + " (build: " + String(obj.fw_date) + " " + String(obj.fw_time) + ")"), + tr("Hardware Version / Build", (obj.hw_ver/100).toFixed(2) + " (build: " + String(obj.prod_cw) + "/" + String(obj.prod_year) + ")"), + tr("Hardware Number", obj.part_num.toString(16)), + tr("Bootloader Version", (obj.boot_ver/100).toFixed(2)) ]) ]); modal("Info for inverter " + obj.name, ml("div", {}, html)); } + function parseIvRadioStats(obj) { + var html = ml("table", {class: "table"}, [ + ml("tbody", {}, [ + tr("TX count", obj.tx_cnt), + tr("RX success", obj.rx_success), + tr("RX fail", obj.rx_fail), + tr("RX no answer", obj.rx_fail_answer), + tr("RX fragments", obj.frame_cnt), + tr("TX retransmits", obj.retransmits) + ]) + ]); + modal("Radio statistics for inverter " + obj.name, ml("div", {}, html)); + } + function parse(obj) { if(null != obj) { parseGeneric(obj["generic"]); diff --git a/src/web/web.h b/src/web/web.h index 2fb95287..fb605272 100644 --- a/src/web/web.h +++ b/src/web/web.h @@ -183,8 +183,8 @@ class Web { mUploadFp.write(data, len); if (final) { mUploadFp.close(); - char pwd[PWD_LEN]; #if !defined(ETHERNET) + char pwd[PWD_LEN]; strncpy(pwd, mConfig->sys.stationPwd, PWD_LEN); // backup WiFi PWD #endif if (!mApp->readSettings("/tmp.json")) {