Preserve realtime data on reboot. Also disconnect WiFi on Pow-U if power drops too low

This commit is contained in:
Gunnar Skjold
2023-09-12 20:50:23 +02:00
parent 40215ee746
commit 03edc65a7a
4 changed files with 119 additions and 70 deletions

View File

@@ -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 = "";

View File

@@ -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() {

View File

@@ -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

View File

@@ -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;
}