From 0f1e4fc9174b86505a651ade178c97814833079c Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 23 Aug 2020 18:29:16 +0200 Subject: [PATCH] Add command ``PowerDelta1`` to ``PowerDelta3`` - Bump version to 8.4.0.3 - Add command ``PowerDelta1`` to ``PowerDelta3`` to trigger on up to three phases (#9134) --- RELEASENOTES.md | 4 +- tasmota/CHANGELOG.md | 4 ++ tasmota/settings.h | 12 ++-- tasmota/settings.ino | 11 +++- tasmota/support_command.ino | 4 +- tasmota/tasmota_version.h | 2 +- tasmota/xdrv_03_energy.ino | 121 +++++++++++++++++++++--------------- 7 files changed, 97 insertions(+), 61 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 70c10750d..7bb3ee324 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -53,7 +53,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c ## Changelog -### Version 8.4.0.2 +### Version 8.4.0.3 - Remove support for 1-step upgrade from versions before 6.6.0.11 to versions after 8.4.0.1 - Change White blend mode moved to using ``SetOption 105`` instead of ``RGBWWTable`` @@ -63,6 +63,8 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Add command ``Restart 2`` to halt system. Needs hardware reset or power cycle to restart (#9046) - Add ESP32 Analog input support for GPIO32 to GPIO39 - Add Zigbee options to ``ZbSend`` ``Config`` and ``ReadCondig`` +- Add Zigbee web gui widget for Temp/Humidity/Pressure sensors - Add better config corruption recovery (#9046) - Add virtual CT for 4 channels lights, emulating a 5th channel - Add support for DYP ME007 ultrasonic distance sensor by Janusz Kostorz (#9113) +- Add command ``PowerDelta1`` to ``PowerDelta3`` to trigger on up to three phases (#9134) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index a94fa3d3c..244e6e7b6 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -1,5 +1,9 @@ ## Unreleased (development) +### 8.4.0.3 20200823 + +- Add command ``PowerDelta1`` to ``PowerDelta3`` to trigger on up to three phases (#9134) + ### 8.4.0.2 20200813 - Remove support for 1-step upgrade from versions before 6.6.0.11 to versions after 8.4.0.1 diff --git a/tasmota/settings.h b/tasmota/settings.h index 0c40a0613..590d0af3e 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -127,7 +127,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t white_blend_mode : 1; // bit 23 (v8.4.0.1) - SetOption105 - White Blend Mode - used to be `RGBWWTable` last value `0`, now deprecated in favor of this option uint32_t virtual_ct : 1; // bit 24 (v8.4.0.1) - SetOption106 - Virtual CT - Creates a virtual White ColorTemp for RGBW lights uint32_t virtual_ct_cw : 1; // bit 25 (v8.4.0.1) - SetOption107 - Virtual CT Channel - signals whether the hardware white is cold CW (true) or warm WW (false) - uint32_t teleinfo_rawdata : 1; // bit 21 (v8.4.0.2) - SetOption108 - enable Teleinfo + Tasmota Energy device (0) or Teleinfo raw data only (1) + uint32_t teleinfo_rawdata : 1; // bit 21 (v8.4.0.2) - SetOption108 - enable Teleinfo + Tasmota Energy device (0) or Teleinfo raw data only (1) uint32_t spare27 : 1; uint32_t spare28 : 1; // bit 28 uint32_t spare29 : 1; // bit 29 @@ -557,7 +557,7 @@ struct { uint16_t dimmer_hw_min; // E90 uint16_t dimmer_hw_max; // E92 uint32_t deepsleep; // E94 - uint16_t energy_power_delta; // E98 + uint16_t ex2_energy_power_delta; // E98 - Free since 8.4.0.3 uint8_t shutter_motordelay[MAX_SHUTTERS]; // E9A int8_t temp_comp; // E9E uint8_t weight_change; // E9F @@ -610,10 +610,14 @@ struct { uint8_t tcp_baudrate; // F41 uint8_t fallback_module; // F42 - uint8_t free_f43[113]; // F43 - Decrement if adding new Setting variables just above and below + uint8_t free_f43[1]; // F43 + + uint16_t energy_power_delta[3]; // F44 + + uint8_t free_f4e[106]; // F4A - Decrement if adding new Setting variables just above and below // Only 32 bit boundary variables below - SysBitfield5 flag5; // EB4 + SysBitfield5 flag5; // FB4 uint16_t pulse_counter_debounce_low; // FB8 uint16_t pulse_counter_debounce_high; // FBA uint32_t keeloq_master_msb; // FBC diff --git a/tasmota/settings.ino b/tasmota/settings.ino index c6d8c52f4..32cc85335 100644 --- a/tasmota/settings.ino +++ b/tasmota/settings.ino @@ -935,7 +935,9 @@ void SettingsDefaultSet2(void) flag3.dds2382_model |= ENERGY_DDS2382_MODE; flag3.hardware_energy_total |= ENERGY_HARDWARE_TOTALS; Settings.param[P_MAX_POWER_RETRY] = MAX_POWER_RETRY; -// Settings.energy_power_delta = 0; +// Settings.energy_power_delta[0] = 0; +// Settings.energy_power_delta[1] = 0; +// Settings.energy_power_delta[2] = 0; Settings.energy_power_calibration = HLW_PREF_PULSE; Settings.energy_voltage_calibration = HLW_UREF_PULSE; Settings.energy_current_calibration = HLW_IREF_PULSE; @@ -1346,7 +1348,7 @@ void SettingsDelta(void) Settings.ex_sbaudrate = 0; */ Settings.flag3.fast_power_cycle_disable = 0; - Settings.energy_power_delta = Settings.ex_energy_power_delta; + Settings.ex2_energy_power_delta = Settings.ex_energy_power_delta; Settings.ex_energy_power_delta = 0; } if (Settings.version < 0x06060015) { @@ -1503,6 +1505,11 @@ void SettingsDelta(void) if (Settings.version < 0x08030106) { Settings.fallback_module = FALLBACK_MODULE; } + if (Settings.version < 0x08040003) { + Settings.energy_power_delta[0] = Settings.ex2_energy_power_delta; + Settings.energy_power_delta[1] = 0; + Settings.energy_power_delta[2] = 0; + } Settings.version = VERSION; SettingsSave(1); diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index 703ee0a67..379d15e0c 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -538,9 +538,9 @@ void CmndStatus(void) #if defined(USE_ENERGY_SENSOR) && defined(USE_ENERGY_MARGIN_DETECTION) if (energy_flg) { if ((0 == payload) || (9 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS9_MARGIN "\":{\"" D_CMND_POWERDELTA "\":%d,\"" D_CMND_POWERLOW "\":%d,\"" D_CMND_POWERHIGH "\":%d,\"" + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS9_MARGIN "\":{\"" D_CMND_POWERDELTA "\":[%d,%d,%d],\"" D_CMND_POWERLOW "\":%d,\"" D_CMND_POWERHIGH "\":%d,\"" D_CMND_VOLTAGELOW "\":%d,\"" D_CMND_VOLTAGEHIGH "\":%d,\"" D_CMND_CURRENTLOW "\":%d,\"" D_CMND_CURRENTHIGH "\":%d}}"), - Settings.energy_power_delta, Settings.energy_min_power, Settings.energy_max_power, + Settings.energy_power_delta[0], Settings.energy_power_delta[1], Settings.energy_power_delta[2], Settings.energy_min_power, Settings.energy_max_power, Settings.energy_min_voltage, Settings.energy_max_voltage, Settings.energy_min_current, Settings.energy_max_current); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "9")); } diff --git a/tasmota/tasmota_version.h b/tasmota/tasmota_version.h index 4d0d9e0f2..ce7e073c2 100644 --- a/tasmota/tasmota_version.h +++ b/tasmota/tasmota_version.h @@ -20,7 +20,7 @@ #ifndef _TASMOTA_VERSION_H_ #define _TASMOTA_VERSION_H_ -const uint32_t VERSION = 0x08040002; +const uint32_t VERSION = 0x08040003; // Lowest compatible version const uint32_t VERSION_COMPATIBLE = 0x07010006; diff --git a/tasmota/xdrv_03_energy.ino b/tasmota/xdrv_03_energy.ino index 4d53af77c..8c33c6ee3 100644 --- a/tasmota/xdrv_03_energy.ino +++ b/tasmota/xdrv_03_energy.ino @@ -108,7 +108,7 @@ struct ENERGY { bool power_on = true; #ifdef USE_ENERGY_MARGIN_DETECTION - uint16_t power_history[3] = { 0 }; + uint16_t power_history[3][3] = {{ 0 }, { 0 }, { 0 }}; uint8_t power_steady_counter = 8; // Allow for power on stabilization bool min_power_flag = false; bool max_power_flag = false; @@ -130,6 +130,31 @@ Ticker ticker_energy; /********************************************************************************************/ +char* EnergyFormatIndex(char* result, char* input, bool json, uint32_t index, bool single = false) +{ + char layout[16]; + GetTextIndexed(layout, sizeof(layout), (index -1) + (3 * json), kEnergyPhases); + switch (index) { + case 2: + snprintf_P(result, FLOATSZ *3, layout, input, input + FLOATSZ); // Dirty + break; + case 3: + snprintf_P(result, FLOATSZ *3, layout, input, input + FLOATSZ, input + FLOATSZ + FLOATSZ); // Even dirtier + break; + default: + snprintf_P(result, FLOATSZ *3, input); + } + return result; +} + +char* EnergyFormat(char* result, char* input, bool json, bool single = false) +{ + uint8_t index = (single) ? 1 : Energy.phase_count; // 1,2,3 + return EnergyFormatIndex(result, input, json, index, single); +} + +/********************************************************************************************/ + bool EnergyTariff1Active() // Off-Peak hours { uint8_t dst = 0; @@ -303,38 +328,53 @@ void EnergyMarginCheck(void) return; } - uint16_t energy_power_u = (uint16_t)(Energy.active_power[0]); - bool jsonflg = false; Response_P(PSTR("{\"" D_RSLT_MARGINS "\":{")); - if (Settings.energy_power_delta) { - int16_t power_diff = energy_power_u - Energy.power_history[0]; - uint16_t delta = abs(power_diff); - if (delta > 0) { - if (Settings.energy_power_delta < 101) { // 1..100 = Percentage - uint16_t min_power = (Energy.power_history[0] > energy_power_u) ? energy_power_u : Energy.power_history[0]; - if (0 == min_power) { min_power++; } // Fix divide by 0 exception (#6741) - delta = (delta * 100) / min_power; - if (delta > Settings.energy_power_delta) { - jsonflg = true; - } - } else { // 101..32000 = Absolute - if (delta > (Settings.energy_power_delta -100)) { - jsonflg = true; + int16_t power_diff[3] = { 0 }; + for (uint32_t phase = 0; phase < Energy.phase_count; phase++) { + uint16_t active_power = (uint16_t)(Energy.active_power[phase]); + + if (Settings.energy_power_delta[phase]) { + power_diff[phase] = active_power - Energy.power_history[phase][0]; + uint16_t delta = abs(power_diff[phase]); + bool threshold_met = false; + if (delta > 0) { + if (Settings.energy_power_delta[phase] < 101) { // 1..100 = Percentage + uint16_t min_power = (Energy.power_history[phase][0] > active_power) ? active_power : Energy.power_history[phase][0]; + if (0 == min_power) { min_power++; } // Fix divide by 0 exception (#6741) + delta = (delta * 100) / min_power; + if (delta > Settings.energy_power_delta[phase]) { + threshold_met = true; + } + } else { // 101..32000 = Absolute + if (delta > (Settings.energy_power_delta[phase] -100)) { + threshold_met = true; + } } } - if (jsonflg) { - Energy.power_history[1] = Energy.active_power[0]; // We only want one report so reset history - Energy.power_history[2] = Energy.active_power[0]; - - ResponseAppend_P(PSTR("\"" D_CMND_POWERDELTA "\":%d"), power_diff); + if (threshold_met) { + Energy.power_history[phase][1] = active_power; // We only want one report so reset history + Energy.power_history[phase][2] = active_power; + jsonflg = true; + } else { + power_diff[phase] = 0; } } + Energy.power_history[phase][0] = Energy.power_history[phase][1]; // Shift in history every second allowing power changes to settle for up to three seconds + Energy.power_history[phase][1] = Energy.power_history[phase][2]; + Energy.power_history[phase][2] = active_power; } - Energy.power_history[0] = Energy.power_history[1]; // Shift in history every second allowing power changes to settle for up to three seconds - Energy.power_history[1] = Energy.power_history[2]; - Energy.power_history[2] = energy_power_u; + 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 *3]; + ResponseAppend_P(PSTR("\"" D_CMND_POWERDELTA "\":%s"), EnergyFormat(value_chr, power_diff_chr[0], 1)); + } + + uint16_t energy_power_u = (uint16_t)(Energy.active_power[0]); if (Energy.power_on && (Settings.energy_min_power || Settings.energy_max_power || Settings.energy_min_voltage || Settings.energy_max_voltage || Settings.energy_min_current || Settings.energy_max_current)) { uint16_t energy_voltage_u = (uint16_t)(Energy.voltage[0]); @@ -725,10 +765,12 @@ void CmndModuleAddress(void) #ifdef USE_ENERGY_MARGIN_DETECTION void CmndPowerDelta(void) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32000)) { - Settings.energy_power_delta = XdrvMailbox.payload; + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 3)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32000)) { + Settings.energy_power_delta[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.energy_power_delta[XdrvMailbox.index -1]); } - ResponseCmndNumber(Settings.energy_power_delta); } void CmndPowerLow(void) @@ -881,29 +923,6 @@ const char HTTP_ENERGY_SNS3[] PROGMEM = "{s}" D_EXPORT_ACTIVE "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; #endif // USE_WEBSERVER -char* EnergyFormatIndex(char* result, char* input, bool json, uint32_t index, bool single = false) -{ - char layout[16]; - GetTextIndexed(layout, sizeof(layout), (index -1) + (3 * json), kEnergyPhases); - switch (index) { - case 2: - snprintf_P(result, FLOATSZ *3, layout, input, input + FLOATSZ); // Dirty - break; - case 3: - snprintf_P(result, FLOATSZ *3, layout, input, input + FLOATSZ, input + FLOATSZ + FLOATSZ); // Even dirtier - break; - default: - snprintf_P(result, FLOATSZ *3, input); - } - return result; -} - -char* EnergyFormat(char* result, char* input, bool json, bool single = false) -{ - uint8_t index = (single) ? 1 : Energy.phase_count; // 1,2,3 - return EnergyFormatIndex(result, input, json, index, single); -} - void EnergyShow(bool json) { for (uint32_t i = 0; i < Energy.phase_count; i++) {