From ab2d6c1169025cc7434668def509c5d41d169a36 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 20 Aug 2024 13:08:56 +0200 Subject: [PATCH] Add Energy command ``PowerSet 60,230`` to calibrate both Current and Power with known resistive load of 60W at 230V using calibrated Voltage --- CHANGELOG.md | 5 +- RELEASENOTES.md | 5 +- .../tasmota_xdrv_driver/xdrv_03_energy.ino | 56 ++++++++++++++++++- .../xdrv_03_esp32_energy.ino | 56 ++++++++++++++++++- 4 files changed, 112 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 037cca6ce..443243fee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,13 @@ All notable changes to this project will be documented in this file. ## [14.2.0.1] ### Added - Energy Log level 4 message when (Calculated) Apparent Power is less than Active Power indicating wrong calibration (#20653) +- Energy command ``PowerSet 60,230`` to calibrate both Current and Power with known resistive load of 60W at 230V using calibrated Voltage +- Energy command ``CurrentSet 60,230`` to calibrate both Power and Current with known resistive load of 60W at 230V using calibrated Voltage ### Breaking Changed ### Changed +- Energy force Apparent Power equals Active Power when (Calculated) Apparent Power is less than Active Power (#20653) ### Fixed - Shutter timing registers overflow (#21966) @@ -18,8 +21,6 @@ All notable changes to this project will be documented in this file. ### Removed - ESP8266 Analog input support using energy driver as only one channel is available -- Energy force Active Power equals Apparent Power when (Calculated) Apparent Power is less than Active Power (#20653) -- Energy force Power Factor to be always 1 or lower (#20653) ## [Released] diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 395314a5b..6167cb6cd 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -121,11 +121,14 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm ## Changelog v14.2.0.1 ### Added +- Energy command ``PowerSet 60,230`` to calibrate both Current and Power with known resistive load of 60W at 230V using calibrated Voltage +- Energy command ``CurrentSet 60,230`` to calibrate both Power and Current with known resistive load of 60W at 230V using calibrated Voltage - Energy Log level 4 message when (Calculated) Apparent Power is less than Active Power indicating wrong calibration [#20653](https://github.com/arendst/Tasmota/issues/20653) ### Breaking Changed ### Changed +- Energy force Apparent Power equals Active Power when (Calculated) Apparent Power is less than Active Power [#20653](https://github.com/arendst/Tasmota/issues/20653) ### Fixed - Energy calculation [#20653](https://github.com/arendst/Tasmota/issues/20653) @@ -133,6 +136,4 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm - PZEM continue energy monitoring when one phase fails [#21968](https://github.com/arendst/Tasmota/issues/21968) ### Removed -- Energy force Active Power equals Apparent Power when (Calculated) Apparent Power is less than Active Power [#20653](https://github.com/arendst/Tasmota/issues/20653) -- Energy force Power Factor to be always 1 or lower [#20653](https://github.com/arendst/Tasmota/issues/20653) - ESP8266 Analog input support using energy driver as only one channel is available diff --git a/tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino b/tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino index d20115a9f..f8ba97b46 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino @@ -974,14 +974,56 @@ void EnergyCommandSetCalResponse(uint32_t cal_type) { } } +void EnergyCommandSetCal(uint32_t cal_type) { + if (XdrvMailbox.data_len) { + // PowerSet 61.2 + // CurrentSet 263 + if (ArgC() > 1) { + // Calibrate current and power using calibrated voltage and known resistive load voltage and power + // PowerSet 60.0,230 + // CurrentSet 60.0,230 + char argument[32]; + float Pgoal = CharToFloat(ArgV(argument, 1)); // 60.0 W + float Ugoal = CharToFloat(ArgV(argument, 2)); // 230 V + float Igoal = Pgoal / Ugoal; // 0.26087 A + float R = Ugoal / Igoal; // 881,666 Ohm + + uint32_t channel = XdrvMailbox.index; + if (channel > Energy->phase_count) { channel = 1; } + channel--; + float Umeas = Energy->voltage[channel]; // 232.0 + // Calculate current and power based on measured voltage + float Ical = Umeas / R; // 0.26306 A + float Pcal = Umeas * Ical; // 61.03 W + Ical *= 1000; // A to mA + + uint32_t cal_type1 = ENERGY_CURRENT_CALIBRATION; + float cal1 = Ical; + float cal2 = Pcal; + if (ENERGY_CURRENT_CALIBRATION == cal_type) { + cal_type1 = ENERGY_POWER_CALIBRATION; + cal1 = Pcal; + cal2 = Ical; + } + XdrvMailbox.data = argument; + ext_snprintf_P(argument, sizeof(argument), PSTR("%5_f"), &cal1); + XdrvMailbox.data_len = strlen(argument); + EnergyCommandSetCalResponse(cal_type1); + ext_snprintf_P(argument, sizeof(argument), PSTR("%5_f"), &cal2); + XdrvMailbox.data_len = strlen(argument); + } + } + EnergyCommandSetCalResponse(cal_type); +} + void CmndPowerSet(void) { - EnergyCommandSetCalResponse(ENERGY_POWER_CALIBRATION); + EnergyCommandSetCal(ENERGY_POWER_CALIBRATION); } void CmndVoltageSet(void) { EnergyCommandSetCalResponse(ENERGY_VOLTAGE_CALIBRATION); } void CmndCurrentSet(void) { - EnergyCommandSetCalResponse(ENERGY_CURRENT_CALIBRATION); + EnergyCommandSetCal(ENERGY_CURRENT_CALIBRATION); } void CmndFrequencySet(void) { EnergyCommandSetCalResponse(ENERGY_FREQUENCY_CALIBRATION); @@ -1184,6 +1226,10 @@ void EnergyShow(bool json) { if (!Energy->type_dc) { if (Energy->current_available && Energy->voltage_available) { for (uint32_t i = 0; i < Energy->phase_count; i++) { + if (0 == Energy->current[i]) { + Energy->active_power[i] = 0; + } + apparent_power[i] = Energy->apparent_power[i]; if (isnan(apparent_power[i])) { apparent_power[i] = Energy->voltage[i] * Energy->current[i]; @@ -1209,11 +1255,15 @@ void EnergyShow(bool json) { } if (apparent_power[i] < Energy->active_power[i]) { // Should be impossible if (apparent_power[i]) { - if ((power_factor[i] > 1.005) && (power_factor[i] < 2.0f)) { // Skip below 0.5% and don't expect 50% differences + if ((power_factor[i] >= 1.02f) && (power_factor[i] < 2.0f)) { // Skip below 2% and don't expect 50% differences AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("NRG: Calibrate as Active %3_fW > Apparent %3_fVA (PF = %4_f)"), &Energy->active_power[i], &apparent_power[i], &power_factor[i]); } } + apparent_power[i] = Energy->active_power[i]; // Force apparent equal to active as mis-calibrated + if (power_factor[i] > 1) { // Should not happen (Active > Apparent) + power_factor[i] = 1; + } } reactive_power[i] = Energy->reactive_power[i]; diff --git a/tasmota/tasmota_xdrv_driver/xdrv_03_esp32_energy.ino b/tasmota/tasmota_xdrv_driver/xdrv_03_esp32_energy.ino index 3fc271c60..848f99d1c 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_03_esp32_energy.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_03_esp32_energy.ino @@ -1285,14 +1285,56 @@ void EnergyCommandSetCalResponse(uint32_t cal_type) { } } +void EnergyCommandSetCal(uint32_t cal_type) { + if (XdrvMailbox.data_len && (XdrvMailbox.index > 0)) { + // PowerSet 61.2 + // CurrentSet 263 + if (ArgC() > 1) { + // Calibrate current and power using calibrated voltage and known resistive load voltage and power + // PowerSet 60.0,230 + // CurrentSet 60.0,230 + char argument[32]; + float Pgoal = CharToFloat(ArgV(argument, 1)); // 60.0 W + float Ugoal = CharToFloat(ArgV(argument, 2)); // 230 V + float Igoal = Pgoal / Ugoal; // 0.26087 A + float R = Ugoal / Igoal; // 881,666 Ohm + + uint32_t channel = XdrvMailbox.index; + if (channel > Energy->phase_count) { channel = 1; } + channel--; + float Umeas = Energy->voltage[channel]; // 232.0 + // Calculate current and power based on measured voltage + float Ical = Umeas / R; // 0.26306 A + float Pcal = Umeas * Ical; // 61.03 W + Ical *= 1000; // A to mA + + uint32_t cal_type1 = ENERGY_CURRENT_CALIBRATION; + float cal1 = Ical; + float cal2 = Pcal; + if (ENERGY_CURRENT_CALIBRATION == cal_type) { + cal_type1 = ENERGY_POWER_CALIBRATION; + cal1 = Pcal; + cal2 = Ical; + } + XdrvMailbox.data = argument; + ext_snprintf_P(argument, sizeof(argument), PSTR("%5_f"), &cal1); + XdrvMailbox.data_len = strlen(argument); + EnergyCommandSetCalResponse(cal_type1); + ext_snprintf_P(argument, sizeof(argument), PSTR("%5_f"), &cal2); + XdrvMailbox.data_len = strlen(argument); + } + } + EnergyCommandSetCalResponse(cal_type); +} + void CmndPowerSet(void) { - EnergyCommandSetCalResponse(ENERGY_POWER_CALIBRATION); + EnergyCommandSetCal(ENERGY_POWER_CALIBRATION); } void CmndVoltageSet(void) { EnergyCommandSetCalResponse(ENERGY_VOLTAGE_CALIBRATION); } void CmndCurrentSet(void) { - EnergyCommandSetCalResponse(ENERGY_CURRENT_CALIBRATION); + EnergyCommandSetCal(ENERGY_CURRENT_CALIBRATION); } void CmndFrequencySet(void) { EnergyCommandSetCalResponse(ENERGY_FREQUENCY_CALIBRATION); @@ -1526,6 +1568,10 @@ void EnergyShow(bool json) { if (!Energy->type_dc) { if (Energy->current_available && Energy->voltage_available) { for (uint32_t i = 0; i < Energy->phase_count; i++) { + if (0 == Energy->current[i]) { + Energy->active_power[i] = 0; + } + apparent_power[i] = Energy->apparent_power[i]; if (isnan(apparent_power[i])) { apparent_power[i] = Energy->voltage[i] * Energy->current[i]; @@ -1551,11 +1597,15 @@ void EnergyShow(bool json) { } if (apparent_power[i] < Energy->active_power[i]) { // Should be impossible if (apparent_power[i]) { - if ((power_factor[i] > 1.005) && (power_factor[i] < 2.0f)) { // Skip below 0.5% and don't expect 50% differences + if ((power_factor[i] >= 1.02f) && (power_factor[i] < 2.0f)) { // Skip below 2% and don't expect 50% differences AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("NRG: Calibrate as Active %3_fW > Apparent %3_fVA (PF = %4_f)"), &Energy->active_power[i], &apparent_power[i], &power_factor[i]); } } + apparent_power[i] = Energy->active_power[i]; // Force apparent equal to active as mis-calibrated + if (power_factor[i] > 1) { // Should not happen (Active > Apparent) + power_factor[i] = 1; + } } reactive_power[i] = Energy->reactive_power[i];