Browse Source

improved set limit and radio statistics

communication with HMS / HMT is fine - for HM it tries to receive on the same channel which does not work for 4-ch inverter
pull/1219/head
lumapu 12 months ago
parent
commit
bf772756ed
  1. 13
      src/app.cpp
  2. 15
      src/app.h
  3. 4
      src/defines.h
  4. 101
      src/hm/Communication.h
  5. 9
      src/hm/hmDefines.h
  6. 29
      src/hm/hmInverter.h
  7. 12
      src/hm/hmRadio.h
  8. 9
      src/hms/cmt2300a.h
  9. 8
      src/hms/hmsRadio.h
  10. 2
      src/web/RestApi.h
  11. 6
      src/web/html/serial.html
  12. 5
      src/web/html/style.css

13
src/app.cpp

@ -60,6 +60,7 @@ void app::setup() {
#endif /* defined(ETHERNET) */
mCommunication.setup(&mTimestamp);
mCommunication.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) {
@ -93,6 +94,7 @@ void app::setup() {
if (mMqttEnabled) {
mMqtt.setup(&mConfig->mqtt, mConfig->sys.deviceName, mVersion, &mSys, &mTimestamp, &mUptime);
mMqtt.setSubscriptionCb(std::bind(&app::mqttSubRxCb, this, std::placeholders::_1));
mCommunication.addAlarmListener([this](Inverter<> *iv) { mMqtt.alarmEvent(iv); });
//mPayload.addAlarmListener([this](Inverter<> *iv) { mMqtt.alarmEvent(iv); });
//mMiPayload.addAlarmListener([this](Inverter<> *iv) { mMqtt.alarmEvent(iv); });
}
@ -122,7 +124,7 @@ void app::setup() {
//-----------------------------------------------------------------------------
void app::loop(void) {
ah::Scheduler::loop();
bool processPayload = false;
//bool processPayload = false;
mNrfRadio.loop();
#if defined(ESP32)
@ -403,8 +405,6 @@ void app::tickMidnight(void) {
//-----------------------------------------------------------------------------
void app::tickSend(void) {
DPRINTLN(DBG_INFO, "tickSend");
if(!mIVCommunicationOn) {
DPRINTLN(DBG_WARN, F("Time not set or it is night time, therefore no communication to the inverter!"));
return;
@ -413,8 +413,11 @@ void app::tickSend(void) {
for (uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
Inverter<> *iv = mSys.getInverterByPos(i);
if(NULL != iv) {
iv->tickSend([this, iv](uint8_t cmd) {
mCommunication.add(iv, cmd);
iv->tickSend([this, iv](uint8_t cmd, bool isDevControl) {
if(isDevControl)
mCommunication.addImportant(iv, cmd);
else
mCommunication.add(iv, cmd);
});
};
}

15
src/app.h

@ -12,12 +12,11 @@
#include "config/settings.h"
#include "defines.h"
#include "appInterface.h"
#include "hm/hmPayload.h"
#include "hm/hmSystem.h"
#include "hm/hmRadio.h"
#include "hms/hmsRadio.h"
#include "hm/hmPayload.h"
#include "hm/miPayload.h"
//#include "hm/hmPayload.h"
//#include "hm/miPayload.h"
#include "publisher/pubMqtt.h"
#include "publisher/pubSerial.h"
#include "utils/crc.h"
@ -42,8 +41,8 @@
#define ACOS(x) (degrees(acos(x)))
typedef HmSystem<MAX_NUM_INVERTERS> HmSystemType;
typedef HmPayload<HmSystemType> PayloadType;
typedef MiPayload<HmSystemType> MiPayloadType;
//typedef HmPayload<HmSystemType> PayloadType;
//typedef MiPayload<HmSystemType> MiPayloadType;
#ifdef ESP32
typedef CmtRadio<esp32_3wSpi> CmtRadioType;
#endif
@ -174,8 +173,8 @@ class app : public IApp, public ah::Scheduler {
void ivSendHighPrio(Inverter<> *iv) {
if(mIVCommunicationOn) { // only send commands if communication is enabled
if (iv->ivGen == IV_MI)
mMiPayload.ivSendHighPrio(iv);
//if (iv->ivGen == IV_MI)
// mMiPayload.ivSendHighPrio(iv);
//else
// mPayload.ivSendHighPrio(iv);
}
@ -320,7 +319,7 @@ class app : public IApp, public ah::Scheduler {
WebType mWeb;
RestApiType mApi;
//PayloadType mPayload;
MiPayloadType mMiPayload;
//MiPayloadType mMiPayload;
PubSerialType mPubSerial;
#if !defined(ETHERNET)
//Improv mImprov;

4
src/defines.h

@ -12,8 +12,8 @@
// VERSION
//-------------------------------------
#define VERSION_MAJOR 0
#define VERSION_MINOR 7
#define VERSION_PATCH 66
#define VERSION_MINOR 8
#define VERSION_PATCH 0
//-------------------------------------
typedef struct {

101
src/hm/Communication.h

@ -10,6 +10,9 @@
#include <Arduino.h>
#include "../utils/crc.h"
typedef std::function<void(uint8_t, Inverter<> *)> payloadListenerType;
typedef std::function<void(Inverter<> *)> alarmListenerType;
class Communication : public CommQueue<> {
public:
void setup(uint32_t *timestamp) {
@ -21,6 +24,14 @@ class Communication : public CommQueue<> {
CommQueue::addImportant(iv, cmd, delOnPop);
}
void addPayloadListener(payloadListenerType cb) {
mCbPayload = cb;
}
void addAlarmListener(alarmListenerType cb) {
mCbAlarm = cb;
}
void loop() {
get([this](bool valid, const queue_s *q) {
if(!valid)
@ -45,6 +56,7 @@ class Communication : public CommQueue<> {
q->iv->radio->sendControlPacket(q->iv, q->cmd, q->iv->powerLimit, false);
} else
q->iv->radio->prepareDevInformCmd(q->iv, q->cmd, q->ts, q->iv->alarmLastId, false);
q->iv->radioStatistics.txCnt++;
mWaitTimeout = millis() + 500;
setAttempt();
mState = States::WAIT;
@ -59,7 +71,10 @@ class Communication : public CommQueue<> {
case States::CHECK_FRAMES: {
if(!q->iv->radio->get()) { // radio buffer empty
cmdDone();
DBGPRINTLN(F("request timeout"));
DPRINT(DBG_INFO, F("request timeout: "));
DBGPRINT(String(millis() - mWaitTimeout + 500));
DBGPRINTLN(F("ms"));
q->iv->radioStatistics.rxFailNoAnser++; // got nothing
if((IV_HMS == q->iv->ivGen) || (IV_HMT == q->iv->ivGen)) {
q->iv->radio->switchFrequency(q->iv, HOY_BOOT_FREQ_KHZ, WORK_FREQ_KHZ);
@ -74,12 +89,14 @@ class Communication : public CommQueue<> {
packet_t *p = &q->iv->radio->mBufCtrl.front();
DPRINT_IVID(DBG_INFO, q->iv->id);
DPRINT(DBG_INFO, F("RX "));
DBGPRINT(F("RX "));
DBGPRINT(String(p->millis));
DBGPRINT(F("ms "));
DBGPRINT(String(p->len));
DBGPRINT(F(" CH"));
DBGPRINT(String(p->ch));
if(IV_HM == q->iv->ivGen) {
DBGPRINT(F(" CH"));
DBGPRINT(String(p->ch));
}
DBGPRINT(F(", "));
DBGPRINT(String(p->rssi));
DBGPRINT(F("dBm | "));
@ -91,8 +108,10 @@ class Communication : public CommQueue<> {
if (p->packet[0] == (TX_REQ_INFO + ALL_FRAMES)) { // response from get information command
parseFrame(p);
nextState = States::CHECK_PACKAGE;
} else if (p->packet[0] == (TX_REQ_DEVCONTROL + ALL_FRAMES)) // response from dev control command
} else if (p->packet[0] == (TX_REQ_DEVCONTROL + ALL_FRAMES)) { // response from dev control command
parseDevCtrl(p, q);
cmdDone(true); // remove done request
}
}
q->iv->radio->mBufCtrl.pop();
@ -104,26 +123,40 @@ class Communication : public CommQueue<> {
case States::CHECK_PACKAGE:
if(0 == mMaxFrameId) {
setAttempt();
DPRINT_IVID(DBG_WARN, q->iv->id);
DBGPRINT(F("last frame missing: request retransmit"));
DBGPRINT(F("last frame missing: request retransmit ("));
DBGPRINT(String(q->attempts));
DBGPRINTLN(F(" attempts left)"));
uint8_t i = 0;
while(i < MAX_PAYLOAD_ENTRIES) {
if(mLocalBuf[i++].len == 0)
break;
}
q->iv->radio->sendCmdPacket(q->iv, TX_REQ_INFO, (SINGLE_FRAME + mMaxFrameId + 1), true);
setAttempt();
mWaitTimeout = millis() + 100;
q->iv->radio->sendCmdPacket(q->iv, TX_REQ_INFO, (ALL_FRAMES + i), true);
q->iv->radioStatistics.retransmits++;
mWaitTimeout = millis() + 500;
mState = States::WAIT;
break;
}
for(uint8_t i = 0; i < mMaxFrameId; i++) {
if(mLocalBuf[i].len == 0) {
setAttempt();
DPRINT_IVID(DBG_WARN, q->iv->id);
DBGPRINT(F("frame "));
DBGPRINT(String(i + 1));
DBGPRINTLN(F(" missing: request retransmit"));
DBGPRINT(F(" missing: request retransmit ("));
DBGPRINT(String(q->attempts));
DBGPRINTLN(F(" attempts left)"));
q->iv->radio->sendCmdPacket(q->iv, TX_REQ_INFO, (ALL_FRAMES + i), true);
setAttempt();
mWaitTimeout = millis() + 100;
q->iv->radioStatistics.retransmits++;
mWaitTimeout = millis() + 500;
mState = States::WAIT;
return;
}
@ -131,6 +164,9 @@ class Communication : public CommQueue<> {
compilePayload(q);
if(NULL != mCbPayload)
(mCbPayload)(q->cmd, q->iv);
cmdDone(true); // remove done request
mState = States::RESET; // everything ok, next request
break;
@ -194,6 +230,7 @@ class Communication : public CommQueue<> {
DBGPRINT(String(q->iv->powerLimit[0]));
DBGPRINT(F(" with PowerLimitControl "));
DBGPRINTLN(String(q->iv->powerLimit[1]));
q->iv->actPowerLimit = 0xffff; // unknown, readback current value
}
inline void compilePayload(const queue_s *q) {
@ -220,9 +257,9 @@ class Communication : public CommQueue<> {
return;
}
DPRINT_IVID(DBG_INFO, q->iv->id);
/*DPRINT_IVID(DBG_INFO, q->iv->id);
DBGPRINT(F("procPyld: cmd: 0x"));
DBGHEXLN(q->cmd);
DBGHEXLN(q->cmd);*/
memset(mPayload, 0, 150);
int8_t rssi = -127;
@ -247,6 +284,40 @@ class Communication : public CommQueue<> {
DBGPRINT(String(len));
DBGPRINT(F("): "));
ah::dumpBuf(mPayload, len);
record_t<> *rec = q->iv->getRecordStruct(q->cmd);
if(NULL == rec) {
DPRINTLN(DBG_ERROR, F("record is NULL!"));
return;
}
if((rec->pyldLen != len) && (0 != rec->pyldLen)) {
DPRINT(DBG_ERROR, F("plausibility check failed, expected "));
DBGPRINT(String(rec->pyldLen));
DBGPRINTLN(F(" bytes"));
q->iv->radioStatistics.rxFail++;
return;
}
q->iv->radioStatistics.rxSuccess++;
rec->ts = q->ts;
for (uint8_t i = 0; i < rec->length; i++) {
q->iv->addValue(i, mPayload, rec);
}
q->iv->rssi = rssi;
q->iv->doCalculations();
if(AlarmData == q->cmd) {
uint8_t i = 0;
while(1) {
if(0 == q->iv->parseAlarmLog(i++, mPayload, len))
break;
if (NULL != mCbAlarm)
(mCbAlarm)(q->iv);
yield();
}
}
}
private:
@ -267,6 +338,8 @@ class Communication : public CommQueue<> {
std::array<frame_t, MAX_PAYLOAD_ENTRIES> mLocalBuf;
uint8_t mMaxFrameId;
uint8_t mPayload[150];
payloadListenerType mCbPayload = NULL;
alarmListenerType mCbAlarm = NULL;
};
#endif /*__COMMUNICATION_H__*/

9
src/hm/hmDefines.h

@ -77,6 +77,15 @@ enum {CH0 = 0, CH1, CH2, CH3, CH4, CH5, CH6};
enum {INV_TYPE_1CH = 0, INV_TYPE_2CH, INV_TYPE_4CH, INV_TYPE_6CH};
#define WORK_FREQ_KHZ 865000 // desired work frequency between DTU and
// inverter in kHz
#define HOY_BASE_FREQ_KHZ 860000 // in kHz
#define HOY_MAX_FREQ_KHZ 923500 // 0xFE * 250kHz + Base_freq
#define HOY_BOOT_FREQ_KHZ 868000 // Hoymiles boot/init frequency after power up inverter
#define FREQ_STEP_KHZ 250 // channel step size in kHz
#define FREQ_WARN_MIN_KHZ 863000 // for EU 863 - 870 MHz is allowed
#define FREQ_WARN_MAX_KHZ 870000 // for EU 863 - 870 MHz is allowed
typedef struct {
uint8_t fieldId; // field id

29
src/hm/hmInverter.h

@ -181,18 +181,23 @@ class Inverter {
// TODO: cleanup
}
void tickSend(std::function<void(uint8_t cmd)> cb) {
if((alarmLastId != alarmMesIndex) && (alarmMesIndex != 0))
cb(AlarmData); // get last alarms
void tickSend(std::function<void(uint8_t cmd, bool isDevControl)> cb) {
if(mDevControlRequest) {
cb(devControlCmd, true);
mDevControlRequest = false;
} else if((alarmLastId != alarmMesIndex) && (alarmMesIndex != 0))
cb(AlarmData, false); // get last alarms
else if(0 == getFwVersion())
cb(InverterDevInform_All); // get firmware version
cb(InverterDevInform_All, false); // get firmware version
else if(0 == getHwVersion())
cb(InverterDevInform_Simple); // get hardware version
cb(InverterDevInform_Simple, false); // get hardware version
else if(actPowerLimit == 0xffff)
cb(SystemConfigPara, false); // power limit info
else
cb(RealTimeRunData_Debug); // get live data
cb(RealTimeRunData_Debug, false); // get live data
}
template <typename T>
/*template <typename T>
void enqueCommand(uint8_t cmd) {
_commandQueue.push(std::make_shared<T>(cmd));
DPRINT_IVID(DBG_INFO, id);
@ -240,7 +245,7 @@ class Inverter {
enqueCommand<InfoCommand>(SystemConfigPara); // power limit info
}
return _commandQueue.front().get()->getCmd();
}
}*/
void init(void) {
@ -305,10 +310,6 @@ class Inverter {
mDevControlRequest = false;
}
inline bool getDevControlRequest() {
return mDevControlRequest;
}
void addValue(uint8_t pos, uint8_t buf[], record_t<> *rec) {
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:addValue"));
if(NULL != rec) {
@ -350,7 +351,7 @@ class Inverter {
DPRINT(DBG_INFO, "alarm ID incremented to ");
DBGPRINTLN(String(alarmMesIndex));
enqueCommand<InfoCommand>(AlarmData);
//enqueCommand<InfoCommand>(AlarmData);
}
}
}
@ -727,7 +728,7 @@ class Inverter {
radioId.b[0] = 0x01;
}
std::queue<std::shared_ptr<CommandAbstract>> _commandQueue;
//std::queue<std::shared_ptr<CommandAbstract>> _commandQueue;
bool mDevControlRequest; // true if change needed
};

12
src/hm/hmRadio.h

@ -118,8 +118,8 @@ class HmRadio : public Radio {
mNrf24.startListening();
uint32_t startMicros = micros();
uint32_t loopMillis = millis();
while (millis()-loopMillis < 400) {
uint32_t loopMillis = millis() + 400;
while (millis() < loopMillis) {
while (micros()-startMicros < 5110) { // listen (4088us or?) 5110us to each channel
if (mIrqRcvd) {
mIrqRcvd = false;
@ -277,7 +277,8 @@ class HmRadio : public Radio {
mRxChIdx = (mTxChIdx + 2) % RF_CHANNELS;
if(mSerialDebug) {
DPRINT(DBG_INFO, F("TX "));
DPRINT_IVID(DBG_INFO, iv->id);
DBGPRINT(F("TX "));
DBGPRINT(String(len));
DBGPRINT(" CH");
DBGPRINT(String(mRfChLst[mTxChIdx]));
@ -290,11 +291,6 @@ class HmRadio : public Radio {
mNrf24.openWritingPipe(reinterpret_cast<uint8_t*>(&iv->radioId.u64));
mNrf24.startWrite(mTxBuf, len, false); // false = request ACK response
mMillis = millis();
if(isRetransmit)
iv->radioStatistics.retransmits++;
else
iv->radioStatistics.txCnt++;
}
uint64_t getIvId(Inverter<> *iv) {

9
src/hms/cmt2300a.h

@ -8,15 +8,6 @@
#include "esp32_3wSpi.h"
#define WORK_FREQ_KHZ 865000 // desired work frequency between DTU and
// inverter in kHz
#define HOY_BASE_FREQ_KHZ 860000 // in kHz
#define HOY_MAX_FREQ_KHZ 923500 // 0xFE * 250kHz + Base_freq
#define HOY_BOOT_FREQ_KHZ 868000 // Hoymiles boot/init frequency after power up inverter
#define FREQ_STEP_KHZ 250 // channel step size in kHz
#define FREQ_WARN_MIN_KHZ 863000 // for EU 863 - 870 MHz is allowed
#define FREQ_WARN_MAX_KHZ 870000 // for EU 863 - 870 MHz is allowed
// detailed register infos from AN142_CMT2300AW_Quick_Start_Guide-Rev0.8.pdf
#define CMT2300A_MASK_CFG_RETAIN 0x10

8
src/hms/hmsRadio.h

@ -80,7 +80,8 @@ class CmtRadio : public Radio {
updateCrcs(&len, appendCrc16);
if(mSerialDebug) {
DPRINT(DBG_INFO, F("TX "));
DPRINT_IVID(DBG_INFO, iv->id);
DBGPRINT(F("TX "));
DBGPRINT(String(mCmt.getFreqKhz()/1000.0f));
DBGPRINT(F("Mhz | "));
ah::dumpBuf(mTxBuf, len);
@ -94,11 +95,6 @@ class CmtRadio : public Radio {
if(CMT_ERR_RX_IN_FIFO == status)
mIrqRcvd = true;
}
if(isRetransmit)
iv->radioStatistics.retransmits++;
else
iv->radioStatistics.txCnt++;
}
uint64_t getIvId(Inverter<> *iv) {

2
src/web/RestApi.h

@ -683,7 +683,7 @@ class RestApi {
}
else if(F("dev") == jsonIn[F("cmd")]) {
DPRINTLN(DBG_INFO, F("dev cmd"));
iv->enqueCommand<InfoCommand>(jsonIn[F("val")].as<int>());
//iv->enqueCommand<InfoCommand>(jsonIn[F("val")].as<int>());
}
else {
jsonOut[F("error")] = F("unknown cmd: '") + jsonIn["cmd"].as<String>() + "'";

6
src/web/html/serial.html

@ -7,9 +7,9 @@
<body>
{#HTML_NAV}
<div id="wrapper">
<div id="content">
<div id="content" style="max-width: 100% !important;">
<div class="row">
<textarea id="serial" class="mt-3" cols="80" rows="20" readonly></textarea>
<textarea id="serial" class="mt-3" cols="80" rows="40" readonly></textarea>
</div>
<div class="row my-3">
<div class="col-3">console active: <span class="dot" id="active"></span></div>
@ -42,7 +42,7 @@
if(true == exeOnce) {
parseNav(obj);
parseESP(obj);
window.setInterval("getAjax('/api/generic', parseGeneric)", 10000);
window.setInterval("getAjax('/api/generic', parseGeneric)", 5000);
exeOnce = false;
setTimeOffset();
}

5
src/web/html/style.css

@ -24,6 +24,11 @@ input[type=file] {
width: 100%;
}
textarea {
color: var(--fg);
background-color: var(--bg);
}
#live span {
color: var(--fg2);
}

Loading…
Cancel
Save