Add energy total spilt

This commit is contained in:
Theo Arends 2021-09-29 15:33:58 +02:00
parent 9b35c54ed1
commit b47e91c8ab
20 changed files with 353 additions and 308 deletions

View File

@ -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<phase>``, ``EnergyToday<phase>`` and ``EnergyYesterday<phase>`` to (re)set energy values
## [9.5.0.8] 20210927
### Added

View File

@ -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 <url>`` if ``#define USE_WEBGETCONFIG`` is enabled to restore/init configuration from external webserver [#13034](https://github.com/arendst/Tasmota/issues/13034)
- Command ``WebQuery <url> GET|POST|PUT|PATCH [<headers>] <body>`` to extent HTTP requests [#13209](https://github.com/arendst/Tasmota/issues/13209)
- Commands ``EnergyTotal<phase>``, ``EnergyToday<phase>`` and ``EnergyYesterday<phase>`` 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

View File

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

View File

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

View File

@ -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++) {
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]);
}
}
}
float active_power_sum = 0.0;
float energy_yesterday_ph[Energy.phase_count];
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]);
}
}
energy_yesterday_ph[i] = (float)Settings->energy_kWhyesterday_ph[i] / 100000;
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];
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]);
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -212,44 +212,11 @@ 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();
*/
}
// AddLog(LOG_LEVEL_DEBUG, PSTR("BL9: Poll"));

View File

@ -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,17 +557,15 @@ 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;
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;
}
}
}

View File

@ -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,18 +53,14 @@ 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();
}
}
}
bool NrgDummyCommand(void) {
bool serviced = true;

View File

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