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)
This commit is contained in:
Theo Arends 2020-08-23 18:29:16 +02:00
parent 6e111477a0
commit 0f1e4fc917
7 changed files with 97 additions and 61 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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