Browse Source

0.8.7

* fix ESP8266 inverter settings #1226
* send radio statistics via MqTT #1227
* made night communication inverter depended
* added option to prevent adding values of inverter to total values (MqTT only) #1199
pull/1234/head
lumapu 1 year ago
parent
commit
1bc3a0f06f
  1. 6
      src/CHANGES.md
  2. 92
      src/app.cpp
  3. 1
      src/app.h
  4. 26
      src/config/settings.h
  5. 2
      src/defines.h
  6. 2
      src/hm/hmInverter.h
  7. 13
      src/publisher/pubMqtt.h
  8. 2
      src/publisher/pubMqttDefs.h
  9. 68
      src/publisher/pubMqttIvData.h
  10. 80
      src/web/RestApi.h
  11. 90
      src/web/html/setup.html
  12. 2
      src/web/web.h

6
src/CHANGES.md

@ -1,5 +1,11 @@
# Development Changes
## 0.8.7 - 2023-11-13
* fix ESP8266 inverter settings #1226
* send radio statistics via MqTT #1227
* made night communication inverter depended
* added option to prevent adding values of inverter to total values (MqTT only) #1199
## 0.8.6 - 2023-11-12
* merged PR #1225
* improved heuristics (prevent update of statitistic during testing)

92
src/app.cpp

@ -4,9 +4,7 @@
//-----------------------------------------------------------------------------
#include <ArduinoJson.h>
#include "app.h"
#include "utils/sun.h"
@ -239,43 +237,50 @@ void app::tickCalcSunrise(void) {
//-----------------------------------------------------------------------------
void app::tickIVCommunication(void) {
mIVCommunicationOn = !mConfig->sun.disNightCom; // if sun.disNightCom is false, communication is always on
if (!mIVCommunicationOn) { // inverter communication only during the day
uint32_t nxtTrig;
if (mTimestamp < (mSunrise - mConfig->sun.offsetSec)) { // current time is before communication start, set next trigger to communication start
nxtTrig = mSunrise - mConfig->sun.offsetSec;
} else {
if (mTimestamp >= (mSunset + mConfig->sun.offsetSec)) { // current time is past communication stop, nothing to do. Next update will be done at midnight by tickCalcSunrise
nxtTrig = 0;
} else { // current time lies within communication start/stop time, set next trigger to communication stop
mIVCommunicationOn = true;
nxtTrig = mSunset + mConfig->sun.offsetSec;
bool restartTick = false;
bool zeroValues = false;
uint32_t nxtTrig = 0;
Inverter<> *iv;
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) {
iv = mSys.getInverterByPos(i);
if(NULL == iv)
continue;
iv->commEnabled = !iv->config->disNightCom; // if sun.disNightCom is false, communication is always on
if (!iv->commEnabled) { // inverter communication only during the day
if (mTimestamp < (mSunrise - mConfig->sun.offsetSec)) { // current time is before communication start, set next trigger to communication start
nxtTrig = mSunrise - mConfig->sun.offsetSec;
} else {
if (mTimestamp >= (mSunset + mConfig->sun.offsetSec)) { // current time is past communication stop, nothing to do. Next update will be done at midnight by tickCalcSunrise
nxtTrig = 0;
} else { // current time lies within communication start/stop time, set next trigger to communication stop
iv->commEnabled = true;
nxtTrig = mSunset + mConfig->sun.offsetSec;
}
}
if (nxtTrig != 0)
restartTick = true;
}
if (nxtTrig != 0)
onceAt(std::bind(&app::tickIVCommunication, this), nxtTrig, "ivCom");
if ((!iv->commEnabled) && (mConfig->inst.rstValsCommStop))
zeroValues = true;
}
tickComm();
if(restartTick) // at least one inverter
onceAt(std::bind(&app::tickIVCommunication, this), nxtTrig, "ivCom");
if (zeroValues) // at least one inverter
once(std::bind(&app::tickZeroValues, this), mConfig->nrf.sendInterval, "tZero");
}
//-----------------------------------------------------------------------------
void app::tickSun(void) {
// only used and enabled by MQTT (see setup())
if (!mMqtt.tickerSun(mSunrise, mSunset, mConfig->sun.offsetSec, mConfig->sun.disNightCom))
if (!mMqtt.tickerSun(mSunrise, mSunset, mConfig->sun.offsetSec))
once(std::bind(&app::tickSun, this), 1, "mqSun"); // MQTT not connected, retry
}
//-----------------------------------------------------------------------------
void app::tickComm(void) {
if ((!mIVCommunicationOn) && (mConfig->inst.rstValsCommStop))
once(std::bind(&app::tickZeroValues, this), mConfig->nrf.sendInterval, "tZero");
if (mMqttEnabled) {
if (!mMqtt.tickerComm(!mIVCommunicationOn))
once(std::bind(&app::tickComm, this), 5, "mqCom"); // MQTT not connected, retry after 5s
}
}
//-----------------------------------------------------------------------------
void app::tickZeroValues(void) {
zeroIvValues(!CHECK_AVAIL, SKIP_YIELD_DAY);
@ -325,22 +330,24 @@ void app::tickMidnight(void) {
//-----------------------------------------------------------------------------
void app::tickSend(void) {
if(!mIVCommunicationOn) {
DPRINTLN(DBG_WARN, F("Time not set or it is night time, therefore no communication to the inverter!"));
return;
}
for (uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
Inverter<> *iv = mSys.getInverterByPos(i);
if(NULL != iv) {
if(iv->config->enabled) {
iv->tickSend([this, iv](uint8_t cmd, bool isDevControl) {
if(isDevControl)
mCommunication.addImportant(iv, cmd);
else
mCommunication.add(iv, cmd);
});
if(NULL == iv)
continue;
if(iv->config->enabled) {
if(!iv->commEnabled) {
DPRINT_IVID(DBG_INFO, iv->id);
DBGPRINTLN(F("no communication to the inverter (night time)"));
continue;
}
iv->tickSend([this, iv](uint8_t cmd, bool isDevControl) {
if(isDevControl)
mCommunication.addImportant(iv, cmd);
else
mCommunication.add(iv, cmd);
});
}
}
@ -358,6 +365,8 @@ void app:: zeroIvValues(bool checkAvail, bool skipYieldDay) {
continue; // skip to next inverter
if (!iv->config->enabled)
continue; // skip to next inverter
if (iv->commEnabled)
continue; // skip to next inverter
if (checkAvail) {
if (!iv->isAvailable())
@ -415,7 +424,6 @@ void app::resetSystem(void) {
mSendLastIvId = 0;
mShowRebootRequest = false;
mIVCommunicationOn = true;
mSavePending = false;
mSaveReboot = false;

1
src/app.h

@ -307,7 +307,6 @@ class app : public IApp, public ah::Scheduler {
Communication mCommunication;
bool mShowRebootRequest;
bool mIVCommunicationOn;
#if defined(ETHERNET)
ahoyeth mEth;

26
src/config/settings.h

@ -30,7 +30,7 @@
* https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html#flash-layout
* */
#define CONFIG_VERSION 1
#define CONFIG_VERSION 2
#define PROT_MASK_INDEX 0x0001
@ -110,7 +110,6 @@ typedef struct {
typedef struct {
float lat;
float lon;
bool disNightCom; // disable night communication
uint16_t offsetSec;
} cfgSun_t;
@ -145,6 +144,8 @@ typedef struct {
char chName[6][MAX_NAME_LENGTH];
uint8_t frequency;
uint8_t powerLevel;
bool disNightCom; // disable night communication
bool add2Total; // add values to total values - useful if one inverter is on battery to turn off
} cfgIv_t;
typedef struct {
@ -420,7 +421,6 @@ class settings {
mCfg.sun.lat = 0.0;
mCfg.sun.lon = 0.0;
mCfg.sun.disNightCom = false;
mCfg.sun.offsetSec = 0;
mCfg.serial.interval = SERIAL_INTERVAL;
@ -442,8 +442,10 @@ class settings {
mCfg.inst.yieldEffiency = 0.955f;
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
mCfg.inst.iv[i].powerLevel = 0xff; // impossible high value
mCfg.inst.iv[i].frequency = 0x12; // 863MHz (minimum allowed frequency)
mCfg.inst.iv[i].powerLevel = 0xff; // impossible high value
mCfg.inst.iv[i].frequency = 0x12; // 863MHz (minimum allowed frequency)
mCfg.inst.iv[i].disNightCom = false;
mCfg.inst.iv[i].add2Total = true;
}
mCfg.led.led0 = DEF_LED0;
@ -465,11 +467,15 @@ class settings {
}
void loadAddedDefaults() {
if(0 < mCfg.configVersion) {
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
if(mCfg.configVersion < 1) {
mCfg.inst.iv[i].powerLevel = 0xff; // impossible high value
mCfg.inst.iv[i].frequency = 0x0; // 860MHz (backward compatibility)
}
if(mCfg.configVersion < 2) {
mCfg.inst.iv[i].disNightCom = false;
mCfg.inst.iv[i].add2Total = true;
}
}
}
@ -601,12 +607,10 @@ class settings {
if(set) {
obj[F("lat")] = mCfg.sun.lat;
obj[F("lon")] = mCfg.sun.lon;
obj[F("dis")] = mCfg.sun.disNightCom;
obj[F("offs")] = mCfg.sun.offsetSec;
} else {
getVal<float>(obj, F("lat"), &mCfg.sun.lat);
getVal<float>(obj, F("lon"), &mCfg.sun.lon);
getVal<bool>(obj, F("dis"), &mCfg.sun.disNightCom);
getVal<uint16_t>(obj, F("offs"), &mCfg.sun.offsetSec);
}
}
@ -734,6 +738,8 @@ class settings {
obj[F("sn")] = cfg->serial.u64;
obj[F("freq")] = cfg->frequency;
obj[F("pa")] = cfg->powerLevel;
obj[F("dis")] = cfg->disNightCom;
obj[F("add")] = cfg->add2Total;
for(uint8_t i = 0; i < 6; i++) {
obj[F("yield")][i] = cfg->yieldCor[i];
obj[F("pwr")][i] = cfg->chMaxPwr[i];
@ -745,6 +751,8 @@ class settings {
getVal<uint64_t>(obj, F("sn"), &cfg->serial.u64);
getVal<uint8_t>(obj, F("freq"), &cfg->frequency);
getVal<uint8_t>(obj, F("pa"), &cfg->powerLevel);
getVal<bool>(obj, F("dis"), &cfg->disNightCom);
getVal<bool>(obj, F("add"), &cfg->add2Total);
uint8_t size = 4;
if(obj.containsKey(F("pwr")))
size = obj[F("pwr")].size();

2
src/defines.h

@ -13,7 +13,7 @@
//-------------------------------------
#define VERSION_MAJOR 0
#define VERSION_MINOR 8
#define VERSION_PATCH 5
#define VERSION_PATCH 7
//-------------------------------------
typedef struct {

2
src/hm/hmInverter.h

@ -130,6 +130,7 @@ class Inverter {
int8_t txRfQuality[5]; // heuristics tx quality (check 'Heuristics.h')
uint8_t txRfChId; // RF TX channel id
uint8_t curCmtFreq; // current used CMT frequency, used to check if freq. was changed during runtime
bool commEnabled; // 'pause night communication' sets this field to false
static uint32_t *timestamp; // system timestamp
static cfgInst_t *generalConfig; // general inverter configuration from setup
@ -150,6 +151,7 @@ class Inverter {
alarmLastId = 0;
rssi = -127;
radio = NULL;
commEnabled = true;
memset(&radioStatistics, 0, sizeof(statistics_t));
memset(txRfQuality, -6, 5);

13
src/publisher/pubMqtt.h

@ -134,7 +134,7 @@ class PubMqtt {
#endif
}
bool tickerSun(uint32_t sunrise, uint32_t sunset, uint32_t offs, bool disNightCom) {
bool tickerSun(uint32_t sunrise, uint32_t sunset, uint32_t offs) {
if (!mClient.connected())
return false;
@ -142,7 +142,16 @@ class PubMqtt {
publish(subtopics[MQTT_SUNSET], String(sunset).c_str(), true);
publish(subtopics[MQTT_COMM_START], String(sunrise - offs).c_str(), true);
publish(subtopics[MQTT_COMM_STOP], String(sunset + offs).c_str(), true);
publish(subtopics[MQTT_DIS_NIGHT_COMM], ((disNightCom) ? dict[STR_TRUE] : dict[STR_FALSE]), true);
Inverter<> *iv;
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
iv = mSys->getInverterByPos(i);
if(NULL == iv)
continue;
snprintf(mSubTopic, 32 + MAX_NAME_LENGTH, "%s/dis_night_comm", iv->config->name);
publish(mSubTopic, ((iv->commEnabled) ? dict[STR_TRUE] : dict[STR_FALSE]), true);
}
return true;
}

2
src/publisher/pubMqttDefs.h

@ -48,7 +48,6 @@ enum {
MQTT_SUNSET,
MQTT_COMM_START,
MQTT_COMM_STOP,
MQTT_DIS_NIGHT_COMM,
MQTT_COMM_DISABLED,
MQTT_COMM_DIS_TS,
MQTT_VERSION,
@ -69,7 +68,6 @@ const char* const subtopics[] PROGMEM = {
"sunset",
"comm_start",
"comm_stop",
"dis_night_comm",
"comm_disabled",
"comm_dis_ts",
"version",

68
src/publisher/pubMqttIvData.h

@ -31,11 +31,11 @@ class PubMqttIvData {
memset(mIvLastRTRpub, 0, MAX_NUM_INVERTERS * sizeof(uint32_t));
mRTRDataHasBeenSent = false;
mTable[IDLE] = &PubMqttIvData::stateIdle;
mTable[START] = &PubMqttIvData::stateStart;
mTable[FIND_NXT_IV] = &PubMqttIvData::stateFindNxtIv;
mTable[SEND_DATA] = &PubMqttIvData::stateSend;
mTable[SEND_TOTALS] = &PubMqttIvData::stateSendTotals;
mTable[IDLE] = &PubMqttIvData::stateIdle;
mTable[START] = &PubMqttIvData::stateStart;
mTable[FIND_NXT_IV] = &PubMqttIvData::stateFindNxtIv;
mTable[SEND_DATA] = &PubMqttIvData::stateSend;
mTable[SEND_TOTALS] = &PubMqttIvData::stateSendTotals;
}
void loop() {
@ -142,25 +142,27 @@ class PubMqttIvData {
// calculate total values for RealTimeRunData_Debug
if (CH0 == rec->assign[mPos].ch) {
if(mIv->status > InverterStatus::STARTING) {
mTotalFound = true;
switch (rec->assign[mPos].fieldId) {
case FLD_PAC:
mTotal[0] += mIv->getValue(mPos, rec);
break;
case FLD_YT:
mTotal[1] += mIv->getValue(mPos, rec);
break;
case FLD_YD: {
float val = mIv->getValue(mPos, rec);
if(0 == val) // inverter restarted during day
mSendTotalYd = false;
else
mTotal[2] += val;
break;
if(mIv->config->add2Total) {
mTotalFound = true;
switch (rec->assign[mPos].fieldId) {
case FLD_PAC:
mTotal[0] += mIv->getValue(mPos, rec);
break;
case FLD_YT:
mTotal[1] += mIv->getValue(mPos, rec);
break;
case FLD_YD: {
float val = mIv->getValue(mPos, rec);
if(0 == val) // inverter restarted during day
mSendTotalYd = false;
else
mTotal[2] += val;
break;
}
case FLD_PDC:
mTotal[3] += mIv->getValue(mPos, rec);
break;
}
case FLD_PDC:
mTotal[3] += mIv->getValue(mPos, rec);
break;
}
} else
mAllTotalFound = false;
@ -178,12 +180,25 @@ class PubMqttIvData {
mPublish(mSubTopic, mVal, retained, qos);
}
mPos++;
} else
} else {
sendRadioStat(rec->length);
mState = FIND_NXT_IV;
}
} else
mState = FIND_NXT_IV;
}
inline void sendRadioStat(uint8_t start) {
snprintf(mSubTopic, 32 + MAX_NAME_LENGTH, "%s/radio_stat", mIv->config->name);
snprintf(mVal, 100, "{\"tx\":%d,\"success\":%d,\"fail\":%d,\"no_answer\":%d,\"retransmits\":%d}",
mIv->radioStatistics.txCnt,
mIv->radioStatistics.rxSuccess,
mIv->radioStatistics.rxFail,
mIv->radioStatistics.rxFailNoAnser,
mIv->radioStatistics.retransmits);
mPublish(mSubTopic, mVal, false, QOS_0);
}
void stateSendTotals() {
uint8_t fieldId;
mRTRDataHasBeenSent = true;
@ -221,7 +236,8 @@ class PubMqttIvData {
} else {
mSendList->pop();
mZeroValues = false;
mState = START;
mPos = 0;
mState = IDLE;
}
}
@ -242,7 +258,7 @@ class PubMqttIvData {
bool mRTRDataHasBeenSent;
char mSubTopic[32 + MAX_NAME_LENGTH + 1];
char mVal[40];
char mVal[100];
bool mZeroValues; // makes sure that yield day is sent even if no inverter is online
std::queue<sendListCmdIv> *mSendList;

80
src/web/RestApi.h

@ -339,27 +339,30 @@ class RestApi {
Inverter<> *iv;
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) {
iv = mSys->getInverterByPos(i);
if(NULL != iv) {
JsonObject obj2 = invArr.createNestedObject();
obj2[F("enabled")] = (bool)iv->config->enabled;
obj2[F("id")] = i;
obj2[F("name")] = String(iv->config->name);
obj2[F("serial")] = String(iv->config->serial.u64, HEX);
obj2[F("channels")] = iv->channels;
obj2[F("freq")] = iv->config->frequency;
if(0xff == iv->config->powerLevel) {
if((IV_HMT == iv->ivGen) || (IV_HMS == iv->ivGen))
obj2[F("pa")] = 30; // 20dBm
else
obj2[F("pa")] = 1; // low
} else
obj2[F("pa")] = iv->config->powerLevel;
for(uint8_t j = 0; j < iv->channels; j ++) {
obj2[F("ch_yield_cor")][j] = (double)iv->config->yieldCor[j];
obj2[F("ch_name")][j] = iv->config->chName[j];
obj2[F("ch_max_pwr")][j] = iv->config->chMaxPwr[j];
}
if(NULL == iv)
continue;
JsonObject obj2 = invArr.createNestedObject();
obj2[F("enabled")] = (bool)iv->config->enabled;
obj2[F("id")] = i;
obj2[F("name")] = String(iv->config->name);
obj2[F("serial")] = String(iv->config->serial.u64, HEX);
obj2[F("channels")] = iv->channels;
obj2[F("freq")] = iv->config->frequency;
obj2[F("disnightcom")] = (bool)iv->config->disNightCom;
obj2[F("add2total")] = (bool)iv->config->add2Total;
if(0xff == iv->config->powerLevel) {
if((IV_HMT == iv->ivGen) || (IV_HMS == iv->ivGen))
obj2[F("pa")] = 30; // 20dBm
else
obj2[F("pa")] = 1; // low
} else
obj2[F("pa")] = iv->config->powerLevel;
for(uint8_t j = 0; j < iv->channels; j ++) {
obj2[F("ch_yield_cor")][j] = (double)iv->config->yieldCor[j];
obj2[F("ch_name")][j] = iv->config->chName[j];
obj2[F("ch_max_pwr")][j] = iv->config->chMaxPwr[j];
}
}
obj[F("interval")] = String(mConfig->nrf.sendInterval);
@ -501,7 +504,6 @@ class RestApi {
void getSun(JsonObject obj) {
obj[F("lat")] = mConfig->sun.lat ? String(mConfig->sun.lat, 5) : "";
obj[F("lon")] = mConfig->sun.lat ? String(mConfig->sun.lon, 5) : "";
obj[F("disnightcom")] = mConfig->sun.disNightCom;
obj[F("offs")] = mConfig->sun.offsetSec;
}
@ -583,24 +585,28 @@ class RestApi {
obj[F("ts_sunrise")] = mApp->getSunrise();
obj[F("ts_sunset")] = mApp->getSunset();
obj[F("ts_offset")] = mConfig->sun.offsetSec;
obj[F("disNightComm")] = mConfig->sun.disNightCom;
JsonArray inv = obj.createNestedArray(F("inverter"));
Inverter<> *iv;
bool disNightCom = false;
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) {
iv = mSys->getInverterByPos(i);
if(NULL != iv) {
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
JsonObject invObj = inv.createNestedObject();
invObj[F("enabled")] = (bool)iv->config->enabled;
invObj[F("id")] = i;
invObj[F("name")] = String(iv->config->name);
invObj[F("cur_pwr")] = ah::round3(iv->getChannelFieldValue(CH0, FLD_PAC, rec));
invObj[F("is_avail")] = iv->isAvailable();
invObj[F("is_producing")] = iv->isProducing();
invObj[F("ts_last_success")] = iv->getLastTs(rec);
}
if(NULL == iv)
continue;
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
JsonObject invObj = inv.createNestedObject();
invObj[F("enabled")] = (bool)iv->config->enabled;
invObj[F("id")] = i;
invObj[F("name")] = String(iv->config->name);
invObj[F("cur_pwr")] = ah::round3(iv->getChannelFieldValue(CH0, FLD_PAC, rec));
invObj[F("is_avail")] = iv->isAvailable();
invObj[F("is_producing")] = iv->isProducing();
invObj[F("ts_last_success")] = iv->getLastTs(rec);
if(iv->config->disNightCom)
disNightCom = true;
}
obj[F("disNightComm")] = disNightCom;
JsonArray warn = obj.createNestedArray(F("warnings"));
if(!mRadioNrf->isChipConnected() && mConfig->nrf.enabled)
@ -729,8 +735,10 @@ class RestApi {
}
mApp->initInverter(jsonIn[F("id")]);
iv->config->frequency = jsonIn[F("freq")];
iv->config->powerLevel = jsonIn[F("pa")];
iv->config->frequency = jsonIn[F("freq")];
iv->config->powerLevel = jsonIn[F("pa")];
iv->config->disNightCom = jsonIn[F("disnightcom")];
iv->config->add2Total = jsonIn[F("add2total")];
mApp->saveSettings(false); // without reboot
} else {
jsonOut[F("error")] = F("unknown cmd");

90
src/web/html/setup.html

@ -140,38 +140,33 @@
<fieldset class="mb-4">
<legend class="des">Inverter</legend>
<div id="inverter"></div>
<div class="row mb-2">
<div class="col-12 col-sm-3"><p class="subdes">General</p></div>
<div class="col-12 col-sm-9"></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Interval [s]</div>
<div class="col-12 col-sm-9"><input type="number" name="invInterval" title="Invalid input"/></div>
<div class="col-8 my-2">Interval [s]</div>
<div class="col-4"><input type="number" name="invInterval" title="Invalid input"/></div>
</div>
<div class="row mb-3">
<div class="col-8 col-sm-3 mb-2">Reset values and YieldDay at midnight</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="invRstMid"/></div>
<div class="col-8 mb-2">Reset values and YieldDay at midnight</div>
<div class="col-4"><input type="checkbox" name="invRstMid"/></div>
</div>
<div class="row mb-3">
<div class="col-8 col-sm-3 mb-2">Reset values when inverter polling pauses at sunset</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="invRstComStop"/></div>
<div class="col-8 mb-2">Reset values when inverter polling pauses at sunset</div>
<div class="col-4"><input type="checkbox" name="invRstComStop"/></div>
</div>
<div class="row mb-3">
<div class="col-8 col-sm-3">Reset values when inverter status is 'not available'</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="invRstNotAvail"/></div>
<div class="col-8">Reset values when inverter status is 'not available'</div>
<div class="col-4"><input type="checkbox" name="invRstNotAvail"/></div>
</div>
<div class="row mb-3">
<div class="col-8 col-sm-3">Reset 'max' values at midnight</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="invRstMaxMid"/></div>
<div class="col-8">Reset 'max' values at midnight</div>
<div class="col-4"><input type="checkbox" name="invRstMaxMid"/></div>
</div>
<div class="row mb-3">
<div class="col-8 col-sm-3">Start without time sync (useful in AP-Only-Mode)</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="strtWthtTm"/></div>
<div class="col-8">Start without time sync (useful in AP-Only-Mode)</div>
<div class="col-4"><input type="checkbox" name="strtWthtTm"/></div>
</div>
<div class="row mb-3">
<div class="col-8 col-sm-3">Yield Efficiency (should be between 0.95 and 0.96)</div>
<div class="col-4 col-sm-9"><input type="number" name="yldEff" step="any"/></div>
<div class="col-8">Yield Efficiency (should be between 0.95 and 0.96)</div>
<div class="col-4"><input type="number" name="yldEff" step="any"/></div>
</div>
</fieldset>
</div>
@ -223,10 +218,6 @@
<div class="col-12 col-sm-3 my-2">Offset (pre sunrise, post sunset)</div>
<div class="col-12 col-sm-9"><select name="sunOffs"></select></div>
</div>
<div class="row mb-3">
<div class="col-8 col-sm-3">Pause polling inverters during night</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="sunDisNightCom"/></div>
</div>
</fieldset>
</div>
@ -434,6 +425,7 @@
[47, "GPIO47"],
[48, "GPIO48"],
];
/*ENDIF_ESP32*/
var nrfPa = [
[0, "MIN (recommended)"],
[1, "LOW"],
@ -442,6 +434,8 @@
];
var esp32cmtPa = [];
var esp32cmtFreq = [];
/*IF_ESP32*/
var freqFmt = new Intl.NumberFormat('en-US', {
minimumIntegerDigits: 3,
minimumFractionDigits: 2
@ -645,6 +639,7 @@
add.ch_yield_cor = [];
add.freq = 12;
add.pa = 30;
add.add2total = true;
var e = document.getElementById("inverter");
e.innerHTML = ""; // remove all childs
@ -674,23 +669,27 @@
}
var cbEn = ml("input", {name: "enable", type: "checkbox"}, null);
if(obj.enabled)
cbEn.checked = true;
var cbDisNightCom = ml("input", {name: "disnightcom", type: "checkbox"}, null);
var cbAddTotal = ml("input", {name: "add2total", type: "checkbox"}, null);
cbEn.checked = (obj.enabled);
cbDisNightCom.checked = (obj.disnightcom);
cbAddTotal.checked = (obj.add2total);
var ser = ml("input", {name: "ser", class: "text", type: "number", max: 138999999999, value: obj.serial}, null);
var html = ml("div", {}, [
tabs(["General", "Inputs", "Radio"]),
tabs(["General", "Inputs", "Radio", "Advanced"]),
ml("div", {id: "divGeneral", class: "tab-content"}, [
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-4"}, "Enable"),
ml("div", {class: "col-8"}, cbEn)
ml("div", {class: "col-2"}, "Enable"),
ml("div", {class: "col-10"}, cbEn)
]),
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-4 mt-2"}, "Serial"),
ml("div", {class: "col-8"}, ser)
ml("div", {class: "col-2 mt-2"}, "Serial"),
ml("div", {class: "col-10"}, ser)
]),
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-4 mt-2"}, "Name"),
ml("div", {class: "col-8"}, ml("input", {name: "name", class: "text", type: "text", value: obj.name}, null))
ml("div", {class: "col-2 mt-2"}, "Name"),
ml("div", {class: "col-10"}, ml("input", {name: "name", class: "text", type: "text", value: obj.name}, null))
])
]),
ml("div", {id: "divInputs", class: "tab-content hide"}, [
@ -704,21 +703,31 @@
ml("input", {type: "hidden", name: "isnrf"}, null),
ml("div", {id: "setcmt"}, [
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-4 mt-2"}, "Frequency"),
ml("div", {class: "col-8"}, sel("freq", esp32cmtFreq, obj.freq))
ml("div", {class: "col-3 mt-2"}, "Frequency"),
ml("div", {class: "col-9"}, sel("freq", esp32cmtFreq, obj.freq))
]),
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-4 mt-2"}, "Power Level"),
ml("div", {class: "col-8"}, sel("cmtpa", esp32cmtPa, obj.pa))
ml("div", {class: "col-3 mt-2"}, "Power Level"),
ml("div", {class: "col-9"}, sel("cmtpa", esp32cmtPa, obj.pa))
]),
]),
ml("div", {id: "setnrf"},
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-4 mt-2"}, "Power Level"),
ml("div", {class: "col-8"}, sel("nrfpa", nrfPa, obj.pa))
ml("div", {class: "col-3 mt-2"}, "Power Level"),
ml("div", {class: "col-9"}, sel("nrfpa", nrfPa, obj.pa))
]),
),
]),
ml("div", {id: "divAdvanced", class: "tab-content hide"}, [
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-10"}, "Pause communication during night (lat. and lon. need to be set)"),
ml("div", {class: "col-2"}, cbDisNightCom)
]),
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-10"}, "Include inverter to sum of total (should be checked by default)"),
ml("div", {class: "col-2"}, cbAddTotal)
])
]),
ml("div", {class: "row mt-5"}, [
ml("div", {class: "col-8", id: "res"}, ""),
ml("div", {class: "col-4 a-r"}, ml("input", {type: "button", value: "save", class: "btn", onclick: function() { ivSave(); }}, null))
@ -758,7 +767,7 @@
})
});
modal("Edit inverter", html);
modal("Edit inverter " + obj.name, html);
ser.dispatchEvent(new Event('change'));
function ivSave() {
@ -781,6 +790,8 @@
else
o.pa = document.getElementsByName("cmtpa")[0].value;
o.freq = document.getElementsByName("freq")[0].value;
o.disnightcom = document.getElementsByName("disnightcom")[0].checked;
o.add2total = document.getElementsByName("add2total")[0].checked;
getAjax("/api/setup", cb, "POST", JSON.stringify(o));
}
@ -841,7 +852,6 @@
function parseSun(obj) {
document.getElementsByName("sunLat")[0].value = obj["lat"];
document.getElementsByName("sunLon")[0].value = obj["lon"];
document.getElementsByName("sunDisNightCom")[0].checked = obj["disnightcom"];
const sel = document.getElementsByName("sunOffs")[0];
for(var i = 0; i <= 60; i++) {
sel.appendChild(opt(i, i + " minutes", (i == (obj["offs"] / 60))));

2
src/web/web.h

@ -535,12 +535,10 @@ class Web {
if (request->arg("sunLat") == "" || (request->arg("sunLon") == "")) {
mConfig->sun.lat = 0.0;
mConfig->sun.lon = 0.0;
mConfig->sun.disNightCom = false;
mConfig->sun.offsetSec = 0;
} else {
mConfig->sun.lat = request->arg("sunLat").toFloat();
mConfig->sun.lon = request->arg("sunLon").toFloat();
mConfig->sun.disNightCom = (request->arg("sunDisNightCom") == "on");
mConfig->sun.offsetSec = request->arg("sunOffs").toInt() * 60;
}

Loading…
Cancel
Save