Browse Source

0.7.54

* added active power control in `W` to live view #201, #673
* updated docu, active power control related #706
* added current AC-Power to `index` page and removed version #763
* improved statistic data, moved to entire struct
* removed `/api/statistics` endpoint from REST-API
pull/1163/head
lumapu 1 year ago
parent
commit
9a09b5f4f8
  1. 2
      User_Manual.md
  2. 7
      src/CHANGES.md
  3. 17
      src/app.cpp
  4. 16
      src/app.h
  5. 10
      src/appInterface.h
  6. 4
      src/defines.h
  7. 17
      src/hm/hmRadio.h
  8. 12
      src/hms/hmsRadio.h
  9. 2
      src/platformio.ini
  10. 55
      src/web/RestApi.h
  11. 9
      src/web/html/index.html
  12. 49
      src/web/html/system.html
  13. 10
      src/web/html/visualization.html

2
User_Manual.md

@ -124,6 +124,8 @@ The AhoyDTU subscribes on following topics:
👆 `<INVERTER_ID>` is the number of the specific inverter in the setup page.
**NOTE:** Some users reported that a limit below 20W results in 0W output of the inverter. To reenable the inverter a reboot command was need to be sent, because a new limit with 100% didn't work.
### Inverter restart
```mqtt

7
src/CHANGES.md

@ -1,5 +1,12 @@
# Development Changes
## 0.7.54 - 2023-09-16
* added active power control in `W` to live view #201, #673
* updated docu, active power control related #706
* added current AC-Power to `index` page and removed version #763
* improved statistic data, moved to entire struct
* removed `/api/statistics` endpoint from REST-API
## 0.7.53 - 2023-09-16
* fix ePaper / display night behaviour #1151
* fix ESP8266 compile error

17
src/app.cpp

@ -34,12 +34,12 @@ void app::setup() {
DBGPRINTLN(F("false"));
if(mConfig->nrf.enabled) {
mNrfRadio.setup(mConfig->nrf.amplifierPower, mConfig->nrf.pinIrq, mConfig->nrf.pinCe, mConfig->nrf.pinCs, mConfig->nrf.pinSclk, mConfig->nrf.pinMosi, mConfig->nrf.pinMiso);
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.enableDebug();
}
#if defined(ESP32)
if(mConfig->cmt.enabled) {
mCmtRadio.setup(mConfig->cmt.pinSclk, mConfig->cmt.pinSdio, mConfig->cmt.pinCsb, mConfig->cmt.pinFcsb, false);
mCmtRadio.setup(&mCmtStat, mConfig->cmt.pinSclk, mConfig->cmt.pinSdio, mConfig->cmt.pinCsb, mConfig->cmt.pinFcsb, false);
mCmtRadio.enableDebug();
}
#endif
@ -62,17 +62,17 @@ void app::setup() {
mSys.setup(&mTimestamp);
mSys.addInverters(&mConfig->inst);
if (mConfig->nrf.enabled) {
mPayload.setup(this, &mSys, &mNrfRadio, &mStat, mConfig->nrf.maxRetransPerPyld, &mTimestamp);
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));
mMiPayload.setup(this, &mSys, &mNrfRadio, &mStat, mConfig->nrf.maxRetransPerPyld, &mTimestamp);
mMiPayload.setup(this, &mSys, &mNrfRadio, &mNrfStat, 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, &mStat, 5, &mTimestamp);
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
@ -133,7 +133,7 @@ void app::loop(void) {
DBGPRINT(F("dBm | "));
ah::dumpBuf(p->packet, p->len);
}
mStat.frmCnt++;
mNrfStat.frmCnt++;
Inverter<> *iv = mSys.findInverter(&p->packet[1]);
if (NULL != iv) {
@ -160,7 +160,7 @@ void app::loop(void) {
DBGPRINT(F("dBm | "));
ah::dumpBuf(&p->data[1], p->data[0]);
}
mStat.frmCnt++;
mCmtStat.frmCnt++;
Inverter<> *iv = mSys.findInverter(&p->data[2]);
if(NULL != iv) {
@ -525,7 +525,8 @@ void app::resetSystem(void) {
mNetworkConnected = false;
memset(&mStat, 0, sizeof(statistics_t));
memset(&mNrfStat, 0, sizeof(statistics_t));
memset(&mCmtStat, 0, sizeof(statistics_t));
}
//-----------------------------------------------------------------------------

16
src/app.h

@ -129,8 +129,12 @@ class app : public IApp, public ah::Scheduler {
return mSaveReboot;
}
statistics_t *getStatistics() {
return &mStat;
statistics_t *getNrfStatistics() {
return &mNrfStat;
}
statistics_t *getCmtStatistics() {
return &mCmtStat;
}
#if !defined(ETHERNET)
@ -208,11 +212,6 @@ class app : public IApp, public ah::Scheduler {
return mWeb.isProtected(request);
}
void getNrfRadioCounters(uint32_t *sendCnt, uint32_t *retransmits) {
*sendCnt = mNrfRadio.mSendCnt;
*retransmits = mNrfRadio.mRetransmits;
}
bool getNrfEnabled(void) {
return mConfig->nrf.enabled;
}
@ -355,7 +354,8 @@ class app : public IApp, public ah::Scheduler {
bool mNetworkConnected;
statistics_t mStat;
statistics_t mNrfStat;
statistics_t mCmtStat;
// mqtt
PubMqttType mMqtt;

10
src/appInterface.h

@ -35,7 +35,8 @@ class IApp {
#endif /* defined(ETHERNET) */
virtual void setRebootFlag() = 0;
virtual const char *getVersion() = 0;
virtual statistics_t *getStatistics() = 0;
virtual statistics_t *getNrfStatistics() = 0;
virtual statistics_t *getCmtStatistics() = 0;
#if !defined(ETHERNET)
virtual void scanAvailNetworks() = 0;
@ -65,13 +66,8 @@ class IApp {
virtual bool getProtection(AsyncWebServerRequest *request) = 0;
virtual void getNrfRadioCounters(uint32_t *sendCnt, uint32_t *retransmits) = 0;
//virtual void getCmtRadioCounters(uint32_t *sendCnt, uint32_t *retransmits) = 0;
virtual void* getRadioObj(bool nrf) = 0;
#if defined(ESP32)
//virtual const CmtRadioType& getCmtRadioObj(void) const = 0;
#endif
};
#endif /*__IAPP_H__*/

4
src/defines.h

@ -13,7 +13,7 @@
//-------------------------------------
#define VERSION_MAJOR 0
#define VERSION_MINOR 7
#define VERSION_PATCH 53
#define VERSION_PATCH 54
//-------------------------------------
typedef struct {
@ -98,6 +98,8 @@ typedef struct {
uint32_t rxFailNoAnser;
uint32_t rxSuccess;
uint32_t frmCnt;
uint32_t txCnt;
uint32_t retransmits;
} statistics_t;
#endif /*__DEFINES_H__*/

17
src/hm/hmRadio.h

@ -53,17 +53,15 @@ class HmRadio {
mTxChIdx = 2; // Start TX with 40
mRxChIdx = 0; // Start RX with 03
mSendCnt = 0;
mRetransmits = 0;
mSerialDebug = false;
mIrqRcvd = false;
}
~HmRadio() {}
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) {
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) {
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
@ -241,10 +239,6 @@ class HmRadio {
}
std::queue<packet_t> mBufCtrl;
uint32_t mSendCnt;
uint32_t mRetransmits;
bool mSerialDebug;
private:
@ -293,7 +287,7 @@ class HmRadio {
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(mSendCnt));
//DPRINTLN(DBG_VERBOSE, "sent packet: #" + String(mStat->txCnt));
// append crc's
if (appendCrc16 && (len > 10)) {
@ -325,9 +319,9 @@ class HmRadio {
mNrf24.startWrite(mTxBuf, len, false); // false = request ACK response
if(isRetransmit)
mRetransmits++;
mStat->retransmits++;
else
mSendCnt++;
mStat->txCnt++;
}
volatile bool mIrqRcvd;
@ -340,6 +334,7 @@ class HmRadio {
SPIClass* mSpi;
RF24 mNrf24;
uint8_t mTxBuf[MAX_RF_PAYLOAD_SIZE];
statistics_t *mStat;
};
#endif /*__RADIO_H__*/

12
src/hms/hmsRadio.h

@ -29,8 +29,9 @@ class CmtRadio {
mCmtAvail = false;
}
void setup(uint8_t pinSclk, uint8_t pinSdio, uint8_t pinCsb, uint8_t pinFcsb, bool genDtuSn = true) {
void setup(statistics_t *stat, 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);
}
@ -135,13 +136,11 @@ class CmtRadio {
}
if(isRetransmit)
mRetransmits++;
mStat->retransmits++;
else
mSendCnt++;
mStat->txCnt++;
}
uint32_t mSendCnt;
uint32_t mRetransmits;
std::queue<hmsPacket_t> mBufCtrl;
private:
@ -156,8 +155,6 @@ class CmtRadio {
mCmt.goRx();
}
mSendCnt = 0;
mRetransmits = 0;
mSerialDebug = false;
mIrqRcvd = false;
mRqstGetRx = false;
@ -217,6 +214,7 @@ class CmtRadio {
bool mIrqRcvd;
bool mRqstGetRx;
bool mCmtAvail;
statistics_t *mStat;
};
#endif /*__HMS_RADIO_H__*/

2
src/platformio.ini

@ -143,6 +143,6 @@ build_flags = ${env.build_flags}
-DDEF_LED1=17
-DLED_ACTIVE_HIGH
-DARDUINO_USB_MODE=1
;-DARDUINO_USB_CDC_ON_BOOT=1
-DARDUINO_USB_CDC_ON_BOOT=1
monitor_filters =
esp32_exception_decoder, colorize

55
src/web/RestApi.h

@ -93,7 +93,6 @@ class RestApi {
else if(path == "system") getSysInfo(request, root);
else if(path == "generic") getGeneric(request, root);
else if(path == "reboot") getReboot(request, root);
else if(path == "statistics") getStatistics(root);
else if(path == "inverter/list") getInverterList(root);
else if(path == "index") getIndex(request, root);
else if(path == "setup") getSetup(request, root);
@ -161,7 +160,7 @@ class RestApi {
ep[F("inverter/list")] = url + F("inverter/list");
ep[F("inverter/id/0")] = url + F("inverter/id/0");
ep[F("inverter/alarm/0")] = url + F("inverter/alarm/0");
ep[F("statistics")] = url + F("statistics");
ep[F("inverter/version/0")] = url + F("inverter/version/0");
ep[F("generic")] = url + F("generic");
ep[F("index")] = url + F("index");
ep[F("setup")] = url + F("setup");
@ -246,7 +245,7 @@ class RestApi {
getRadioCmtInfo(obj.createNestedObject(F("radioCmt")));
#endif
getMqttInfo(obj.createNestedObject(F("mqtt")));
getStatistics(obj.createNestedObject(F("statistics")));
getStatistics(obj.createNestedArray(F("statistics")));
#if defined(ESP32)
obj[F("chip_revision")] = ESP.getChipRevision();
@ -311,14 +310,23 @@ class RestApi {
obj[F("html")] = F("reboot. Autoreload after 10 seconds");
}
void getStatistics(JsonObject obj) {
statistics_t *stat = mApp->getStatistics();
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")] = mRadioNrf->mSendCnt;
obj[F("retransmits")] = mRadioNrf->mRetransmits;
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 getInverterList(JsonObject obj) {
@ -369,6 +377,7 @@ class RestApi {
obj[F("version")] = String(iv->getFwVersion());
obj[F("power_limit_read")] = ah::round3(iv->actPowerLimit);
obj[F("power_limit_ack")] = iv->powerLimitAck;
obj[F("max_pwr")] = iv->getMaxPower();
obj[F("ts_last_success")] = rec->ts;
obj[F("generation")] = iv->ivGen;
obj[F("status")] = (uint8_t)iv->status;
@ -576,7 +585,7 @@ class RestApi {
invObj[F("enabled")] = (bool)iv->config->enabled;
invObj[F("id")] = i;
invObj[F("name")] = String(iv->config->name);
invObj[F("version")] = String(iv->getFwVersion());
invObj[F("cur_pwr")] = iv->getPosByChFld(CH0, FLD_PAC, rec);
invObj[F("is_avail")] = iv->isAvailable();
invObj[F("is_producing")] = iv->isProducing();
invObj[F("ts_last_success")] = iv->getLastTs(rec);
@ -642,28 +651,6 @@ class RestApi {
}
}
/*void getRecord(JsonObject obj, uint8_t recType) {
JsonArray invArr = obj.createNestedArray(F("inverter"));
Inverter<> *iv;
record_t<> *rec;
uint8_t pos;
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) {
iv = mSys->getInverterByPos(i);
if(NULL != iv) {
rec = iv->getRecordStruct(recType);
JsonArray obj2 = invArr.createNestedArray();
for(uint8_t j = 0; j < rec->length; j++) {
byteAssign_t *assign = iv->getByteAssign(j, rec);
pos = (iv->getPosByChFld(assign->ch, assign->fieldId, rec));
obj2[j]["fld"] = (0xff != pos) ? String(iv->getFieldName(pos, rec)) : notAvail;
obj2[j]["unit"] = (0xff != pos) ? String(iv->getUnit(pos, rec)) : notAvail;
obj2[j]["val"] = (0xff != pos) ? String(iv->getValue(pos, rec)) : notAvail;
}
}
}
}*/
bool setCtrl(JsonObject jsonIn, JsonObject jsonOut) {
Inverter<> *iv = mSys->getInverterByPos(jsonIn[F("id")]);
bool accepted = true;

9
src/web/html/index.html

@ -134,15 +134,16 @@
} else {
avail = "available and is ";
if(false == i["is_producing"])
avail += "not ";
else
avail += "not producing";
else {
icon = iconSuccessFull;
avail += "producing";
avail += "producing " + i.cur_pwr + "W";
}
}
p.append(
svg(icon, 30, 30, "icon " + cl),
span("Inverter #" + i["id"] + ": " + i["name"] + " (v" + i["version"] + ") is " + avail),
span("Inverter #" + i["id"] + ": " + i["name"] + " is " + avail),
br()
);

49
src/web/html/system.html

@ -57,45 +57,52 @@
]);
}
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"]
if(obj.radioNrf.en)
if(obj.radioNrf.en) {
lines = [
tr("NRF24L01", badge(obj.radioNrf.isconnected, ((obj.radioNrf.isconnected) ? "" : "not ") + "connected")),
tr("NRF24 Power Level", pa[obj.radioNrf.power_level]),
tr("NRF24 Data Rate", dr[obj.radioNrf.dataRate] + "bps")
];
else
Array.prototype.push.apply(lines, parseStat(obj.statistics[0]));
} else
lines = [tr("NRF24L01", badge(false, "not enabled"))];
/*IF_ESP32*/
if(obj.radioCmt.en)
lines.push(tr("CMT2300A", badge(obj.radioCmt.isconnected, ((obj.radioCmt.isconnected) ? "" : "not ") + "connected")));
else
lines.push(tr("CMT2300A", badge(false, "not enabled")));
/*ENDIF_ESP32*/
var stat = obj.statistics;
document.getElementById("info").append(
headline("Radio"),
headline("Radio NRF"),
ml("table", {class: "table"},
ml("tbody", {}, lines)
),
)
);
/*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"))];
headline("Statistics"),
document.getElementById("info").append(
headline("Radio CMT"),
ml("table", {class: "table"},
ml("tbody", {}, [
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)
])
ml("tbody", {}, cmt)
)
);
/*ENDIF_ESP32*/
}
function parseMqtt(obj) {

10
src/web/html/visualization.html

@ -98,6 +98,14 @@
var t = span("&nbsp;&deg;C");
var clh = (0 == obj.status) ? "iv-h-dis" : "iv-h";
var clbg = (0 == obj.status) ? "iv-bg-dis" : "iv-bg";
var pwrLimit = "n/a";
if(65535 != obj.power_limit_read) {
pwrLimit = obj.power_limit_read + "&nbsp;%";
if(0 != obj.max_pwr)
pwrLimit += ", " + (obj.max_pwr * obj.power_limit_read / 100) + "W";
}
return ml("div", {class: "row mt-2"},
ml("div", {class: "col"}, [
ml("div", {class: "p-2 " + clh},
@ -105,7 +113,7 @@
ml("div", {class: "col mx-2 mx-md-1"}, ml("span", { class: "pointer", onclick: function() {
getAjax("/api/inverter/version/" + obj.id, parseIvVersion);
}}, obj.name)),
ml("div", {class: "col a-c"}, "Active Power Control: " + ((obj.power_limit_read == 65535) ? "n/a" : (obj.power_limit_read + "&nbsp;%"))),
ml("div", {class: "col a-c"}, "Active Power Control: " + pwrLimit),
ml("div", {class: "col a-c"}, ml("span", { class: "pointer", onclick: function() {
getAjax("/api/inverter/alarm/" + obj.id, parseIvAlarm);
}}, ("Alarms: " + obj.alarm_cnt))),

Loading…
Cancel
Save