From b47e91c8ab59ce65d6cb578b44df8c219bc8bd0e Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 29 Sep 2021 15:33:58 +0200 Subject: [PATCH] Add energy total spilt --- CHANGELOG.md | 3 + RELEASENOTES.md | 2 + tasmota/i18n.h | 3 + tasmota/settings.h | 2 +- tasmota/xdrv_03_energy.ino | 511 +++++++++++++++++------------- tasmota/xdrv_10_scripter.ino | 8 +- tasmota/xdrv_11_knx.ino | 21 +- tasmota/xdrv_16_tuyamcu.ino | 4 +- tasmota/xdrv_45_shelly_dimmer.ino | 2 +- tasmota/xnrg_01_hlw8012.ino | 2 +- tasmota/xnrg_02_cse7766.ino | 2 +- tasmota/xnrg_03_pzem004t.ino | 5 + tasmota/xnrg_04_mcp39f501.ino | 2 +- tasmota/xnrg_05_pzem_ac.ino | 7 +- tasmota/xnrg_06_pzem_dc.ino | 7 +- tasmota/xnrg_07_ade7953.ino | 12 +- tasmota/xnrg_14_bl09xx.ino | 37 +-- tasmota/xnrg_19_cse7761.ino | 18 +- tasmota/xnrg_30_dummy.ino | 9 +- tasmota/xsns_75_prometheus.ino | 4 +- 20 files changed, 353 insertions(+), 308 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1b9bc5f7..0418b3302 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. ## [Unreleased] - Development ## [9.5.0.9] +### Added +- Command ``SetOption129 1`` to enable split total energy results (#13030) +- Commands ``EnergyTotal``, ``EnergyToday`` and ``EnergyYesterday`` to (re)set energy values ## [9.5.0.8] 20210927 ### Added diff --git a/RELEASENOTES.md b/RELEASENOTES.md index f75ecbb9e..8e93548ab 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -104,10 +104,12 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo - Command ``SetOption2 1`` to enable display of global temperature/humidity/pressure info to JSON sensor message - Command ``SetOption127 1`` to force Wi-Fi in no-sleep mode even if ``Sleep 0`` is not enabled - Command ``SetOption128 0|1`` web referer check disabling HTTP API commands if set to 0. Default set to 1 for backward compatibility [#12828](https://github.com/arendst/Tasmota/issues/12828) +- Command ``SetOption129 1`` to enable split total energy results [#13030](https://github.com/arendst/Tasmota/issues/13030) - Command ``SetSensor1..127 0|1`` to globally disable individual sensor driver - Command ``Subscribe2 ...`` to subscribe to a MQTT topic without appended "/#" [#12858](https://github.com/arendst/Tasmota/issues/12858) - Command ``WebGetConfig `` if ``#define USE_WEBGETCONFIG`` is enabled to restore/init configuration from external webserver [#13034](https://github.com/arendst/Tasmota/issues/13034) - Command ``WebQuery GET|POST|PUT|PATCH [] `` to extent HTTP requests [#13209](https://github.com/arendst/Tasmota/issues/13209) +- Commands ``EnergyTotal``, ``EnergyToday`` and ``EnergyYesterday`` to (re)set energy values - Optional IP filter to command ``TCPStart`` [#12806](https://github.com/arendst/Tasmota/issues/12806) - Neopool commands ``NPPHRes``, ``NPCLRes`` and ``NPIonRes`` [#12813](https://github.com/arendst/Tasmota/issues/12813) - Support for second DNS server diff --git a/tasmota/i18n.h b/tasmota/i18n.h index 41c52f10b..1e025318d 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -433,6 +433,9 @@ #define D_CMND_CURRENTLOW "CurrentLow" #define D_CMND_CURRENTHIGH "CurrentHigh" #define D_CMND_ENERGYRESET "EnergyReset" +#define D_CMND_ENERGYTODAY "EnergyToday" +#define D_CMND_ENERGYYESTERDAY "EnergyYesterday" +#define D_CMND_ENERGYTOTAL "EnergyTotal" #define D_CMND_POWERSET "PowerSet" #define D_CMND_VOLTAGESET "VoltageSet" #define D_CMND_CURRENTSET "CurrentSet" diff --git a/tasmota/settings.h b/tasmota/settings.h index ee83f44d3..91b1ff552 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -158,7 +158,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t ds18x20_mean : 1; // bit 12 (v9.3.1.2) - SetOption126 - (DS18x20) Enable arithmetic mean over teleperiod for JSON temperature (1) uint32_t wifi_no_sleep : 1; // bit 13 (v9.5.0.2) - SetOption127 - (Wifi) Keep wifi in no-sleep mode, prevents some occasional unresponsiveness uint32_t disable_referer_chk : 1; // bit 14 (v9.5.0.5) - SetOption128 - (Web) Allow access without referer check - uint32_t spare15 : 1; // bit 15 + uint32_t energy_phase : 1; // bit 15 (v9.5.0.9) - SetOption129 - (Energy) Show phase information uint32_t spare16 : 1; // bit 16 uint32_t spare17 : 1; // bit 17 uint32_t spare18 : 1; // bit 18 diff --git a/tasmota/xdrv_03_energy.ino b/tasmota/xdrv_03_energy.ino index 2e679a497..e0a73321c 100644 --- a/tasmota/xdrv_03_energy.ino +++ b/tasmota/xdrv_03_energy.ino @@ -56,7 +56,7 @@ const char kEnergyCommands[] PROGMEM = "|" // No prefix D_CMND_SAFEPOWER "|" D_CMND_SAFEPOWERHOLD "|" D_CMND_SAFEPOWERWINDOW "|" #endif // USE_ENERGY_POWER_LIMIT #endif // USE_ENERGY_MARGIN_DETECTION - D_CMND_ENERGYRESET "|" D_CMND_TARIFF; + D_CMND_ENERGYRESET "|" D_CMND_ENERGYTODAY "|" D_CMND_ENERGYYESTERDAY "|" D_CMND_ENERGYTOTAL "|" D_CMND_TARIFF; void (* const EnergyCommand[])(void) PROGMEM = { &CmndPowerCal, &CmndVoltageCal, &CmndCurrentCal, &CmndFrequencyCal, @@ -69,9 +69,9 @@ void (* const EnergyCommand[])(void) PROGMEM = { &CmndSafePower, &CmndSafePowerHold, &CmndSafePowerWindow, #endif // USE_ENERGY_POWER_LIMIT #endif // USE_ENERGY_MARGIN_DETECTION - &CmndEnergyReset, &CmndTariff}; + &CmndEnergyReset, &CmndEnergyToday, &CmndEnergyYesterday, &CmndEnergyTotal, &CmndTariff}; -const char kEnergyPhases[] PROGMEM = "|%s / %s|%s / %s / %s||[%s,%s]|[%s,%s,%s]"; +const char kEnergyPhases[] PROGMEM = "|%*_f / %*_f|%*_f / %*_f / %*_f||[%*_f,%*_f]|[%*_f,%*_f,%*_f]"; struct ENERGY { float voltage[ENERGY_MAX_PHASES]; // 123.1 V @@ -85,15 +85,17 @@ struct ENERGY { float import_active[ENERGY_MAX_PHASES]; // 123.123 kWh #endif // SDM630_IMPORT || SDM72_IMPEXP float export_active[ENERGY_MAX_PHASES]; // 123.123 kWh + float start_energy[ENERGY_MAX_PHASES]; // 12345.12345 kWh total previous + float daily[ENERGY_MAX_PHASES]; // 123.123 kWh + float total[ENERGY_MAX_PHASES]; // 12345.12345 kWh total energy + float daily_sum; // 123.123 kWh + float total_sum; // 12345.12345 kWh total energy + float yesterday_sum; // 123.123 kWh - float start_energy; // 12345.12345 kWh total previous - float daily; // 123.123 kWh - float total; // 12345.12345 kWh total energy - - unsigned long kWhtoday_delta; // 1212312345 Wh 10^-5 (deca micro Watt hours) - Overflows to Energy.kWhtoday (HLW and CSE only) - unsigned long kWhtoday_offset; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily - unsigned long kWhtoday; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily - unsigned long period; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily + uint32_t kWhtoday_delta[ENERGY_MAX_PHASES]; // 1212312345 Wh 10^-5 (deca micro Watt hours) - Overflows to Energy.kWhtoday (HLW and CSE only) + uint32_t kWhtoday_offset[ENERGY_MAX_PHASES]; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily + uint32_t kWhtoday[ENERGY_MAX_PHASES]; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily + uint32_t period[ENERGY_MAX_PHASES]; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily uint8_t fifth_second; uint8_t command_code; @@ -134,27 +136,38 @@ Ticker ticker_energy; /********************************************************************************************/ -char* EnergyFormatIndex(char* result, char* input, bool json, uint32_t index, bool single = false) -{ - char layout[16]; +char* EnergyFormatIndex(char* result, float* input, uint32_t resolution, bool json, uint32_t index, bool single = false) { + char layout[20]; GetTextIndexed(layout, sizeof(layout), (index -1) + (ENERGY_MAX_PHASES * json), kEnergyPhases); switch (index) { case 2: - snprintf_P(result, FLOATSZ * ENERGY_MAX_PHASES, layout, input, input + FLOATSZ); // Dirty + ext_snprintf_P(result, FLOATSZ * ENERGY_MAX_PHASES, layout, resolution, &input[0], resolution, &input[1]); break; case 3: - snprintf_P(result, FLOATSZ * ENERGY_MAX_PHASES, layout, input, input + FLOATSZ, input + FLOATSZ + FLOATSZ); // Even dirtier + ext_snprintf_P(result, FLOATSZ * ENERGY_MAX_PHASES, layout, resolution, &input[0], resolution, &input[1], resolution, &input[2]); break; default: - snprintf_P(result, FLOATSZ * ENERGY_MAX_PHASES, input); + ext_snprintf_P(result, FLOATSZ * ENERGY_MAX_PHASES, PSTR("%*_f"), resolution, &input[0]); } return result; } -char* EnergyFormat(char* result, char* input, bool json, bool single = false) -{ +char* EnergyFormat(char* result, float* input, uint32_t resolution, bool json, bool single = false) { uint8_t index = (single) ? 1 : Energy.phase_count; // 1,2,3 - return EnergyFormatIndex(result, input, json, index, single); + return EnergyFormatIndex(result, input, resolution, json, index, single); +} + +char* EnergyFormatSum(char* result, float* input, uint32_t resolution, bool json, bool single = false) { + uint8_t index = (single) ? 1 : Energy.phase_count; // 1,2,3 + float input_sum = 0.0; + if (!Settings->flag5.energy_phase) { + for (uint32_t i = 0; i < index; i++) { + input_sum += input[i]; + } + input = &input_sum; + index = 1; + } + return EnergyFormatIndex(result, input, resolution, json, index, single); } /********************************************************************************************/ @@ -183,22 +196,30 @@ bool EnergyTariff1Active() // Off-Peak hours } } -void EnergyUpdateToday(void) -{ - if (Energy.kWhtoday_delta > 1000) { - unsigned long delta = Energy.kWhtoday_delta / 1000; - Energy.kWhtoday_delta -= (delta * 1000); - Energy.kWhtoday += delta; - } +void EnergyUpdateToday(void) { + Energy.total_sum = 0.0; + Energy.yesterday_sum = 0.0; + Energy.daily_sum = 0.0; + for (uint32_t i = 0; i < Energy.phase_count; i++) { + if (Energy.kWhtoday_delta[i] > 1000) { + uint32_t delta = Energy.kWhtoday_delta[i] / 1000; + Energy.kWhtoday_delta[i] -= (delta * 1000); + Energy.kWhtoday[i] += delta; + } - RtcSettings.energy_kWhtoday = Energy.kWhtoday_offset + Energy.kWhtoday; - Energy.daily = (float)(RtcSettings.energy_kWhtoday) / 100000; - Energy.total = (float)(RtcSettings.energy_kWhtotal + RtcSettings.energy_kWhtoday) / 100000; + RtcSettings.energy_kWhtoday_ph[i] = Energy.kWhtoday_offset[i] + Energy.kWhtoday[i]; + Energy.daily[i] = (float)(RtcSettings.energy_kWhtoday_ph[i]) / 100000; + Energy.total[i] = (float)(RtcSettings.energy_kWhtotal_ph[i] + RtcSettings.energy_kWhtoday_ph[i]) / 100000; + + Energy.total_sum += Energy.total[i]; + Energy.yesterday_sum += (float)Settings->energy_kWhyesterday_ph[i] / 100000; + Energy.daily_sum += Energy.daily[i]; + } if (RtcTime.valid){ // We calc the difference only if we have a valid RTC time. - uint32_t energy_diff = (uint32_t)(Energy.total * 100000) - RtcSettings.energy_usage.last_usage_kWhtotal; - RtcSettings.energy_usage.last_usage_kWhtotal = (uint32_t)(Energy.total * 100000); + uint32_t energy_diff = (uint32_t)(Energy.total_sum * 100000) - RtcSettings.energy_usage.last_usage_kWhtotal; + RtcSettings.energy_usage.last_usage_kWhtotal = (uint32_t)(Energy.total_sum * 100000); uint32_t return_diff = 0; if (!isnan(Energy.export_active[0])) { @@ -225,25 +246,26 @@ void EnergyUpdateToday(void) } } -void EnergyUpdateTotal(float value, bool kwh) +void EnergyUpdateTotal(float value, bool kwh, uint32_t phase = 0); +void EnergyUpdateTotal(float value, bool kwh, uint32_t phase) { // AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: Energy Total %4_f %sWh"), &value, (kwh) ? "k" : ""); uint32_t multiplier = (kwh) ? 100000 : 100; // kWh or Wh to deca milli Wh - if (0 == Energy.start_energy || (value < Energy.start_energy)) { - Energy.start_energy = value; // Init after restart and handle roll-over if any + if (0 == Energy.start_energy[phase] || (value < Energy.start_energy[phase])) { + Energy.start_energy[phase] = value; // Init after restart and handle roll-over if any } - else if (value != Energy.start_energy) { - Energy.kWhtoday = (unsigned long)((value - Energy.start_energy) * multiplier); + else if (value != Energy.start_energy[phase]) { + Energy.kWhtoday[phase] = (unsigned long)((value - Energy.start_energy[phase]) * multiplier); } - if ((Energy.total < (value - 0.01)) && // We subtract a little offset to avoid continuous updates + if ((Energy.total[phase] < (value - 0.01)) && // We subtract a little offset to avoid continuous updates Settings->flag3.hardware_energy_total) { // SetOption72 - Enable hardware energy total counter as reference (#6561) - RtcSettings.energy_kWhtotal = (unsigned long)((value * multiplier) - Energy.kWhtoday_offset - Energy.kWhtoday); - Settings->energy_kWhtotal = RtcSettings.energy_kWhtotal; - Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000; - Settings->energy_kWhtotal_time = (!Energy.kWhtoday_offset) ? LocalTime() : Midnight(); + RtcSettings.energy_kWhtotal_ph[phase] = (unsigned long)((value * multiplier) - Energy.kWhtoday_offset[phase] - Energy.kWhtoday[phase]); + Settings->energy_kWhtotal_ph[phase] = RtcSettings.energy_kWhtotal_ph[phase]; + Energy.total[phase] = (float)(RtcSettings.energy_kWhtotal_ph[phase] + Energy.kWhtoday_offset[phase] + Energy.kWhtoday[phase]) / 100000; + Settings->energy_kWhtotal_time = (!Energy.kWhtoday_offset[phase]) ? LocalTime() : Midnight(); // AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: Energy Total updated with hardware value")); } EnergyUpdateToday(); @@ -264,23 +286,26 @@ void Energy200ms(void) if (RtcTime.valid) { if (!Energy.kWhtoday_offset_init && (RtcTime.day_of_year == Settings->energy_kWhdoy)) { - Energy.kWhtoday_offset = Settings->energy_kWhtoday; + for (uint32_t i = 0; i < 3; i++) { + Energy.kWhtoday_offset[i] = Settings->energy_kWhtoday_ph[i]; + } Energy.kWhtoday_offset_init = true; } if (LocalTime() == Midnight()) { - Settings->energy_kWhyesterday = RtcSettings.energy_kWhtoday; + for (uint32_t i = 0; i < 3; i++) { + Settings->energy_kWhyesterday_ph[i] = RtcSettings.energy_kWhtoday_ph[i]; - RtcSettings.energy_kWhtotal += RtcSettings.energy_kWhtoday; - Settings->energy_kWhtotal = RtcSettings.energy_kWhtotal; + RtcSettings.energy_kWhtotal_ph[i] += RtcSettings.energy_kWhtoday_ph[i]; + Settings->energy_kWhtotal_ph[i] = RtcSettings.energy_kWhtotal_ph[i]; - Energy.period -= RtcSettings.energy_kWhtoday; // this becomes a large unsigned, effectively a negative for EnergyShow calculation - Energy.kWhtoday = 0; - Energy.kWhtoday_offset = 0; - RtcSettings.energy_kWhtoday = 0; - Energy.start_energy = 0; + Energy.period[i] -= RtcSettings.energy_kWhtoday_ph[i]; // this becomes a large unsigned, effectively a negative for EnergyShow calculation + Energy.kWhtoday[i] = 0; + Energy.kWhtoday_offset[i] = 0; + RtcSettings.energy_kWhtoday_ph[i] = 0; + Energy.start_energy[i] = 0; // Energy.kWhtoday_delta = 0; // dont zero this, we need to carry the remainder over to tomorrow - + } EnergyUpdateToday(); #if defined(USE_ENERGY_MARGIN_DETECTION) && defined(USE_ENERGY_POWER_LIMIT) Energy.max_energy_state = 3; @@ -302,8 +327,10 @@ void EnergySaveState(void) { Settings->energy_kWhdoy = (RtcTime.valid) ? RtcTime.day_of_year : 0; - Settings->energy_kWhtoday = RtcSettings.energy_kWhtoday; - Settings->energy_kWhtotal = RtcSettings.energy_kWhtotal; + for (uint32_t i = 0; i < 3; i++) { + Settings->energy_kWhtoday_ph[i] = RtcSettings.energy_kWhtoday_ph[i]; + Settings->energy_kWhtotal_ph[i] = RtcSettings.energy_kWhtotal_ph[i]; + } Settings->energy_usage = RtcSettings.energy_usage; } @@ -371,12 +398,20 @@ void EnergyMarginCheck(void) Energy.power_history[phase][2] = active_power; } if (jsonflg) { +/* char power_diff_chr[Energy.phase_count][FLOATSZ]; for (uint32_t phase = 0; phase < Energy.phase_count; phase++) { dtostrfd(power_diff[phase], 0, power_diff_chr[phase]); } char value_chr[FLOATSZ * ENERGY_MAX_PHASES]; ResponseAppend_P(PSTR("\"" D_CMND_POWERDELTA "\":%s"), EnergyFormat(value_chr, power_diff_chr[0], 1)); +*/ + float power_diff_f[Energy.phase_count]; + for (uint32_t phase = 0; phase < Energy.phase_count; phase++) { + power_diff_f[phase] = power_diff[phase]; + } + char value_chr[FLOATSZ * ENERGY_MAX_PHASES]; + ResponseAppend_P(PSTR("\"" D_CMND_POWERDELTA "\":%s"), EnergyFormat(value_chr, power_diff_f, 0, 1)); } uint16_t energy_power_u = (uint16_t)(Energy.active_power[0]); @@ -467,7 +502,7 @@ void EnergyMarginCheck(void) // Max Energy if (Settings->energy_max_energy) { - uint16_t energy_daily_u = (uint16_t)(Energy.daily * 1000); + uint16_t energy_daily_u = (uint16_t)(Energy.daily_sum * 1000); if (!Energy.max_energy_state && (RtcTime.hour == Settings->energy_max_energy_start)) { Energy.max_energy_state = 1; ResponseTime_P(PSTR(",\"" D_JSON_ENERGYMONITOR "\":\"%s\"}"), GetStateText(1)); @@ -476,7 +511,7 @@ void EnergyMarginCheck(void) } else if ((1 == Energy.max_energy_state ) && (energy_daily_u >= Settings->energy_max_energy)) { Energy.max_energy_state = 2; - ResponseTime_P(PSTR(",\"" D_JSON_MAXENERGYREACHED "\":%3_f}"), &Energy.daily); + ResponseTime_P(PSTR(",\"" D_JSON_MAXENERGYREACHED "\":%3_f}"), &Energy.daily_sum); MqttPublishPrefixTopicRulesProcess_P(STAT, S_RSLT_WARNING); EnergyMqttShow(); SetAllPower(POWER_ALL_OFF, SRC_MAXENERGY); @@ -559,48 +594,9 @@ void CmndEnergyReset(void) { uint32_t params = ParseParameters(2, values); values[0] *= 100; - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 5)) { + if ((XdrvMailbox.index > 3) && (XdrvMailbox.index <= 5)) { if (params > 0) { switch (XdrvMailbox.index) { - case 1: - // Reset Energy Today - Energy.kWhtoday_offset = values[0]; - Energy.kWhtoday = 0; - Energy.kWhtoday_delta = 0; - Energy.start_energy = 0; - Energy.period = Energy.kWhtoday_offset; - Settings->energy_kWhtoday = Energy.kWhtoday_offset; - RtcSettings.energy_kWhtoday = Energy.kWhtoday_offset; - Energy.daily = (float)Energy.kWhtoday_offset / 100000; - if( params > 1) { - Settings->energy_kWhtotal_time = values[1]; - } - else { - if (!RtcSettings.energy_kWhtotal && !Energy.kWhtoday_offset) { - Settings->energy_kWhtotal_time = LocalTime(); - } - } - break; - case 2: - // Reset Energy Yesterday - Settings->energy_kWhyesterday = values[0]; - if( params > 1) { - Settings->energy_kWhtotal_time = values[1]; - } - break; - case 3: - // Reset Energy Total - RtcSettings.energy_kWhtotal = values[0]; - Settings->energy_kWhtotal = RtcSettings.energy_kWhtotal; -// Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000; - if( params > 1) { - Settings->energy_kWhtotal_time = values[1]; - } - else { - Settings->energy_kWhtotal_time = (!Energy.kWhtoday_offset) ? LocalTime() : Midnight(); - } - RtcSettings.energy_usage.last_usage_kWhtotal = (uint32_t)(Energy.total * 1000); - break; case 4: // Reset energy_usage.usage totals RtcSettings.energy_usage.usage1_kWhtotal = values[0]; @@ -623,24 +619,100 @@ void CmndEnergyReset(void) { } } - Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000; - float energy_kWhyesterday = (float)Settings->energy_kWhyesterday / 100000; float usage1_kWhtotal = (float)Settings->energy_usage.usage1_kWhtotal / 100000; float usage2_kWhtotal = (float)Settings->energy_usage.usage2_kWhtotal / 100000; float return1_kWhtotal = (float)Settings->energy_usage.return1_kWhtotal / 100000; float return2_kWhtotal = (float)Settings->energy_usage.return2_kWhtotal / 100000; - Response_P(PSTR("{\"%s\":{\"" D_JSON_TOTAL "\":%*_f,\"" D_JSON_YESTERDAY "\":%*_f,\"" D_JSON_TODAY "\":%*_f,\"" D_JSON_USAGE "\":[%*_f,%*_f],\"" D_JSON_EXPORT "\":[%*_f,%*_f]}}"), + Response_P(PSTR("{\"%s\":{\"" D_JSON_USAGE "\":[%*_f,%*_f],\"" D_JSON_EXPORT "\":[%*_f,%*_f]}}"), XdrvMailbox.command, - Settings->flag2.energy_resolution, &Energy.total, - Settings->flag2.energy_resolution, &energy_kWhyesterday, - Settings->flag2.energy_resolution, &Energy.daily, Settings->flag2.energy_resolution, &usage1_kWhtotal, Settings->flag2.energy_resolution, &usage2_kWhtotal, Settings->flag2.energy_resolution, &return1_kWhtotal, Settings->flag2.energy_resolution, &return2_kWhtotal); } +void CmndEnergyResponse(void) { + char value_chr[FLOATSZ * ENERGY_MAX_PHASES]; // Used by EnergyFormatIndex + char value2_chr[FLOATSZ * ENERGY_MAX_PHASES]; + char value3_chr[FLOATSZ * ENERGY_MAX_PHASES]; + + float energy_yesterday_ph[3]; + for (uint32_t i = 0; i < Energy.phase_count; i++) { + energy_yesterday_ph[i] = (float)Settings->energy_kWhyesterday_ph[i] / 100000; + Energy.total[i] = (float)(RtcSettings.energy_kWhtotal_ph[i] + Energy.kWhtoday_offset[i] + Energy.kWhtoday[i]) / 100000; + } + + Response_P(PSTR("{\"%s\":{\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s}"), + XdrvMailbox.command, + EnergyFormat(value_chr, Energy.total, Settings->flag2.energy_resolution, true), + EnergyFormat(value2_chr, energy_yesterday_ph, Settings->flag2.energy_resolution, true), + EnergyFormat(value3_chr, Energy.daily, Settings->flag2.energy_resolution, true)); +} + +void CmndEnergyTotal(void) { + uint32_t values[2] = { 0 }; + uint32_t params = ParseParameters(2, values); + values[0] *= 100; + + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= Energy.phase_count) && (params > 0)) { + uint32_t phase = XdrvMailbox.index -1; + // Reset Energy Total + RtcSettings.energy_kWhtotal_ph[phase] = values[0]; + Settings->energy_kWhtotal_ph[phase] = RtcSettings.energy_kWhtotal_ph[phase]; + if (params > 1) { + Settings->energy_kWhtotal_time = values[1]; + } else { + Settings->energy_kWhtotal_time = (!Energy.kWhtoday_offset[phase]) ? LocalTime() : Midnight(); + } + RtcSettings.energy_usage.last_usage_kWhtotal = (uint32_t)(Energy.total[phase] * 1000); + } + CmndEnergyResponse(); +} + +void CmndEnergyYesterday(void) { + uint32_t values[2] = { 0 }; + uint32_t params = ParseParameters(2, values); + values[0] *= 100; + + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= Energy.phase_count) && (params > 0)) { + uint32_t phase = XdrvMailbox.index -1; + // Reset Energy Yesterday + Settings->energy_kWhyesterday_ph[phase] = values[0]; + if (params > 1) { + Settings->energy_kWhtotal_time = values[1]; + } + } + CmndEnergyResponse(); +} +void CmndEnergyToday(void) { + uint32_t values[2] = { 0 }; + uint32_t params = ParseParameters(2, values); + values[0] *= 100; + + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= Energy.phase_count) && (params > 0)) { + uint32_t phase = XdrvMailbox.index -1; + // Reset Energy Today + Energy.kWhtoday_offset[phase] = values[0]; + Energy.kWhtoday[phase] = 0; + Energy.kWhtoday_delta[phase] = 0; + Energy.start_energy[phase] = 0; + Energy.period[phase] = Energy.kWhtoday_offset[phase]; + Settings->energy_kWhtoday_ph[phase] = Energy.kWhtoday_offset[phase]; + RtcSettings.energy_kWhtoday_ph[phase] = Energy.kWhtoday_offset[phase]; + Energy.daily[phase] = (float)Energy.kWhtoday_offset[phase] / 100000; + if (params > 1) { + Settings->energy_kWhtotal_time = values[1]; + } + else { + if (!RtcSettings.energy_kWhtotal_ph[phase] && !Energy.kWhtoday_offset[phase]) { + Settings->energy_kWhtotal_time = LocalTime(); + } + } + } + CmndEnergyResponse(); +} + void CmndTariff(void) { // Tariff1 22:00,23:00 - Tariff1 start hour for Standard Time and Daylight Savings Time // Tariff2 6:00,7:00 - Tariff2 start hour for Standard Time and Daylight Savings Time @@ -910,15 +982,30 @@ void EnergySnsInit(void) XnrgCall(FUNC_INIT); if (TasmotaGlobal.energy_driver) { + + // Update for split phase totals (v9.5.0.9) + if ((Settings->energy_kWhtoday > 0) && (0 == Settings->energy_kWhtoday_ph[0])) { + Settings->energy_kWhtoday_ph[0] = Settings->energy_kWhtoday; + Settings->energy_kWhyesterday_ph[0] = Settings->energy_kWhyesterday; + Settings->energy_kWhtotal_ph[0] = Settings->energy_kWhtotal; + RtcSettings.energy_kWhtoday_ph[0] = RtcSettings.energy_kWhtoday; + RtcSettings.energy_kWhtotal_ph[0] = RtcSettings.energy_kWhtotal; + Settings->energy_kWhtoday = 0; + } + // Energy.kWhtoday_offset = 0; // Do not use at Power On as Rtc was invalid (but has been restored from Settings already) if ((ResetReason() != REASON_DEFAULT_RST) && RtcSettingsValid()) { - Energy.kWhtoday_offset = RtcSettings.energy_kWhtoday; + for (uint32_t i = 0; i < 3; i++) { + Energy.kWhtoday_offset[i] = RtcSettings.energy_kWhtoday_ph[i]; + } Energy.kWhtoday_offset_init = true; } -// Energy.kWhtoday = 0; -// Energy.kWhtoday_delta = 0; - Energy.period = Energy.kWhtoday_offset; + for (uint32_t i = 0; i < 3; i++) { +// Energy.kWhtoday_ph[i] = 0; +// Energy.kWhtoday_delta[i] = 0; + Energy.period[i] = Energy.kWhtoday_offset[i]; + } EnergyUpdateToday(); ticker_energy.attach_ms(200, Energy200ms); } @@ -944,103 +1031,71 @@ const char HTTP_ENERGY_SNS4[] PROGMEM = #endif // SDM630_IMPORT || SDM72_IMPEXP #endif // USE_WEBSERVER -void EnergyShow(bool json) -{ - for (uint32_t i = 0; i < Energy.phase_count; i++) { - if (Energy.voltage_common) { +void EnergyShow(bool json) { + if (Energy.voltage_common) { + for (uint32_t i = 0; i < Energy.phase_count; i++) { Energy.voltage[i] = Energy.voltage[0]; } } - float power_factor_knx = Energy.power_factor[0]; - - char apparent_power_chr[Energy.phase_count][FLOATSZ]; - char reactive_power_chr[Energy.phase_count][FLOATSZ]; - char power_factor_chr[Energy.phase_count][FLOATSZ]; - char frequency_chr[Energy.phase_count][FLOATSZ]; + float apparent_power[Energy.phase_count]; + float reactive_power[Energy.phase_count]; + float power_factor[Energy.phase_count]; if (!Energy.type_dc) { if (Energy.current_available && Energy.voltage_available) { for (uint32_t i = 0; i < Energy.phase_count; i++) { - float apparent_power = Energy.apparent_power[i]; - if (isnan(apparent_power)) { - apparent_power = Energy.voltage[i] * Energy.current[i]; + apparent_power[i] = Energy.apparent_power[i]; + if (isnan(apparent_power[i])) { + apparent_power[i] = Energy.voltage[i] * Energy.current[i]; } - if (apparent_power < Energy.active_power[i]) { // Should be impossible - Energy.active_power[i] = apparent_power; + if (apparent_power[i] < Energy.active_power[i]) { // Should be impossible + Energy.active_power[i] = apparent_power[i]; } - float power_factor = Energy.power_factor[i]; - if (isnan(power_factor)) { - power_factor = (Energy.active_power[i] && apparent_power) ? Energy.active_power[i] / apparent_power : 0; - if (power_factor > 1) { - power_factor = 1; + power_factor[i] = Energy.power_factor[i]; + if (isnan(power_factor[i])) { + power_factor[i] = (Energy.active_power[i] && apparent_power[i]) ? Energy.active_power[i] / apparent_power[i] : 0; + if (power_factor[i] > 1) { + power_factor[i] = 1; } } - if (0 == i) { power_factor_knx = power_factor; } - float reactive_power = Energy.reactive_power[i]; - if (isnan(reactive_power)) { - reactive_power = 0; - uint32_t difference = ((uint32_t)(apparent_power * 100) - (uint32_t)(Energy.active_power[i] * 100)) / 10; - if ((Energy.current[i] > 0.005) && ((difference > 15) || (difference > (uint32_t)(apparent_power * 100 / 1000)))) { + reactive_power[i] = Energy.reactive_power[i]; + if (isnan(reactive_power[i])) { + reactive_power[i] = 0; + uint32_t difference = ((uint32_t)(apparent_power[i] * 100) - (uint32_t)(Energy.active_power[i] * 100)) / 10; + if ((Energy.current[i] > 0.005) && ((difference > 15) || (difference > (uint32_t)(apparent_power[i] * 100 / 1000)))) { // calculating reactive power only if current is greater than 0.005A and // difference between active and apparent power is greater than 1.5W or 1% - //reactive_power = (float)(RoundSqrtInt((uint64_t)(apparent_power * apparent_power * 100) - (uint64_t)(Energy.active_power[i] * Energy.active_power[i] * 100))) / 10; - float power_diff = apparent_power * apparent_power - Energy.active_power[i] * Energy.active_power[i]; + //reactive_power[i] = (float)(RoundSqrtInt((uint64_t)(apparent_power[i] * apparent_power[i] * 100) - (uint64_t)(Energy.active_power[i] * Energy.active_power[i] * 100))) / 10; + float power_diff = apparent_power[i] * apparent_power[i] - Energy.active_power[i] * Energy.active_power[i]; if (power_diff < 10737418) // 2^30 / 100 (RoundSqrtInt is limited to 2^30-1) - reactive_power = (float)(RoundSqrtInt((uint32_t)(power_diff * 100.0))) / 10.0; + reactive_power[i] = (float)(RoundSqrtInt((uint32_t)(power_diff * 100.0))) / 10.0; else - reactive_power = (float)(SqrtInt((uint32_t)(power_diff))); + reactive_power[i] = (float)(SqrtInt((uint32_t)(power_diff))); } } - dtostrfd(apparent_power, Settings->flag2.wattage_resolution, apparent_power_chr[i]); - dtostrfd(reactive_power, Settings->flag2.wattage_resolution, reactive_power_chr[i]); - dtostrfd(power_factor, 2, power_factor_chr[i]); } } - for (uint32_t i = 0; i < Energy.phase_count; i++) { - float frequency = Energy.frequency[i]; - if (isnan(Energy.frequency[i])) { - frequency = 0; - } - dtostrfd(frequency, Settings->flag2.frequency_resolution, frequency_chr[i]); - } } - char voltage_chr[Energy.phase_count][FLOATSZ]; - char current_chr[Energy.phase_count][FLOATSZ]; - char active_power_chr[Energy.phase_count][FLOATSZ]; -#if defined(SDM630_IMPORT) || defined(SDM72_IMPEXP) - char import_active_chr[Energy.phase_count][FLOATSZ]; -#endif // SDM630_IMPORT || SDM72_IMPEXP - char export_active_chr[Energy.phase_count][FLOATSZ]; + float active_power_sum = 0.0; + float energy_yesterday_ph[Energy.phase_count]; for (uint32_t i = 0; i < Energy.phase_count; i++) { - dtostrfd(Energy.voltage[i], Settings->flag2.voltage_resolution, voltage_chr[i]); - dtostrfd(Energy.current[i], Settings->flag2.current_resolution, current_chr[i]); - dtostrfd(Energy.active_power[i], Settings->flag2.wattage_resolution, active_power_chr[i]); -#if defined(SDM630_IMPORT) || defined(SDM72_IMPEXP) - dtostrfd(Energy.import_active[i], Settings->flag2.energy_resolution, import_active_chr[i]); -#endif // SDM630_IMPORT || SDM72_IMPEXP - dtostrfd(Energy.export_active[i], Settings->flag2.energy_resolution, export_active_chr[i]); + energy_yesterday_ph[i] = (float)Settings->energy_kWhyesterday_ph[i] / 100000; + + active_power_sum += Energy.active_power[i]; } - char energy_total_chr[FLOATSZ]; - dtostrfd(Energy.total, Settings->flag2.energy_resolution, energy_total_chr); - char energy_daily_chr[FLOATSZ]; - dtostrfd(Energy.daily, Settings->flag2.energy_resolution, energy_daily_chr); - char energy_yesterday_chr[FLOATSZ]; - dtostrfd((float)Settings->energy_kWhyesterday / 100000, Settings->flag2.energy_resolution, energy_yesterday_chr); - - bool energy_tariff = false; - char energy_usage_chr[2][FLOATSZ]; - char energy_return_chr[2][FLOATSZ]; + float energy_usage[2]; + float energy_return[2]; if (Settings->tariff[0][0] != Settings->tariff[1][0]) { - dtostrfd((float)RtcSettings.energy_usage.usage1_kWhtotal / 100000, Settings->flag2.energy_resolution, energy_usage_chr[0]); // Tariff1 - dtostrfd((float)RtcSettings.energy_usage.usage2_kWhtotal / 100000, Settings->flag2.energy_resolution, energy_usage_chr[1]); // Tariff2 - dtostrfd((float)RtcSettings.energy_usage.return1_kWhtotal / 100000, Settings->flag2.energy_resolution, energy_return_chr[0]); // Tariff1 - dtostrfd((float)RtcSettings.energy_usage.return2_kWhtotal / 100000, Settings->flag2.energy_resolution, energy_return_chr[1]); // Tariff2 + energy_usage[0] = (float)RtcSettings.energy_usage.usage1_kWhtotal / 100000; // Tariff1 + energy_usage[1] = (float)RtcSettings.energy_usage.usage2_kWhtotal / 100000; // Tariff2 + energy_return[0] = (float)RtcSettings.energy_usage.return1_kWhtotal / 100000; // Tariff1 + energy_return[1] = (float)RtcSettings.energy_usage.return2_kWhtotal / 100000; // Tariff2 energy_tariff = true; } @@ -1053,86 +1108,94 @@ void EnergyShow(bool json) ResponseAppend_P(PSTR(",\"" D_RSLT_ENERGY "\":{\"" D_JSON_TOTAL_START_TIME "\":\"%s\",\"" D_JSON_TOTAL "\":%s"), GetDateAndTime(DT_ENERGY).c_str(), - energy_total_chr); + EnergyFormatSum(value_chr, Energy.total, Settings->flag2.energy_resolution, json)); if (energy_tariff) { ResponseAppend_P(PSTR(",\"" D_JSON_TOTAL D_CMND_TARIFF "\":%s"), - EnergyFormatIndex(value_chr, energy_usage_chr[0], json, 2)); + EnergyFormatIndex(value_chr, energy_usage, Settings->flag2.energy_resolution, json, 2)); } ResponseAppend_P(PSTR(",\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s"), - energy_yesterday_chr, - energy_daily_chr); + EnergyFormatSum(value_chr, energy_yesterday_ph, Settings->flag2.energy_resolution, json), + EnergyFormatSum(value2_chr, Energy.daily, Settings->flag2.energy_resolution, json)); #if defined(SDM630_IMPORT) || defined(SDM72_IMPEXP) if (!isnan(Energy.import_active[0])) { ResponseAppend_P(PSTR(",\"" D_JSON_IMPORT_ACTIVE "\":%s"), - EnergyFormat(value_chr, import_active_chr[0], json)); + EnergyFormat(value_chr, Energy.import_active, Settings->flag2.energy_resolution, json)); if (energy_tariff) { ResponseAppend_P(PSTR(",\"" D_JSON_IMPORT D_CMND_TARIFF "\":%s"), - EnergyFormatIndex(value_chr, energy_return_chr[0], json, 2)); + EnergyFormatIndex(value_chr, energy_return, Settings->flag2.energy_resolution, json, 2)); } } #endif // SDM630_IMPORT || SDM72_IMPEXP if (!isnan(Energy.export_active[0])) { ResponseAppend_P(PSTR(",\"" D_JSON_EXPORT_ACTIVE "\":%s"), - EnergyFormat(value_chr, export_active_chr[0], json)); + EnergyFormat(value_chr, Energy.export_active, Settings->flag2.energy_resolution, json)); if (energy_tariff) { ResponseAppend_P(PSTR(",\"" D_JSON_EXPORT D_CMND_TARIFF "\":%s"), - EnergyFormatIndex(value_chr, energy_return_chr[0], json, 2)); + EnergyFormatIndex(value_chr, energy_return, Settings->flag2.energy_resolution, json, 2)); } } if (show_energy_period) { - float energy = (float)(RtcSettings.energy_kWhtoday - Energy.period) / 100; - Energy.period = RtcSettings.energy_kWhtoday; - char energy_period_chr[FLOATSZ]; - dtostrfd(energy, Settings->flag2.wattage_resolution, energy_period_chr); - ResponseAppend_P(PSTR(",\"" D_JSON_PERIOD "\":%s"), energy_period_chr); + float energy_period[Energy.phase_count]; + for (uint32_t i = 0; i < Energy.phase_count; i++) { + energy_period[i] = (float)(RtcSettings.energy_kWhtoday_ph[i] - Energy.period[i]) / 100; + Energy.period[i] = RtcSettings.energy_kWhtoday_ph[i]; + } + ResponseAppend_P(PSTR(",\"" D_JSON_PERIOD "\":%s"), + EnergyFormat(value_chr, energy_period, Settings->flag2.wattage_resolution, json)); } + ResponseAppend_P(PSTR(",\"" D_JSON_POWERUSAGE "\":%s"), - EnergyFormat(value_chr, active_power_chr[0], json)); + EnergyFormat(value_chr, Energy.active_power, Settings->flag2.wattage_resolution, json)); if (!Energy.type_dc) { if (Energy.current_available && Energy.voltage_available) { ResponseAppend_P(PSTR(",\"" D_JSON_APPARENT_POWERUSAGE "\":%s,\"" D_JSON_REACTIVE_POWERUSAGE "\":%s,\"" D_JSON_POWERFACTOR "\":%s"), - EnergyFormat(value_chr, apparent_power_chr[0], json), - EnergyFormat(value2_chr, reactive_power_chr[0], json), - EnergyFormat(value3_chr, power_factor_chr[0], json)); + EnergyFormat(value_chr, apparent_power, Settings->flag2.wattage_resolution, json), + EnergyFormat(value2_chr, reactive_power, Settings->flag2.wattage_resolution, json), + EnergyFormat(value3_chr, power_factor, 2, json)); } if (!isnan(Energy.frequency[0])) { ResponseAppend_P(PSTR(",\"" D_JSON_FREQUENCY "\":%s"), - EnergyFormat(value_chr, frequency_chr[0], json, Energy.frequency_common)); + EnergyFormat(value_chr, Energy.frequency, Settings->flag2.frequency_resolution, json, Energy.frequency_common)); } } if (Energy.voltage_available) { ResponseAppend_P(PSTR(",\"" D_JSON_VOLTAGE "\":%s"), - EnergyFormat(value_chr, voltage_chr[0], json, Energy.voltage_common)); + EnergyFormat(value_chr, Energy.voltage, Settings->flag2.voltage_resolution, json, Energy.voltage_common)); } if (Energy.current_available) { ResponseAppend_P(PSTR(",\"" D_JSON_CURRENT "\":%s"), - EnergyFormat(value_chr, current_chr[0], json)); + EnergyFormat(value_chr, Energy.current, Settings->flag2.current_resolution, json)); } XnrgCall(FUNC_JSON_APPEND); ResponseJsonEnd(); #ifdef USE_DOMOTICZ if (show_energy_period) { // Only send if telemetry - dtostrfd(Energy.total * 1000, 1, energy_total_chr); - DomoticzSensorPowerEnergy((int)Energy.active_power[0], energy_total_chr); // PowerUsage, EnergyToday + char temp_chr[FLOATSZ]; + if (Energy.voltage_available) { + dtostrfd(Energy.voltage[0], Settings->flag2.voltage_resolution, temp_chr); + DomoticzSensor(DZ_VOLTAGE, temp_chr); // Voltage + } + if (Energy.current_available) { + dtostrfd(Energy.current[0], Settings->flag2.current_resolution, temp_chr); + DomoticzSensor(DZ_CURRENT, temp_chr); // Current + } + dtostrfd(Energy.total_sum * 1000, 1, temp_chr); + DomoticzSensorPowerEnergy((int)active_power_sum, temp_chr); // PowerUsage, EnergyToday + char energy_usage_chr[2][FLOATSZ]; + char energy_return_chr[2][FLOATSZ]; dtostrfd((float)RtcSettings.energy_usage.usage1_kWhtotal / 100, 1, energy_usage_chr[0]); // Tariff1 dtostrfd((float)RtcSettings.energy_usage.usage2_kWhtotal / 100, 1, energy_usage_chr[1]); // Tariff2 dtostrfd((float)RtcSettings.energy_usage.return1_kWhtotal / 100, 1, energy_return_chr[0]); dtostrfd((float)RtcSettings.energy_usage.return2_kWhtotal / 100, 1, energy_return_chr[1]); - DomoticzSensorP1SmartMeter(energy_usage_chr[0], energy_usage_chr[1], energy_return_chr[0], energy_return_chr[1], (int)Energy.active_power[0]); + DomoticzSensorP1SmartMeter(energy_usage_chr[0], energy_usage_chr[1], energy_return_chr[0], energy_return_chr[1], (int)active_power_sum); - if (Energy.voltage_available) { - DomoticzSensor(DZ_VOLTAGE, voltage_chr[0]); // Voltage - } - if (Energy.current_available) { - DomoticzSensor(DZ_CURRENT, current_chr[0]); // Current - } } #endif // USE_DOMOTICZ #ifdef USE_KNX @@ -1143,42 +1206,46 @@ void EnergyShow(bool json) if (Energy.current_available) { KnxSensor(KNX_ENERGY_CURRENT, Energy.current[0]); } - KnxSensor(KNX_ENERGY_POWER, Energy.active_power[0]); + KnxSensor(KNX_ENERGY_POWER, active_power_sum); if (!Energy.type_dc) { - KnxSensor(KNX_ENERGY_POWERFACTOR, power_factor_knx); + KnxSensor(KNX_ENERGY_POWERFACTOR, power_factor[0]); } - KnxSensor(KNX_ENERGY_DAILY, Energy.daily); - KnxSensor(KNX_ENERGY_TOTAL, Energy.total); - KnxSensor(KNX_ENERGY_YESTERDAY, (float)Settings->energy_kWhyesterday / 100000); + KnxSensor(KNX_ENERGY_DAILY, Energy.daily_sum); + KnxSensor(KNX_ENERGY_TOTAL, Energy.total_sum); + KnxSensor(KNX_ENERGY_YESTERDAY, Energy.yesterday_sum); } #endif // USE_KNX #ifdef USE_WEBSERVER } else { if (Energy.voltage_available) { - WSContentSend_PD(HTTP_SNS_VOLTAGE, EnergyFormat(value_chr, voltage_chr[0], json, Energy.voltage_common)); + WSContentSend_PD(HTTP_SNS_VOLTAGE, EnergyFormat(value_chr, Energy.voltage, Settings->flag2.voltage_resolution, json, Energy.voltage_common)); } - if (Energy.current_available) { - WSContentSend_PD(HTTP_SNS_CURRENT, EnergyFormat(value_chr, current_chr[0], json)); - } - WSContentSend_PD(HTTP_SNS_POWER, EnergyFormat(value_chr, active_power_chr[0], json)); if (!Energy.type_dc) { - if (Energy.current_available && Energy.voltage_available) { - WSContentSend_PD(HTTP_ENERGY_SNS1, EnergyFormat(value_chr, apparent_power_chr[0], json), - EnergyFormat(value2_chr, reactive_power_chr[0], json), - EnergyFormat(value3_chr, power_factor_chr[0], json)); - } if (!isnan(Energy.frequency[0])) { WSContentSend_PD(PSTR("{s}" D_FREQUENCY "{m}%s " D_UNIT_HERTZ "{e}"), - EnergyFormat(value_chr, frequency_chr[0], json, Energy.frequency_common)); + EnergyFormat(value_chr, Energy.frequency, Settings->flag2.frequency_resolution, json, Energy.frequency_common)); } } - WSContentSend_PD(HTTP_ENERGY_SNS2, energy_daily_chr, energy_yesterday_chr, energy_total_chr); + if (Energy.current_available) { + WSContentSend_PD(HTTP_SNS_CURRENT, EnergyFormat(value_chr, Energy.current, Settings->flag2.current_resolution, json)); + } + WSContentSend_PD(HTTP_SNS_POWER, EnergyFormat(value_chr, Energy.active_power, Settings->flag2.wattage_resolution, json)); + if (!Energy.type_dc) { + if (Energy.current_available && Energy.voltage_available) { + WSContentSend_PD(HTTP_ENERGY_SNS1, EnergyFormat(value_chr, apparent_power, Settings->flag2.wattage_resolution, json), + EnergyFormat(value2_chr, reactive_power, Settings->flag2.wattage_resolution, json), + EnergyFormat(value3_chr, power_factor, 2, json)); + } + } + WSContentSend_PD(HTTP_ENERGY_SNS2, EnergyFormatSum(value_chr, Energy.daily, Settings->flag2.energy_resolution, json), + EnergyFormatSum(value2_chr, energy_yesterday_ph, Settings->flag2.energy_resolution, json), + EnergyFormatSum(value3_chr, Energy.total, Settings->flag2.energy_resolution, json)); if (!isnan(Energy.export_active[0])) { - WSContentSend_PD(HTTP_ENERGY_SNS3, EnergyFormat(value_chr, export_active_chr[0], json)); + WSContentSend_PD(HTTP_ENERGY_SNS3, EnergyFormat(value_chr, Energy.export_active, Settings->flag2.energy_resolution, json)); } #if defined(SDM630_IMPORT) || defined(SDM72_IMPEXP) if (!isnan(Energy.import_active[0])) { - WSContentSend_PD(HTTP_ENERGY_SNS4, EnergyFormat(value_chr, import_active_chr[0], json)); + WSContentSend_PD(HTTP_ENERGY_SNS4, EnergyFormat(value_chr, Energy.import_active, Settings->flag2.energy_resolution, json)); } #endif // SDM630_IMPORT || SDM72_IMPEXP diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index 7d8578129..c4d21a723 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -1946,7 +1946,7 @@ chknext: while (*lp==' ') lp++; switch ((uint32_t)fvar) { case 0: - fvar = Energy.total; + fvar = Energy.total_sum; break; case 1: fvar = Energy.voltage[0]; @@ -1976,13 +1976,13 @@ chknext: fvar = Energy.active_power[2]; break; case 10: - fvar = Energy.start_energy; + fvar = Energy.start_energy[0]; break; case 11: - fvar = Energy.daily; + fvar = Energy.daily_sum; break; case 12: - fvar = (float)Settings->energy_kWhyesterday/100000.0; + fvar = Energy.yesterday_sum; break; default: diff --git a/tasmota/xdrv_11_knx.ino b/tasmota/xdrv_11_knx.ino index 12d663d9d..84f5e867e 100644 --- a/tasmota/xdrv_11_knx.ino +++ b/tasmota/xdrv_11_knx.ino @@ -645,7 +645,7 @@ void KNX_CB_Action(message_t const &msg, void *arg) } } else if (chan->type == KNX_ENERGY_VOLTAGE) // Reply KNX_ENERGY_VOLTAGE - { + { knx.answer_4byte_float(msg.received_on, Energy.voltage[0]); if (Settings->flag.knx_enable_enhancement) { knx.answer_4byte_float(msg.received_on, Energy.voltage[0]); @@ -678,27 +678,26 @@ void KNX_CB_Action(message_t const &msg, void *arg) } else if (chan->type == KNX_ENERGY_YESTERDAY) // Reply KNX_ENERGY_YESTERDAY { - float energy_kWhyesterday = (float)Settings->energy_kWhyesterday / 100000; - knx.answer_4byte_float(msg.received_on, energy_kWhyesterday); + knx.answer_4byte_float(msg.received_on, Energy.yesterday_sum); if (Settings->flag.knx_enable_enhancement) { - knx.answer_4byte_float(msg.received_on, energy_kWhyesterday); - knx.answer_4byte_float(msg.received_on, energy_kWhyesterday); + knx.answer_4byte_float(msg.received_on, Energy.yesterday_sum); + knx.answer_4byte_float(msg.received_on, Energy.yesterday_sum); } } else if (chan->type == KNX_ENERGY_DAILY) // Reply KNX_ENERGY_DAILY { - knx.answer_4byte_float(msg.received_on, Energy.daily); + knx.answer_4byte_float(msg.received_on, Energy.daily_sum); if (Settings->flag.knx_enable_enhancement) { - knx.answer_4byte_float(msg.received_on, Energy.daily); - knx.answer_4byte_float(msg.received_on, Energy.daily); + knx.answer_4byte_float(msg.received_on, Energy.daily_sum); + knx.answer_4byte_float(msg.received_on, Energy.daily_sum); } } else if (chan->type == KNX_ENERGY_TOTAL) // Reply KNX_ENERGY_TOTAL { - knx.answer_4byte_float(msg.received_on, Energy.total); + knx.answer_4byte_float(msg.received_on, Energy.total_sum); if (Settings->flag.knx_enable_enhancement) { - knx.answer_4byte_float(msg.received_on, Energy.total); - knx.answer_4byte_float(msg.received_on, Energy.total); + knx.answer_4byte_float(msg.received_on, Energy.total_sum); + knx.answer_4byte_float(msg.received_on, Energy.total_sum); } } #ifdef USE_RULES diff --git a/tasmota/xdrv_16_tuyamcu.ino b/tasmota/xdrv_16_tuyamcu.ino index 53dcc770e..395fdef57 100644 --- a/tasmota/xdrv_16_tuyamcu.ino +++ b/tasmota/xdrv_16_tuyamcu.ino @@ -753,7 +753,7 @@ void TuyaProcessStatePacket(void) { if (RtcTime.valid) { if (Tuya.lastPowerCheckTime != 0 && Energy.active_power[0] > 0) { - Energy.kWhtoday += Energy.active_power[0] * (float)(Rtc.utc_time - Tuya.lastPowerCheckTime) / 36.0; + Energy.kWhtoday[0] += Energy.active_power[0] * (float)(Rtc.utc_time - Tuya.lastPowerCheckTime) / 36.0; EnergyUpdateToday(); } Tuya.lastPowerCheckTime = Rtc.utc_time; @@ -885,7 +885,7 @@ void TuyaProcessStatePacket(void) { if (RtcTime.valid) { if (Tuya.lastPowerCheckTime != 0 && Energy.active_power[0] > 0) { - Energy.kWhtoday += Energy.active_power[0] * (float)(Rtc.utc_time - Tuya.lastPowerCheckTime) / 36.0; + Energy.kWhtoday[0] += Energy.active_power[0] * (float)(Rtc.utc_time - Tuya.lastPowerCheckTime) / 36.0; EnergyUpdateToday(); } Tuya.lastPowerCheckTime = Rtc.utc_time; diff --git a/tasmota/xdrv_45_shelly_dimmer.ino b/tasmota/xdrv_45_shelly_dimmer.ino index b96b83d4b..d1f317bfc 100644 --- a/tasmota/xdrv_45_shelly_dimmer.ino +++ b/tasmota/xdrv_45_shelly_dimmer.ino @@ -540,7 +540,7 @@ bool ShdPacketProcess(void) #ifdef SHELLY_DIMMER_DEBUG AddLog(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "%4_f W is %u dmWh during %u ms"), &Energy.active_power[0], deca_microWh, time_passed); #endif // SHELLY_DIMMER_DEBUG - Energy.kWhtoday_delta += deca_microWh; + Energy.kWhtoday_delta[0] += deca_microWh; EnergyUpdateToday(); } Shd.last_power_check = millis(); diff --git a/tasmota/xnrg_01_hlw8012.ino b/tasmota/xnrg_01_hlw8012.ino index c494a0b46..310e64360 100644 --- a/tasmota/xnrg_01_hlw8012.ino +++ b/tasmota/xnrg_01_hlw8012.ino @@ -214,7 +214,7 @@ void HlwEverySecond(void) { hlw_len = 10000 * 100 / Hlw.energy_period_counter; // Add *100 to fix rounding on loads at 3.6kW (#9160) Hlw.energy_period_counter = 0; if (hlw_len) { - Energy.kWhtoday_delta += (((Hlw.power_ratio * Settings->energy_power_calibration) / 36) * 100) / hlw_len; + Energy.kWhtoday_delta[0] += (((Hlw.power_ratio * Settings->energy_power_calibration) / 36) * 100) / hlw_len; EnergyUpdateToday(); } } diff --git a/tasmota/xnrg_02_cse7766.ino b/tasmota/xnrg_02_cse7766.ino index 9fc73549a..445781b37 100644 --- a/tasmota/xnrg_02_cse7766.ino +++ b/tasmota/xnrg_02_cse7766.ino @@ -205,7 +205,7 @@ void CseEverySecond(void) { // prevent invalid load delta steps even checksum is valid but allow up to 4kW (issue #7155): if (delta <= (4000 * 1000 / 36)) { // max load for S31/Pow R2: 4.00kW Cse.cf_pulses_last_time = Cse.cf_pulses; - Energy.kWhtoday_delta += delta; + Energy.kWhtoday_delta[0] += delta; } else { AddLog(LOG_LEVEL_DEBUG, PSTR("CSE: Overload")); diff --git a/tasmota/xnrg_03_pzem004t.ino b/tasmota/xnrg_03_pzem004t.ino index 1abd8d95a..d132fccfa 100644 --- a/tasmota/xnrg_03_pzem004t.ino +++ b/tasmota/xnrg_03_pzem004t.ino @@ -192,6 +192,7 @@ void PzemEvery250ms(void) Energy.active_power[Pzem.phase] = value; break; case 4: // Total energy as 99999Wh +/* Pzem.energy += value; if (Pzem.phase == Energy.phase_count -1) { if (Pzem.energy > Pzem.last_energy) { // Handle missed phase @@ -202,6 +203,10 @@ void PzemEvery250ms(void) } Pzem.energy = 0; } +*/ + if (TasmotaGlobal.uptime > PZEM_STABILIZE) { + EnergyUpdateTotal(value, false, Pzem.phase); + } break; } Pzem.read_state++; diff --git a/tasmota/xnrg_04_mcp39f501.ino b/tasmota/xnrg_04_mcp39f501.ino index 9f9ea562e..7601731a4 100644 --- a/tasmota/xnrg_04_mcp39f501.ino +++ b/tasmota/xnrg_04_mcp39f501.ino @@ -534,7 +534,7 @@ void McpEverySecond(void) } if (mcp_active_power) { - Energy.kWhtoday_delta += ((mcp_active_power * 10) / 36); + Energy.kWhtoday_delta[0] += ((mcp_active_power * 10) / 36); EnergyUpdateToday(); } diff --git a/tasmota/xnrg_05_pzem_ac.ino b/tasmota/xnrg_05_pzem_ac.ino index acf826c69..ecf67fab0 100644 --- a/tasmota/xnrg_05_pzem_ac.ino +++ b/tasmota/xnrg_05_pzem_ac.ino @@ -78,6 +78,7 @@ void PzemAcEverySecond(void) Energy.frequency[PzemAc.phase] = (float)((buffer[17] << 8) + buffer[18]) / 10.0; // 50.0 Hz Energy.power_factor[PzemAc.phase] = (float)((buffer[19] << 8) + buffer[20]) / 100.0; // 1.00 +/* PzemAc.energy += (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); // 4294967295 Wh if (PzemAc.phase == Energy.phase_count -1) { if (PzemAc.energy > PzemAc.last_energy) { // Handle missed phase @@ -88,7 +89,11 @@ void PzemAcEverySecond(void) } PzemAc.energy = 0; } - +*/ + float energy = (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); // 4294967295 Wh + if (TasmotaGlobal.uptime > PZEM_AC_STABILIZE) { + EnergyUpdateTotal(energy, false, PzemAc.phase); + } } } } diff --git a/tasmota/xnrg_06_pzem_dc.ino b/tasmota/xnrg_06_pzem_dc.ino index df17d2c82..dab53a2ec 100644 --- a/tasmota/xnrg_06_pzem_dc.ino +++ b/tasmota/xnrg_06_pzem_dc.ino @@ -74,7 +74,7 @@ void PzemDcEverySecond(void) Energy.voltage[PzemDc.channel] = (float)((buffer[3] << 8) + buffer[4]) / 100.0; // 655.00 V Energy.current[PzemDc.channel] = (float)((buffer[5] << 8) + buffer[6]) / 100.0; // 655.00 A Energy.active_power[PzemDc.channel] = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0; // 429496729.0 W - +/* PzemDc.energy += (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]); // 4294967295 Wh if (PzemDc.channel == Energy.phase_count -1) { if (PzemDc.energy > PzemDc.last_energy) { // Handle missed channel @@ -85,6 +85,11 @@ void PzemDcEverySecond(void) } PzemDc.energy = 0; } +*/ + float energy = (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]); // 4294967295 Wh + if (TasmotaGlobal.uptime > PZEM_DC_STABILIZE) { + EnergyUpdateTotal(energy, false, PzemDc.channel); + } } } } diff --git a/tasmota/xnrg_07_ade7953.ino b/tasmota/xnrg_07_ade7953.ino index f4270deaf..a09e4f257 100644 --- a/tasmota/xnrg_07_ade7953.ino +++ b/tasmota/xnrg_07_ade7953.ino @@ -180,12 +180,11 @@ void Ade7953GetData(void) } uint32_t current_rms_sum = Ade7953.current_rms[0] + Ade7953.current_rms[1]; - uint32_t active_power_sum = Ade7953.active_power[0] + Ade7953.active_power[1]; - AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("ADE: U %d, C %d, I %d + %d = %d, P %d + %d = %d"), + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("ADE: U %d, C %d, I %d + %d = %d, P %d + %d"), Ade7953.voltage_rms, Ade7953.period, Ade7953.current_rms[0], Ade7953.current_rms[1], current_rms_sum, - Ade7953.active_power[0], Ade7953.active_power[1], active_power_sum); + Ade7953.active_power[0], Ade7953.active_power[1]); if (Energy.power_on) { // Powered on Energy.voltage[0] = (float)Ade7953.voltage_rms / Settings->energy_voltage_calibration; @@ -208,19 +207,16 @@ void Ade7953GetData(void) Energy.current[channel] = 0; } else { Energy.current[channel] = (float)Ade7953.current_rms[channel] / (Settings->energy_current_calibration * 10); + Energy.kWhtoday_delta[channel] += Energy.active_power[channel] * 1000 / 36; } } + EnergyUpdateToday(); /* } else { // Powered off Energy.data_valid[0] = ENERGY_WATCHDOG; Energy.data_valid[1] = ENERGY_WATCHDOG; */ } - - if (active_power_sum) { - Energy.kWhtoday_delta += ((active_power_sum * (100000 / (Settings->energy_power_calibration / 10))) / 3600); - EnergyUpdateToday(); - } } void Ade7953EnergyEverySecond(void) diff --git a/tasmota/xnrg_14_bl09xx.ino b/tasmota/xnrg_14_bl09xx.ino index 68534d425..db064af6a 100644 --- a/tasmota/xnrg_14_bl09xx.ino +++ b/tasmota/xnrg_14_bl09xx.ino @@ -212,43 +212,10 @@ void Bl09XXEverySecond(void) { memset(Bl09XX.power, 0, sizeof(Bl09XX.power)); } else { // Calculate energy by using active power - uint32_t energy_sum = 0; for (uint32_t channel = 0; channel < Energy.phase_count; channel++) { - energy_sum += (Energy.active_power[channel] * 1000); + Energy.kWhtoday_delta[channel] += Energy.active_power[channel] * 1000 / 36; } - if (energy_sum) { - Energy.kWhtoday_delta += energy_sum / 36; - EnergyUpdateToday(); - } -/* - // Calculate energy by using active energy pulse count - bool update_today = false; - for (int chan = 0 ; chan < Energy.phase_count ; chan++ ) { - if (BL09XX_PULSES_NOT_INITIALIZED == Bl09XX.cf_pulses_last_time[chan]) { - Bl09XX.cf_pulses_last_time[chan] = Bl09XX.cf_pulses[chan]; // Init after restart - } else { - uint32_t cf_pulses = 0; - if (Bl09XX.cf_pulses[chan] < Bl09XX.cf_pulses_last_time[chan]) { // Rolled over after 0xFFFFFF (16777215) pulses - cf_pulses = (0x1000000 - Bl09XX.cf_pulses_last_time[chan]) + Bl09XX.cf_pulses[chan]; - } else { - cf_pulses = Bl09XX.cf_pulses[chan] - Bl09XX.cf_pulses_last_time[chan]; - } - if (cf_pulses && Energy.active_power[chan]) { - uint32_t watt256 = (1638400 * 256) / Settings->energy_power_calibration; - uint32_t delta = (cf_pulses * watt256) / 36; - if (delta <= (4000 * 1000 / 36)) { // max load for SHP10: 4.00kW (3.68kW) - Bl09XX.cf_pulses_last_time[chan] = Bl09XX.cf_pulses[chan]; - } else { - AddLog(LOG_LEVEL_DEBUG, PSTR("BL9: Overload [%d] %d"), chan, delta); - Bl09XX.cf_pulses_last_time[chan] = BL09XX_PULSES_NOT_INITIALIZED; - } - update_today = true; - } - } - } - if (update_today) - EnergyUpdateToday(); -*/ + EnergyUpdateToday(); } // AddLog(LOG_LEVEL_DEBUG, PSTR("BL9: Poll")); diff --git a/tasmota/xnrg_19_cse7761.ino b/tasmota/xnrg_19_cse7761.ino index 2bf593a03..c61322d30 100644 --- a/tasmota/xnrg_19_cse7761.ino +++ b/tasmota/xnrg_19_cse7761.ino @@ -95,7 +95,7 @@ struct { uint32_t energy[2] = { 0 }; uint32_t active_power[2] = { 0 }; uint16_t coefficient[8] = { 0 }; - uint8_t energy_update = 0; + uint8_t energy_update[2] = { 0 }; uint8_t init = 4; uint8_t ready = 0; } CSE7761Data; @@ -476,7 +476,7 @@ void Cse7761GetData(void) { // Energy.current[channel] = (float)(((uint64_t)CSE7761Data.current_rms[channel] * CSE7761Data.coefficient[RmsIAC + channel]) >> 23) / 1000; // A Energy.current[channel] = (float)CSE7761Data.current_rms[channel] / Settings->energy_current_calibration; // A CSE7761Data.energy[channel] += Energy.active_power[channel]; - CSE7761Data.energy_update++; + CSE7761Data.energy_update[channel]++; } } } @@ -557,16 +557,14 @@ void Cse7761EverySecond(void) { } else { if (2 == CSE7761Data.ready) { - if (CSE7761Data.energy_update) { - uint32_t energy_sum = ((CSE7761Data.energy[0] + CSE7761Data.energy[1]) * 1000) / CSE7761Data.energy_update; - if (energy_sum) { - Energy.kWhtoday_delta += energy_sum / 36; - EnergyUpdateToday(); + for (uint32_t channel = 0; channel < 2; channel++) { + if (CSE7761Data.energy_update[channel]) { + Energy.kWhtoday_delta[channel] += ((CSE7761Data.energy[channel] * 1000) / CSE7761Data.energy_update[channel]) / 36; + CSE7761Data.energy[channel] = 0; + CSE7761Data.energy_update[channel] = 0; } + EnergyUpdateToday(); } - CSE7761Data.energy[0] = 0; - CSE7761Data.energy[1] = 0; - CSE7761Data.energy_update = 0; } } } diff --git a/tasmota/xnrg_30_dummy.ino b/tasmota/xnrg_30_dummy.ino index dd8c77848..4aa308fdb 100644 --- a/tasmota/xnrg_30_dummy.ino +++ b/tasmota/xnrg_30_dummy.ino @@ -44,7 +44,6 @@ void NrgDummyEverySecond(void) { if (Energy.power_on) { // Powered on - float energy = 0; for (uint32_t channel = 0; channel < Energy.phase_count; channel++) { Energy.voltage[channel] = ((float)Settings->energy_voltage_calibration / 100); // V Energy.frequency[channel] = ((float)Settings->energy_frequency_calibration / 100); // Hz @@ -54,16 +53,12 @@ void NrgDummyEverySecond(void) { Energy.current[channel] = 0; } else { Energy.current[channel] = ((float)Settings->energy_current_calibration / 100000); // A - energy += Energy.active_power[channel]; + Energy.kWhtoday_delta[channel] += Energy.active_power[channel] * 1000 / 36; } Energy.data_valid[channel] = 0; } } - - if (energy > 0) { - Energy.kWhtoday_delta += energy * 1000 / 36; - EnergyUpdateToday(); - } + EnergyUpdateToday(); } } diff --git a/tasmota/xsns_75_prometheus.ino b/tasmota/xsns_75_prometheus.ino index 1d38f7b03..caa2330fa 100644 --- a/tasmota/xsns_75_prometheus.ino +++ b/tasmota/xsns_75_prometheus.ino @@ -268,10 +268,10 @@ void HandleMetrics(void) { Energy.active_power[0], Settings->flag2.wattage_resolution, nullptr); WritePromMetricDec(PSTR("energy_power_kilowatts_daily"), kPromMetricCounter, - Energy.daily, Settings->flag2.energy_resolution, nullptr); + Energy.daily_sum, Settings->flag2.energy_resolution, nullptr); WritePromMetricDec(PSTR("energy_power_kilowatts_total"), kPromMetricCounter, - Energy.total, Settings->flag2.energy_resolution, nullptr); + Energy.total_sum, Settings->flag2.energy_resolution, nullptr); #endif for (uint32_t device = 0; device < TasmotaGlobal.devices_present; device++) {