diff --git a/lib/EnergyAccounting/include/EnergyAccounting.h b/lib/EnergyAccounting/include/EnergyAccounting.h index ff831db2..10ee6817 100644 --- a/lib/EnergyAccounting/include/EnergyAccounting.h +++ b/lib/EnergyAccounting/include/EnergyAccounting.h @@ -56,10 +56,25 @@ struct EnergyAccountingData2 { uint16_t costLastMonth; }; +struct EnergyAccountingRealtimeData { + uint8_t magic; + uint8_t currentHour; + uint8_t currentDay; + uint8_t currentThresholdIdx; + float use; + float costHour; + float costDay; + float produce; + float incomeHour; + float incomeDay; + unsigned long lastImportUpdateMillis; + unsigned long lastExportUpdateMillis; +}; + class EnergyAccounting { public: - EnergyAccounting(RemoteDebug*); + EnergyAccounting(RemoteDebug*, EnergyAccountingRealtimeData*); void setup(AmsDataStorage *ds, EnergyAccountingConfig *config); void setEapi(EntsoeApi *eapi); void setTimezone(Timezone*); @@ -103,17 +118,13 @@ public: private: RemoteDebug* debugger = NULL; - unsigned long lastImportUpdateMillis = 0; - unsigned long lastExportUpdateMillis = 0; bool init = false, initPrice = false; AmsDataStorage *ds = NULL; EntsoeApi *eapi = NULL; EnergyAccountingConfig *config = NULL; Timezone *tz = NULL; - uint8_t currentHour = 0, currentDay = 0, currentThresholdIdx = 0; - float use = 0, costHour = 0, costDay = 0; - float produce = 0, incomeHour = 0, incomeDay = 0; EnergyAccountingData data = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + EnergyAccountingRealtimeData* realtimeData = NULL; float fixedPrice = 0; String currency = ""; diff --git a/lib/EnergyAccounting/src/EnergyAccounting.cpp b/lib/EnergyAccounting/src/EnergyAccounting.cpp index f1bd1bfe..9f4441eb 100644 --- a/lib/EnergyAccounting/src/EnergyAccounting.cpp +++ b/lib/EnergyAccounting/src/EnergyAccounting.cpp @@ -3,15 +3,29 @@ #include "AmsStorage.h" #include "FirmwareVersion.h" -EnergyAccounting::EnergyAccounting(RemoteDebug* debugger) { +EnergyAccounting::EnergyAccounting(RemoteDebug* debugger, EnergyAccountingRealtimeData* rtd) { data.version = 1; this->debugger = debugger; + if(rtd->magic != 0x6A) { + rtd->magic = 0x6A; + rtd->currentHour = 0; + rtd->currentDay = 0; + rtd->currentThresholdIdx = 0; + rtd->use = 0; + rtd->costHour = 0; + rtd->costDay = 0; + rtd->produce = 0; + rtd->incomeHour = 0; + rtd->incomeDay = 0; + rtd->lastImportUpdateMillis = 0; + rtd->lastExportUpdateMillis = 0; + } + this->realtimeData = rtd; } void EnergyAccounting::setup(AmsDataStorage *ds, EnergyAccountingConfig *config) { this->ds = ds; this->config = config; - this->currentThresholdIdx = 0; } void EnergyAccounting::setEapi(EntsoeApi *eapi) { @@ -44,8 +58,8 @@ bool EnergyAccounting::update(AmsData* amsData) { breakTime(tz->toLocal(now), local); if(!init) { - currentHour = local.Hour; - currentDay = local.Day; + this->realtimeData->currentHour = local.Hour; + this->realtimeData->currentDay = local.Day; if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("(EnergyAccounting) Initializing data at %lu\n"), (int32_t) now); if(!load()) { if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("(EnergyAccounting) Unable to load existing data\n")); @@ -75,7 +89,7 @@ bool EnergyAccounting::update(AmsData* amsData) { calcDayCost(); } - if(local.Hour != currentHour && (amsData->getListType() >= 3 || local.Minute == 1)) { + if(local.Hour != this->realtimeData->currentHour && (amsData->getListType() >= 3 || local.Minute == 1)) { if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("(EnergyAccounting) New local hour %d\n"), local.Hour); tmElements_t oneHrAgo, oneHrAgoLocal; @@ -85,28 +99,28 @@ bool EnergyAccounting::update(AmsData* amsData) { breakTime(tz->toLocal(now-3600), oneHrAgoLocal); ret |= updateMax(val, oneHrAgoLocal.Day); - currentHour = local.Hour; // Need to be defined here so that day cost is correctly calculated + this->realtimeData->currentHour = local.Hour; // Need to be defined here so that day cost is correctly calculated if(local.Hour > 0) { calcDayCost(); } - use = 0; - produce = 0; - costHour = 0; - incomeHour = 0; + this->realtimeData->use = 0; + this->realtimeData->produce = 0; + this->realtimeData->costHour = 0; + this->realtimeData->incomeHour = 0; - uint8_t prevDay = currentDay; - if(local.Day != currentDay) { + uint8_t prevDay = this->realtimeData->currentDay; + if(local.Day != this->realtimeData->currentDay) { if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("(EnergyAccounting) New day %d\n"), local.Day); - data.costYesterday = costDay * 100; - data.costThisMonth += costDay * 100; - costDay = 0; + data.costYesterday = this->realtimeData->costDay * 100; + data.costThisMonth += this->realtimeData->costDay * 100; + this->realtimeData->costDay = 0; - data.incomeYesterday = incomeDay * 100; - data.incomeThisMonth += incomeDay * 100; - incomeDay = 0; + data.incomeYesterday = this->realtimeData->incomeDay * 100; + data.incomeThisMonth += this->realtimeData->incomeDay * 100; + this->realtimeData->incomeDay = 0; - currentDay = local.Day; + this->realtimeData->currentDay = local.Day; ret = true; } @@ -137,47 +151,47 @@ bool EnergyAccounting::update(AmsData* amsData) { data.lastMonthAccuracy = accuracy; data.month = local.Month; - currentThresholdIdx = 0; + this->realtimeData->currentThresholdIdx = 0; ret = true; } } - if(this->lastImportUpdateMillis < amsData->getLastUpdateMillis()) { - unsigned long ms = amsData->getLastUpdateMillis() - this->lastImportUpdateMillis; + if(this->realtimeData->lastImportUpdateMillis < amsData->getLastUpdateMillis()) { + unsigned long ms = amsData->getLastUpdateMillis() - this->realtimeData->lastImportUpdateMillis; float kwhi = (amsData->getActiveImportPower() * (((float) ms) / 3600000.0)) / 1000.0; if(kwhi > 0) { if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(EnergyAccounting) Adding %.4f kWh import\n"), kwhi); - use += kwhi; + this->realtimeData->use += kwhi; if(price != ENTSOE_NO_VALUE) { float cost = price * kwhi; if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(EnergyAccounting) and %.4f %s\n"), cost / 100.0, currency.c_str()); - costHour += cost; - costDay += cost; + this->realtimeData->costHour += cost; + this->realtimeData->costDay += cost; } } - lastImportUpdateMillis = amsData->getLastUpdateMillis(); + this->realtimeData->lastImportUpdateMillis = amsData->getLastUpdateMillis(); } - if(amsData->getListType() > 1 && this->lastExportUpdateMillis < amsData->getLastUpdateMillis()) { - unsigned long ms = amsData->getLastUpdateMillis() - this->lastExportUpdateMillis; + if(amsData->getListType() > 1 && this->realtimeData->lastExportUpdateMillis < amsData->getLastUpdateMillis()) { + unsigned long ms = amsData->getLastUpdateMillis() - this->realtimeData->lastExportUpdateMillis; float kwhe = (amsData->getActiveExportPower() * (((float) ms) / 3600000.0)) / 1000.0; if(kwhe > 0) { if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(EnergyAccounting) Adding %.4f kWh export\n"), kwhe); - produce += kwhe; + this->realtimeData->produce += kwhe; if(price != ENTSOE_NO_VALUE) { float income = price * kwhe; if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(EnergyAccounting) and %.4f %s\n"), income / 100.0, currency.c_str()); - incomeHour += income; - incomeDay += income; + this->realtimeData->incomeHour += income; + this->realtimeData->incomeDay += income; } } - lastExportUpdateMillis = amsData->getLastUpdateMillis(); + this->realtimeData-> lastExportUpdateMillis = amsData->getLastUpdateMillis(); } if(config != NULL) { - if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(EnergyAccounting) calculating threshold, currently at %d\n"), currentThresholdIdx); - while(getMonthMax() > config->thresholds[currentThresholdIdx] && currentThresholdIdx < 10) currentThresholdIdx++; - if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(EnergyAccounting) new threshold %d\n"), currentThresholdIdx); + if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(EnergyAccounting) calculating threshold, currently at %d\n"), this->realtimeData->currentThresholdIdx); + while(getMonthMax() > config->thresholds[this->realtimeData->currentThresholdIdx] && this->realtimeData->currentThresholdIdx < 10) this->realtimeData->currentThresholdIdx++; + if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(EnergyAccounting) new threshold %d\n"), this->realtimeData->currentThresholdIdx); } return ret; @@ -191,25 +205,25 @@ void EnergyAccounting::calcDayCost() { if(getPriceForHour(0) != ENTSOE_NO_VALUE) { if(initPrice) { - costDay = 0; - incomeDay = 0; + this->realtimeData->costDay = 0; + this->realtimeData->incomeDay = 0; } - for(uint8_t i = 0; i < currentHour; i++) { + for(uint8_t i = 0; i < this->realtimeData->currentHour; i++) { float price = getPriceForHour(i - local.Hour); if(price == ENTSOE_NO_VALUE) break; breakTime(now - ((local.Hour - i) * 3600), utc); int16_t wh = ds->getHourImport(utc.Hour); - costDay += price * (wh / 1000.0); + this->realtimeData->costDay += price * (wh / 1000.0); wh = ds->getHourExport(utc.Hour); - incomeDay += price * (wh / 1000.0); + this->realtimeData->incomeDay += price * (wh / 1000.0); } initPrice = true; } } float EnergyAccounting::getUseThisHour() { - return use; + return this->realtimeData->use; } float EnergyAccounting::getUseToday() { @@ -219,7 +233,7 @@ float EnergyAccounting::getUseToday() { if(now < FirmwareVersion::BuildEpoch) return 0.0; tmElements_t utc, local; breakTime(tz->toLocal(now), local); - for(uint8_t i = 0; i < currentHour; i++) { + for(uint8_t i = 0; i < this->realtimeData->currentHour; i++) { breakTime(now - ((local.Hour - i) * 3600), utc); ret += ds->getHourImport(utc.Hour) / 1000.0; } @@ -230,7 +244,7 @@ float EnergyAccounting::getUseThisMonth() { time_t now = time(nullptr); if(now < FirmwareVersion::BuildEpoch) return 0.0; float ret = 0; - for(uint8_t i = 1; i < currentDay; i++) { + for(uint8_t i = 1; i < this->realtimeData->currentDay; i++) { ret += ds->getDayImport(i) / 1000.0; } return ret + getUseToday(); @@ -241,7 +255,7 @@ float EnergyAccounting::getUseLastMonth() { } float EnergyAccounting::getProducedThisHour() { - return produce; + return this->realtimeData->produce; } float EnergyAccounting::getProducedToday() { @@ -251,7 +265,7 @@ float EnergyAccounting::getProducedToday() { if(now < FirmwareVersion::BuildEpoch) return 0.0; tmElements_t utc, local; breakTime(tz->toLocal(now), local); - for(uint8_t i = 0; i < currentHour; i++) { + for(uint8_t i = 0; i < this->realtimeData->currentHour; i++) { breakTime(now - ((local.Hour - i) * 3600), utc); ret += ds->getHourExport(utc.Hour) / 1000.0; } @@ -262,7 +276,7 @@ float EnergyAccounting::getProducedThisMonth() { time_t now = time(nullptr); if(now < FirmwareVersion::BuildEpoch) return 0.0; float ret = 0; - for(uint8_t i = 1; i < currentDay; i++) { + for(uint8_t i = 1; i < this->realtimeData->currentDay; i++) { ret += ds->getDayExport(i) / 1000.0; } return ret + getProducedToday(); @@ -273,11 +287,11 @@ float EnergyAccounting::getProducedLastMonth() { } float EnergyAccounting::getCostThisHour() { - return costHour; + return this->realtimeData->costHour; } float EnergyAccounting::getCostToday() { - return costDay; + return this->realtimeData->costDay; } float EnergyAccounting::getCostYesterday() { @@ -293,11 +307,11 @@ float EnergyAccounting::getCostLastMonth() { } float EnergyAccounting::getIncomeThisHour() { - return incomeHour; + return this->realtimeData->incomeHour; } float EnergyAccounting::getIncomeToday() { - return incomeDay; + return this->realtimeData->incomeDay; } float EnergyAccounting::getIncomeYesterday() { @@ -315,7 +329,7 @@ float EnergyAccounting::getIncomeLastMonth() { uint8_t EnergyAccounting::getCurrentThreshold() { if(config == NULL) return 0; - return config->thresholds[currentThresholdIdx]; + return config->thresholds[this->realtimeData->currentThresholdIdx]; } float EnergyAccounting::getMonthMax() { diff --git a/platformio.ini b/platformio.ini index ce0047c5..4310874b 100755 --- a/platformio.ini +++ b/platformio.ini @@ -32,7 +32,7 @@ lib_ignore = ${common.lib_ignore} extra_scripts = ${common.extra_scripts} [env:esp32] -platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.08.01/platform-espressif32.zip +platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.07.00/platform-espressif32.zip framework = arduino board = esp32dev board_build.f_cpu = 160000000L @@ -47,7 +47,7 @@ extra_scripts = ${common.extra_scripts} # https://github.com/Jason2866/esp32-arduino-lib-builder [env:esp32s2] -platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.08.01/platform-espressif32.zip +platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.07.00/platform-espressif32.zip framework = arduino board = esp32-s2-saola-1 board_build.mcu = esp32s2 @@ -63,7 +63,7 @@ lib_ignore = ${common.lib_ignore} extra_scripts = ${common.extra_scripts} [env:esp32solo] -platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.08.01/platform-espressif32.zip +platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.07.00/platform-espressif32.zip framework = arduino board = esp32-solo1 board_build.f_cpu = 160000000L @@ -75,7 +75,7 @@ lib_ignore = ${common.lib_ignore} extra_scripts = ${common.extra_scripts} [env:esp32c3] -platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.08.01/platform-espressif32.zip +platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.07.00/platform-espressif32.zip framework = arduino board = esp32-c3-devkitm-1 board_build.mcu = esp32c3 diff --git a/src/AmsToMqttBridge.cpp b/src/AmsToMqttBridge.cpp index af9347e6..36633a57 100644 --- a/src/AmsToMqttBridge.cpp +++ b/src/AmsToMqttBridge.cpp @@ -112,6 +112,7 @@ SoftwareSerial *swSerial = NULL; HardwareSerial *hwSerial = NULL; uint8_t rxBufferErrors = 0; +SystemConfig sysConfig; GpioConfig gpioConfig; MeterConfig meterConfig; bool mqttEnabled = false; @@ -122,7 +123,12 @@ bool ntpEnabled = false; bool mdnsEnabled = false; AmsDataStorage ds(&Debug); -EnergyAccounting ea(&Debug); +#if defined(ESP32) +RTC_NOINIT_ATTR EnergyAccountingRealtimeData rtd; +#else +EnergyAccountingRealtimeData rtd; +#endif +EnergyAccounting ea(&Debug, &rtd); uint8_t wifiReconnectCount = 0; @@ -189,8 +195,7 @@ void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) { switch(reason) { case WIFI_REASON_AUTH_FAIL: case WIFI_REASON_NO_AP_FOUND: - SystemConfig sys; - if(!config.getSystemConfig(sys) || sys.dataCollectionConsent == 0) { + if(sysConfig.dataCollectionConsent == 0) { swapWifiMode(); } else { WiFi_disconnect(WIFI_CONNECTION_TIMEOUT); @@ -213,6 +218,12 @@ void setup() { if(!config.getGpioConfig(gpioConfig)) { config.clearGpio(gpioConfig); } + if(!config.getSystemConfig(sysConfig)) { + sysConfig.boardType = 0; + sysConfig.vendorConfigured = false; + sysConfig.userConfigured = false; + sysConfig.dataCollectionConsent = false; + } delay(1); config.loadTempSensors(); @@ -639,6 +650,7 @@ void handleEnergyAccountingChanged() { } char ntpServerName[64] = ""; +float maxVcc = 2.9; void handleNtpChange() { NtpConfig ntp; @@ -696,6 +708,22 @@ void handleSystem(unsigned long now) { } maxDetectPayloadDetectDone = true; } + + if(sysConfig.boardType == 7 && maxVcc > 2.8) { // Pow-U + float vcc = hw.getVcc(); + if(vcc > 3.4 || vcc < 2.8) { + maxVcc = 0; + } else if(vcc > maxVcc) { + debugD_P(PSTR("Setting new max Vcc to %.2f"), vcc); + maxVcc = vcc; + } else { + float diff = maxVcc-vcc; + if(diff > 0.3) { + debugW_P(PSTR("Vcc dropped to %.2f, disconnecting WiFi for %d seconds to preserve power"), vcc, (uint8_t) (WIFI_CONNECTION_TIMEOUT/1000)); + WiFi_disconnect(WIFI_CONNECTION_TIMEOUT); + } + } + } } void handleTemperature(unsigned long now) { @@ -848,9 +876,7 @@ void setupHanPort(GpioConfig& gpioConfig, uint32_t baud, uint8_t parityOrdinal, parityOrdinal = 3; // 8N1 } - SystemConfig sys; - config.getSystemConfig(sys); - switch(sys.boardType) { + switch(sysConfig.boardType) { case 8: // HAN mosquito: has inverting level shifter invert = !invert; break; @@ -1752,10 +1778,8 @@ void MQTT_connect() { break; case 4: HomeAssistantConfig haconf; - SystemConfig sys; config.getHomeAssistantConfig(haconf); - config.getSystemConfig(sys); - mqttHandler = new HomeAssistantMqttHandler(mqtt, (char*) commonBuffer, mqttConfig.clientId, mqttConfig.publishTopic, sys.boardType, haconf, &hw); + mqttHandler = new HomeAssistantMqttHandler(mqtt, (char*) commonBuffer, mqttConfig.clientId, mqttConfig.publishTopic, sysConfig.boardType, haconf, &hw); break; }