mirror of
https://github.com/lumapu/ahoy.git
synced 2025-05-10 07:26:38 +02:00
* moved mqtt loop out of checkTicker
as mentioned in #49
* added irritation and efficiency calculations (** EPPROM CHANGE - YOUR SETTINGS MAYBE BECOME CURRUPT! **) * improved style
This commit is contained in:
parent
8238e90903
commit
7844ea2946
11 changed files with 180 additions and 28 deletions
|
@ -25,6 +25,7 @@ app::app() : Main() {
|
|||
|
||||
memset(mPayload, 0, (MAX_NUM_INVERTERS * sizeof(invPayload_t)));
|
||||
mRxFailed = 0;
|
||||
mRxSuccess = 0;
|
||||
|
||||
mSys = new HmSystemType();
|
||||
}
|
||||
|
@ -51,13 +52,15 @@ void app::setup(uint32_t timeout) {
|
|||
if(mSettingsValid) {
|
||||
uint64_t invSerial;
|
||||
char invName[MAX_NAME_LENGTH + 1] = {0};
|
||||
uint16_t modPwr[4];
|
||||
|
||||
// inverter
|
||||
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) {
|
||||
mEep->read(ADDR_INV_ADDR + (i * 8), &invSerial);
|
||||
mEep->read(ADDR_INV_NAME + (i * MAX_NAME_LENGTH), invName, MAX_NAME_LENGTH);
|
||||
mEep->read(ADDR_INV_MOD_PWR + (i * 2 * 4), modPwr, 4);
|
||||
if(0ULL != invSerial) {
|
||||
mSys->addInverter(invName, invSerial);
|
||||
mSys->addInverter(invName, invSerial, modPwr);
|
||||
DPRINTLN("add inverter: " + String(invName) + ", SN: " + String(invSerial, HEX));
|
||||
}
|
||||
}
|
||||
|
@ -174,9 +177,11 @@ void app::loop(void) {
|
|||
}
|
||||
}
|
||||
|
||||
if(mMqttActive)
|
||||
mMqtt.loop();
|
||||
|
||||
if(checkTicker(&mTicker, 1000)) {
|
||||
if(mMqttActive) {
|
||||
mMqtt.loop();
|
||||
if(++mMqttTicker >= mMqttInterval) {
|
||||
mMqttTicker = 0;
|
||||
mMqtt.isConnected(true);
|
||||
|
@ -331,6 +336,7 @@ void app::processPayload(bool retransmit) {
|
|||
DPRINT("Payload (" + String(offs) + "): ");
|
||||
mSys->Radio.dumpBuf(NULL, payload, offs);
|
||||
}
|
||||
mRxSuccess++;
|
||||
|
||||
for(uint8_t i = 0; i < iv->listLen; i++) {
|
||||
iv->addValue(i, payload);
|
||||
|
@ -374,9 +380,11 @@ void app::showSetup(void) {
|
|||
uint64_t invSerial;
|
||||
char invName[MAX_NAME_LENGTH + 1] = {0};
|
||||
uint8_t invType;
|
||||
uint16_t modPwr[4];
|
||||
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) {
|
||||
mEep->read(ADDR_INV_ADDR + (i * 8), &invSerial);
|
||||
mEep->read(ADDR_INV_NAME + (i * MAX_NAME_LENGTH), invName, MAX_NAME_LENGTH);
|
||||
mEep->read(ADDR_INV_MOD_PWR + (i * 2 * 4), modPwr, 4);
|
||||
inv += "<p class=\"subdes\">Inverter "+ String(i) + "</p>";
|
||||
|
||||
inv += "<label for=\"inv" + String(i) + "Addr\">Address</label>";
|
||||
|
@ -389,6 +397,13 @@ void app::showSetup(void) {
|
|||
inv += "<input type=\"text\" class=\"text\" name=\"inv" + String(i) + "Name\" value=\"";
|
||||
inv += String(invName);
|
||||
inv += "\"/ maxlength=\"" + String(MAX_NAME_LENGTH) + "\">";
|
||||
|
||||
inv += "<label for=\"inv" + String(i) + "ModPwr0\">Max Module Power (Wp)</label>";
|
||||
for(uint8_t j = 0; j < 4; j++) {
|
||||
inv += "<input type=\"text\" class=\"text sh\" name=\"inv" + String(i) + "ModPwr" + String(j) + "\" value=\"";
|
||||
inv += String(modPwr[j]);
|
||||
inv += "\"/ maxlength=\"4\">";
|
||||
}
|
||||
}
|
||||
html.replace("{INVERTERS}", String(inv));
|
||||
|
||||
|
@ -486,10 +501,31 @@ void app::showErase() {
|
|||
|
||||
//-----------------------------------------------------------------------------
|
||||
void app::showStatistics(void) {
|
||||
String content = "Failed Payload: " + String(mRxFailed) + "\n";
|
||||
String content = "Receive success: " + String(mRxSuccess) + "\n";
|
||||
content += "Receive fail: " + String(mRxFailed) + "\n";
|
||||
content += "Send Cnt: " + String(mSys->Radio.mSendCnt) + String("\n\n");
|
||||
|
||||
content += "Free Heap: 0x" + String(ESP.getFreeHeap(), HEX) + "\n";
|
||||
content += "Free Heap: 0x" + String(ESP.getFreeHeap(), HEX) + "\n\n";
|
||||
|
||||
Inverter<> *iv;
|
||||
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
|
||||
iv = mSys->getInverterByPos(i);
|
||||
if(NULL != iv) {
|
||||
bool avail = true;
|
||||
content += "Inverter '"+ String(iv->name) + "' is ";
|
||||
if(!iv->isAvailable(mTimestamp)) {
|
||||
content += "not ";
|
||||
avail = false;
|
||||
}
|
||||
content += "available and is ";
|
||||
if(!iv->isProducing(mTimestamp))
|
||||
content += "not ";
|
||||
content += "producing\n";
|
||||
|
||||
if(!avail)
|
||||
content += "-> last successful transmission: " + getDateTimeStr(iv->getLastTs());
|
||||
}
|
||||
}
|
||||
|
||||
if(!mSys->Radio.isChipConnected())
|
||||
content += "WARNING! your NRF24 module can't be reached, check the wiring and pinout (<a href=\"/setup\">setup</a>)\n";
|
||||
|
@ -537,9 +573,9 @@ void app::showLiveData(void) {
|
|||
|
||||
modHtml += "<div class=\"iv\">";
|
||||
modHtml += "<div class=\"ch-iv\"><span class=\"head\">" + String(iv->name) + "</span>";
|
||||
uint8_t list[8] = {FLD_UAC, FLD_IAC, FLD_PAC, FLD_F, FLD_PCT, FLD_T, FLD_YT, FLD_YD};
|
||||
uint8_t list[] = {FLD_UAC, FLD_IAC, FLD_PAC, FLD_F, FLD_PCT, FLD_T, FLD_YT, FLD_YD, FLD_PDC, FLD_EFF};
|
||||
|
||||
for(uint8_t fld = 0; fld < 8; fld++) {
|
||||
for(uint8_t fld = 0; fld < 10; fld++) {
|
||||
pos = (iv->getPosByChFld(CH0, list[fld]));
|
||||
if(0xff != pos) {
|
||||
modHtml += "<div class=\"subgrp\">";
|
||||
|
@ -553,13 +589,14 @@ void app::showLiveData(void) {
|
|||
|
||||
for(uint8_t ch = 1; ch <= modNum; ch ++) {
|
||||
modHtml += "<div class=\"ch\"><span class=\"head\">CHANNEL " + String(ch) + "</span>";
|
||||
for(uint8_t j = 0; j < 5; j++) {
|
||||
for(uint8_t j = 0; j < 6; j++) {
|
||||
switch(j) {
|
||||
default: pos = (iv->getPosByChFld(ch, FLD_UDC)); break;
|
||||
case 1: pos = (iv->getPosByChFld(ch, FLD_IDC)); break;
|
||||
case 2: pos = (iv->getPosByChFld(ch, FLD_PDC)); break;
|
||||
case 3: pos = (iv->getPosByChFld(ch, FLD_YD)); break;
|
||||
case 4: pos = (iv->getPosByChFld(ch, FLD_YT)); break;
|
||||
case 5: pos = (iv->getPosByChFld(ch, FLD_IRR)); break;
|
||||
}
|
||||
if(0xff != pos) {
|
||||
modHtml += "<span class=\"value\">" + String(iv->getValue(pos));
|
||||
|
@ -569,7 +606,7 @@ void app::showLiveData(void) {
|
|||
}
|
||||
modHtml += "</div>";
|
||||
}
|
||||
modHtml += "<div class=\"ts\">Last data update: " + getDateTimeStr(iv->ts) + "</div>";
|
||||
modHtml += "<div class=\"ts\">Last received data requested at: " + getDateTimeStr(iv->ts) + "</div>";
|
||||
modHtml += "</div>";
|
||||
#else
|
||||
// dump all data to web frontend
|
||||
|
@ -613,6 +650,12 @@ void app::saveValues(bool webSend = true) {
|
|||
// name
|
||||
mWeb->arg("inv" + String(i) + "Name").toCharArray(buf, 20);
|
||||
mEep->write(ADDR_INV_NAME + (i * MAX_NAME_LENGTH), buf, MAX_NAME_LENGTH);
|
||||
|
||||
// max module power
|
||||
for(uint8_t j = 0; j < 4; j++) {
|
||||
uint16_t pwr = mWeb->arg("inv" + String(i) + "ModPwr" + String(j)).toInt();
|
||||
mEep->write(ADDR_INV_MOD_PWR + (i * 2 * 4) + (j*2), pwr);
|
||||
}
|
||||
}
|
||||
|
||||
interval = mWeb->arg("invInterval").toInt();
|
||||
|
|
|
@ -86,6 +86,7 @@ class app : public Main {
|
|||
|
||||
invPayload_t mPayload[MAX_NUM_INVERTERS];
|
||||
uint32_t mRxFailed;
|
||||
uint32_t mRxSuccess;
|
||||
|
||||
// timer
|
||||
uint32_t mTicker;
|
||||
|
|
|
@ -38,9 +38,15 @@
|
|||
// maximum buffer length of packet received / sent to RF24 module
|
||||
#define MAX_RF_PAYLOAD_SIZE 32
|
||||
|
||||
// maximum total payload size
|
||||
// maximum total payload buffers (must be greater than the number of received frame fragments)
|
||||
#define MAX_PAYLOAD_ENTRIES 4
|
||||
|
||||
// number of seconds since last successful response, before inverter is marked inactive
|
||||
#define INACT_THRES_SEC 300
|
||||
|
||||
// threshold of minimum power on which the inverter is marked as inactive
|
||||
#define INACT_PWR_THRESH 3
|
||||
|
||||
// changes the style of "/setup" page, visualized = nicer
|
||||
#define LIVEDATA_VISUALIZED
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
//-------------------------------------
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 4
|
||||
#define VERSION_PATCH 5
|
||||
#define VERSION_PATCH 8
|
||||
|
||||
|
||||
//-------------------------------------
|
||||
|
@ -36,7 +36,7 @@ typedef struct {
|
|||
|
||||
#define INV_ADDR_LEN MAX_NUM_INVERTERS * 8 // uint64_t
|
||||
#define INV_NAME_LEN MAX_NUM_INVERTERS * MAX_NAME_LENGTH // char[]
|
||||
#define INV_TYPE_LEN MAX_NUM_INVERTERS * 1 // uint8_t
|
||||
#define INV_CH_MOD_PWR_LEN MAX_NUM_INVERTERS * 2 * 4 // uint16_t (4 channels)
|
||||
#define INV_INTERVAL_LEN 2 // uint16_t
|
||||
|
||||
#define PINOUT_LEN 3 // 3 pins: CS, CE, IRQ
|
||||
|
@ -68,8 +68,8 @@ typedef struct {
|
|||
|
||||
#define ADDR_INV_ADDR ADDR_RF24_AMP_PWR + RF24_AMP_PWR_LEN
|
||||
#define ADDR_INV_NAME ADDR_INV_ADDR + INV_ADDR_LEN
|
||||
#define ADDR_INV_TYPE ADDR_INV_NAME + INV_NAME_LEN // obsolete
|
||||
#define ADDR_INV_INTERVAL ADDR_INV_TYPE + INV_TYPE_LEN
|
||||
#define ADDR_INV_MOD_PWR ADDR_INV_NAME + INV_NAME_LEN
|
||||
#define ADDR_INV_INTERVAL ADDR_INV_MOD_PWR + INV_CH_MOD_PWR_LEN
|
||||
|
||||
#define ADDR_MQTT_ADDR ADDR_INV_INTERVAL + INV_INTERVAL_LEN
|
||||
#define ADDR_MQTT_USER ADDR_MQTT_ADDR + MQTT_ADDR_LEN
|
||||
|
|
|
@ -47,6 +47,13 @@ class eep {
|
|||
*value |= (EEPROM.read(addr++));
|
||||
}
|
||||
|
||||
void read(uint32_t addr, uint16_t data[], uint16_t length) {
|
||||
for(uint16_t i = 0; i < length; i ++) {
|
||||
*(data) = (EEPROM.read(addr++) << 8);
|
||||
*(data++) |= (EEPROM.read(addr++));
|
||||
}
|
||||
}
|
||||
|
||||
void read(uint32_t addr, uint32_t *value) {
|
||||
*value = (EEPROM.read(addr++) << 24);
|
||||
*value |= (EEPROM.read(addr++) << 16);
|
||||
|
@ -109,6 +116,14 @@ class eep {
|
|||
EEPROM.commit();
|
||||
}
|
||||
|
||||
|
||||
void write(uint32_t addr, uint16_t data[], uint16_t length) {
|
||||
for(uint16_t i = 0; i < length; i ++) {
|
||||
EEPROM.write(addr++, (data[i] >> 8) & 0xff);
|
||||
EEPROM.write(addr++, (data[i] ) & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
void write(uint32_t addr, uint32_t value) {
|
||||
EEPROM.write(addr++, (value >> 24) & 0xff);
|
||||
EEPROM.write(addr++, (value >> 16) & 0xff);
|
||||
|
|
|
@ -18,13 +18,13 @@ const char* const units[] = {"V", "A", "W", "Wh", "kWh", "Hz", "°C", "%"};
|
|||
|
||||
// field types
|
||||
enum {FLD_UDC = 0, FLD_IDC, FLD_PDC, FLD_YD, FLD_YW, FLD_YT,
|
||||
FLD_UAC, FLD_IAC, FLD_PAC, FLD_F, FLD_T, FLD_PCT};
|
||||
FLD_UAC, FLD_IAC, FLD_PAC, FLD_F, FLD_T, FLD_PCT, FLD_EFF, FLD_IRR};
|
||||
const char* const fields[] = {"U_DC", "I_DC", "P_DC", "YieldDay", "YieldWeek", "YieldTotal",
|
||||
"U_AC", "I_AC", "P_AC", "Freq", "Temp", "Pct"};
|
||||
"U_AC", "I_AC", "P_AC", "Freq", "Temp", "Pct", "Effiency", "Irradiation"};
|
||||
|
||||
|
||||
// indices to calculation functions, defined in hmInverter.h
|
||||
enum {CALC_YT_CH0 = 0, CALC_YD_CH0, CALC_UDC_CH};
|
||||
enum {CALC_YT_CH0 = 0, CALC_YD_CH0, CALC_UDC_CH, CALC_PDC_CH0, CALC_EFF_CH0, CALC_IRR_CH};
|
||||
enum {CMD_CALC = 0xffff};
|
||||
|
||||
|
||||
|
@ -58,12 +58,15 @@ const byteAssign_t hm1chAssignment[] = {
|
|||
{ FLD_PDC, UNIT_W, CH1, 6, 2, 10 },
|
||||
{ FLD_YD, UNIT_WH, CH1, 12, 2, 1 },
|
||||
{ FLD_YT, UNIT_KWH, CH1, 8, 4, 1000 },
|
||||
{ FLD_IRR, UNIT_PCT, CH1, CALC_IRR_CH, CH1, CMD_CALC },
|
||||
|
||||
{ FLD_UAC, UNIT_V, CH0, 14, 2, 10 },
|
||||
{ FLD_IAC, UNIT_A, CH0, 22, 2, 100 },
|
||||
{ FLD_PAC, UNIT_W, CH0, 18, 2, 10 },
|
||||
{ FLD_F, UNIT_HZ, CH0, 16, 2, 100 },
|
||||
{ FLD_T, UNIT_C, CH0, 26, 2, 10 }
|
||||
{ FLD_T, UNIT_C, CH0, 26, 2, 10 },
|
||||
{ FLD_PDC, UNIT_W, CH0, CALC_PDC_CH0, 0, CMD_CALC },
|
||||
{ FLD_EFF, UNIT_PCT, CH0, CALC_EFF_CH0, 0, CMD_CALC }
|
||||
};
|
||||
#define HM1CH_LIST_LEN (sizeof(hm1chAssignment) / sizeof(byteAssign_t))
|
||||
|
||||
|
@ -77,12 +80,14 @@ const byteAssign_t hm2chAssignment[] = {
|
|||
{ FLD_PDC, UNIT_W, CH1, 6, 2, 10 },
|
||||
{ FLD_YD, UNIT_WH, CH1, 22, 2, 1 },
|
||||
{ FLD_YT, UNIT_KWH, CH1, 14, 4, 1000 },
|
||||
{ FLD_IRR, UNIT_PCT, CH1, CALC_IRR_CH, CH1, CMD_CALC },
|
||||
|
||||
{ FLD_UDC, UNIT_V, CH2, 8, 2, 10 },
|
||||
{ FLD_IDC, UNIT_A, CH2, 10, 2, 100 },
|
||||
{ FLD_PDC, UNIT_W, CH2, 12, 2, 10 },
|
||||
{ FLD_YD, UNIT_WH, CH2, 24, 2, 1 },
|
||||
{ FLD_YT, UNIT_KWH, CH2, 18, 4, 1000 },
|
||||
{ FLD_IRR, UNIT_PCT, CH2, CALC_IRR_CH, CH2, CMD_CALC },
|
||||
|
||||
{ FLD_UAC, UNIT_V, CH0, 26, 2, 10 },
|
||||
{ FLD_IAC, UNIT_A, CH0, 34, 2, 100 },
|
||||
|
@ -90,7 +95,9 @@ const byteAssign_t hm2chAssignment[] = {
|
|||
{ FLD_F, UNIT_HZ, CH0, 28, 2, 100 },
|
||||
{ FLD_T, UNIT_C, CH0, 38, 2, 10 },
|
||||
{ FLD_YD, UNIT_WH, CH0, CALC_YD_CH0, 0, CMD_CALC },
|
||||
{ FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC }
|
||||
{ FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC },
|
||||
{ FLD_PDC, UNIT_W, CH0, CALC_PDC_CH0, 0, CMD_CALC },
|
||||
{ FLD_EFF, UNIT_PCT, CH0, CALC_EFF_CH0, 0, CMD_CALC }
|
||||
|
||||
};
|
||||
#define HM2CH_LIST_LEN (sizeof(hm2chAssignment) / sizeof(byteAssign_t))
|
||||
|
@ -105,24 +112,28 @@ const byteAssign_t hm4chAssignment[] = {
|
|||
{ FLD_PDC, UNIT_W, CH1, 8, 2, 10 },
|
||||
{ FLD_YD, UNIT_WH, CH1, 20, 2, 1 },
|
||||
{ FLD_YT, UNIT_KWH, CH1, 12, 4, 1000 },
|
||||
{ FLD_IRR, UNIT_PCT, CH1, CALC_IRR_CH, CH1, CMD_CALC },
|
||||
|
||||
{ FLD_UDC, UNIT_V, CH2, CALC_UDC_CH, CH1, CMD_CALC },
|
||||
{ FLD_IDC, UNIT_A, CH2, 6, 2, 100 },
|
||||
{ FLD_PDC, UNIT_W, CH2, 10, 2, 10 },
|
||||
{ FLD_YD, UNIT_WH, CH2, 22, 2, 1 },
|
||||
{ FLD_YT, UNIT_KWH, CH2, 16, 4, 1000 },
|
||||
{ FLD_IRR, UNIT_PCT, CH2, CALC_IRR_CH, CH2, CMD_CALC },
|
||||
|
||||
{ FLD_UDC, UNIT_V, CH3, 24, 2, 10 },
|
||||
{ FLD_IDC, UNIT_A, CH3, 26, 2, 100 },
|
||||
{ FLD_PDC, UNIT_W, CH3, 30, 2, 10 },
|
||||
{ FLD_YD, UNIT_WH, CH3, 42, 2, 1 },
|
||||
{ FLD_YT, UNIT_KWH, CH3, 34, 4, 1000 },
|
||||
{ FLD_IRR, UNIT_PCT, CH3, CALC_IRR_CH, CH3, CMD_CALC },
|
||||
|
||||
{ FLD_UDC, UNIT_V, CH4, CALC_UDC_CH, CH3, CMD_CALC },
|
||||
{ FLD_IDC, UNIT_A, CH4, 28, 2, 100 },
|
||||
{ FLD_PDC, UNIT_W, CH4, 32, 2, 10 },
|
||||
{ FLD_YD, UNIT_WH, CH4, 44, 2, 1 },
|
||||
{ FLD_YT, UNIT_KWH, CH4, 38, 4, 1000 },
|
||||
{ FLD_IRR, UNIT_PCT, CH4, CALC_IRR_CH, CH4, CMD_CALC },
|
||||
|
||||
{ FLD_UAC, UNIT_V, CH0, 46, 2, 10 },
|
||||
{ FLD_IAC, UNIT_A, CH0, 54, 2, 100 },
|
||||
|
@ -131,7 +142,9 @@ const byteAssign_t hm4chAssignment[] = {
|
|||
{ FLD_PCT, UNIT_PCT, CH0, 56, 2, 10 },
|
||||
{ FLD_T, UNIT_C, CH0, 58, 2, 10 },
|
||||
{ FLD_YD, UNIT_WH, CH0, CALC_YD_CH0, 0, CMD_CALC },
|
||||
{ FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC }
|
||||
{ FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC },
|
||||
{ FLD_PDC, UNIT_W, CH0, CALC_PDC_CH0, 0, CMD_CALC },
|
||||
{ FLD_EFF, UNIT_PCT, CH0, CALC_EFF_CH0, 0, CMD_CALC }
|
||||
};
|
||||
#define HM4CH_LIST_LEN (sizeof(hm4chAssignment) / sizeof(byteAssign_t))
|
||||
|
||||
|
|
|
@ -26,6 +26,15 @@ static T calcYieldDayCh0(Inverter<> *iv, uint8_t arg0);
|
|||
template<class T=float>
|
||||
static T calcUdcCh(Inverter<> *iv, uint8_t arg0);
|
||||
|
||||
template<class T=float>
|
||||
static T calcPowerDcCh0(Inverter<> *iv, uint8_t arg0);
|
||||
|
||||
template<class T=float>
|
||||
static T calcEffiencyCh0(Inverter<> *iv, uint8_t arg0);
|
||||
|
||||
template<class T=float>
|
||||
static T calcIrradiation(Inverter<> *iv, uint8_t arg0);
|
||||
|
||||
template<class T=float>
|
||||
using func_t = T (Inverter<> *, uint8_t);
|
||||
|
||||
|
@ -41,7 +50,10 @@ template<class T=float>
|
|||
const calcFunc_t<T> calcFunctions[] = {
|
||||
{ CALC_YT_CH0, &calcYieldTotalCh0 },
|
||||
{ CALC_YD_CH0, &calcYieldDayCh0 },
|
||||
{ CALC_UDC_CH, &calcUdcCh }
|
||||
{ CALC_UDC_CH, &calcUdcCh },
|
||||
{ CALC_PDC_CH0, &calcPowerDcCh0 },
|
||||
{ CALC_EFF_CH0, &calcEffiencyCh0 },
|
||||
{ CALC_IRR_CH, &calcIrradiation }
|
||||
};
|
||||
|
||||
|
||||
|
@ -58,6 +70,7 @@ class Inverter {
|
|||
uint8_t channels; // number of PV channels (1-4)
|
||||
uint32_t ts; // timestamp of last received payload
|
||||
RECORDTYPE *record; // pointer for values
|
||||
uint16_t chMaxPwr[4]; // maximum power of the modules (Wp)
|
||||
|
||||
Inverter() {
|
||||
ts = 0;
|
||||
|
@ -124,6 +137,22 @@ class Inverter {
|
|||
}
|
||||
}
|
||||
|
||||
bool isAvailable(uint32_t timestamp) {
|
||||
return ((timestamp - ts) < INACT_THRES_SEC);
|
||||
}
|
||||
|
||||
bool isProducing(uint32_t timestamp) {
|
||||
if(isAvailable(timestamp)) {
|
||||
uint8_t pos = getPosByChFld(CH0, FLD_PAC);
|
||||
return (getValue(pos) > INACT_PWR_THRESH);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t getLastTs(void) {
|
||||
return ts;
|
||||
}
|
||||
|
||||
private:
|
||||
void toRadioId(void) {
|
||||
radioId.u64 = 0ULL;
|
||||
|
@ -203,5 +232,44 @@ static T calcUdcCh(Inverter<> *iv, uint8_t arg0) {
|
|||
return 0.0;
|
||||
}
|
||||
|
||||
template<class T=float>
|
||||
static T calcPowerDcCh0(Inverter<> *iv, uint8_t arg0) {
|
||||
if(NULL != iv) {
|
||||
T dcPower = 0;
|
||||
for(uint8_t i = 1; i <= iv->channels; i++) {
|
||||
uint8_t pos = iv->getPosByChFld(i, FLD_PDC);
|
||||
dcPower += iv->getValue(pos);
|
||||
}
|
||||
return dcPower;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
template<class T=float>
|
||||
static T calcEffiencyCh0(Inverter<> *iv, uint8_t arg0) {
|
||||
if(NULL != iv) {
|
||||
uint8_t pos = iv->getPosByChFld(CH0, FLD_PAC);
|
||||
T acPower = iv->getValue(pos);
|
||||
T dcPower = 0;
|
||||
for(uint8_t i = 1; i <= iv->channels; i++) {
|
||||
pos = iv->getPosByChFld(i, FLD_PDC);
|
||||
dcPower += iv->getValue(pos);
|
||||
}
|
||||
if(dcPower > 0)
|
||||
return acPower / dcPower * 100.0f;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
template<class T=float>
|
||||
static T calcIrradiation(Inverter<> *iv, uint8_t arg0) {
|
||||
// arg0 = channel
|
||||
if(NULL != iv) {
|
||||
uint8_t pos = iv->getPosByChFld(arg0, FLD_PDC);
|
||||
if(iv->chMaxPwr[arg0-1] > 0)
|
||||
return iv->getValue(pos) / iv->chMaxPwr[arg0-1] * 100.0f;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
#endif /*__HM_INVERTER_H__*/
|
||||
|
|
|
@ -27,7 +27,7 @@ class HmSystem {
|
|||
Radio.setup(&BufCtrl);
|
||||
}
|
||||
|
||||
INVERTERTYPE *addInverter(const char *name, uint64_t serial) {
|
||||
INVERTERTYPE *addInverter(const char *name, uint64_t serial, uint16_t chMaxPwr[]) {
|
||||
if(MAX_INVERTER <= mNumInv) {
|
||||
DPRINT("max number of inverters reached!");
|
||||
return NULL;
|
||||
|
@ -35,6 +35,7 @@ class HmSystem {
|
|||
INVERTERTYPE *p = &mInverter[mNumInv];
|
||||
p->id = mNumInv;
|
||||
p->serial.u64 = serial;
|
||||
memcpy(p->chMaxPwr, chMaxPwr, (4*2));
|
||||
DPRINT("SERIAL: " + String(p->serial.b[5], HEX));
|
||||
DPRINTLN(" " + String(p->serial.b[4], HEX));
|
||||
if(p->serial.b[5] == 0x11) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#ifndef __STYLE_CSS_H__
|
||||
#define __STYLE_CSS_H__
|
||||
const char style_css[] PROGMEM = "h1 {margin:0;padding:20pt;font-size:22pt;color:#fff;background-color:#006ec0;display:block;text-transform:uppercase;}html, body {font-family:Arial;margin:0;padding:0;}p {text-align:justify;font-size:13pt;}.des {margin-top:35px;font-size:13pt;color:#006ec0;}.subdes {font-size:12pt;color:#006ec0;margin-left:7px;}a:link, a:visited {text-decoration:none;font-size:13pt;color:#006ec0;}a:hover, a:focus {color:#f00;}a.erase {background-color:#006ec0;color:#fff;padding:7px;display:inline-block;margin-top:30px;float:right;}#content {padding:15px 15px 60px 15px;}#footer {position:fixed;bottom:0px;height:45px;background-color:#006ec0;width:100%;border-top:5px solid #fff;}#footer p, #footer a {color:#fff;padding:0 7px 0 7px;font-size:10pt !important;}div.content {background-color:#fff;padding-bottom:65px;overflow:auto;}input, select {padding:7px;font-size:13pt;}input.text, select {width:70%;box-sizing:border-box;margin-bottom:10px;border:1px solid #ccc;}input.btn {background-color:#006ec0;color:#fff;border:0px;float:right;margin:10px 0 30px;text-transform:uppercase;}input.cb {margin-bottom:20px;}label {width:20%;display:inline-block;font-size:12pt;padding-right:10px;margin-left:10px;}.left {float:left;}.right {float:right;}div.ch-iv {width:100%;background-color:#32b004;display:inline-block;margin-bottom:15px;padding-bottom:20px;overflow:auto;}div.ch {width:220px;min-height:350px;background-color:#006ec0;display:inline-block;margin:0 10px 15px 10px;overflow:auto;padding-bottom:20px;}div.ch .value, div.ch .info, div.ch .head, div.ch-iv .value, div.ch-iv .info, div.ch-iv .head {color:#fff;display:block;width:100%;text-align:center;}.subgrp {float:left;width:220px;}div.ch .unit, div.ch-iv .unit {font-size:19px;margin-left:10px;}div.ch .value, div.ch-iv .value {margin-top:20px;font-size:24px;}div.ch .info, div.ch-iv .info {margin-top:3px;font-size:10px;}div.ch .head {background-color:#003c80;padding:10px 0 10px 0;}div.ch-iv .head {background-color:#1c6800;padding:10px 0 10px 0;}div.iv {max-width:960px;margin-bottom:40px;}div.ts {font-size:13px;background-color:#ddd;border-top:7px solid #999;padding:7px;}#note {margin:50px 10px 10px 10px;padding-top:10px;width:100%;border-top:1px solid #bbb;}@media(max-width:500px) {div.ch .unit, div.ch-iv .unit {font-size:18px;}div.ch {width:170px;min-height:100px;}.subgrp {width:180px;}}";
|
||||
const char style_css[] PROGMEM = "h1 {margin:0;padding:20pt;font-size:22pt;color:#fff;background-color:#006ec0;display:block;text-transform:uppercase;}html, body {font-family:Arial;margin:0;padding:0;}p {text-align:justify;font-size:13pt;}.des {margin-top:35px;font-size:13pt;color:#006ec0;}.subdes {font-size:12pt;color:#006ec0;margin-left:7px;}a:link, a:visited {text-decoration:none;font-size:13pt;color:#006ec0;}a:hover, a:focus {color:#f00;}a.erase {background-color:#006ec0;color:#fff;padding:7px;display:inline-block;margin-top:30px;float:right;}#content {padding:15px 15px 60px 15px;}#footer {position:fixed;bottom:0px;height:45px;background-color:#006ec0;width:100%;border-top:5px solid #fff;}#footer p, #footer a {color:#fff;padding:0 7px 0 7px;font-size:10pt !important;}div.content {background-color:#fff;padding-bottom:65px;overflow:auto;}input, select {padding:7px;font-size:13pt;}input.text, select {width:70%;box-sizing:border-box;margin-bottom:10px;border:1px solid #ccc;}input.sh {max-width:100px !important;margin-right:10px;}input.btn {background-color:#006ec0;color:#fff;border:0px;float:right;margin:10px 0 30px;text-transform:uppercase;}input.cb {margin-bottom:20px;}label {width:20%;display:inline-block;font-size:12pt;padding-right:10px;margin-left:10px;}.left {float:left;}.right {float:right;}div.ch-iv {width:100%;background-color:#32b004;display:inline-block;margin-bottom:15px;padding-bottom:20px;overflow:auto;}div.ch {width:220px;min-height:350px;background-color:#006ec0;display:inline-block;margin:0 10px 15px 10px;overflow:auto;padding-bottom:20px;}div.ch .value, div.ch .info, div.ch .head, div.ch-iv .value, div.ch-iv .info, div.ch-iv .head {color:#fff;display:block;width:100%;text-align:center;}.subgrp {float:left;width:220px;}div.ch .unit, div.ch-iv .unit {font-size:19px;margin-left:10px;}div.ch .value, div.ch-iv .value {margin-top:20px;font-size:24px;}div.ch .info, div.ch-iv .info {margin-top:3px;font-size:10px;}div.ch .head {background-color:#003c80;padding:10px 0 10px 0;}div.ch-iv .head {background-color:#1c6800;padding:10px 0 10px 0;}div.iv {max-width:960px;margin-bottom:40px;}div.ts {font-size:13px;background-color:#ddd;border-top:7px solid #999;padding:7px;}#note {margin:50px 10px 10px 10px;padding-top:10px;width:100%;border-top:1px solid #bbb;}@media(max-width:500px) {div.ch .unit, div.ch-iv .unit {font-size:18px;}div.ch {width:170px;min-height:100px;}.subgrp {width:180px;}}";
|
||||
#endif /*__STYLE_CSS_H__*/
|
||||
|
|
|
@ -88,6 +88,11 @@ input.text, select {
|
|||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
input.sh {
|
||||
max-width: 100px !important;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
input.btn {
|
||||
background-color: #006ec0;
|
||||
color: #fff;
|
||||
|
|
|
@ -59,8 +59,8 @@ class mqtt {
|
|||
}
|
||||
|
||||
void loop() {
|
||||
//if(!mClient->connected())
|
||||
// reconnect();
|
||||
if(!mClient->connected())
|
||||
reconnect();
|
||||
mClient->loop();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue