diff --git a/src/_P076_HLW8012.ino b/src/_P076_HLW8012.ino index eacb3bd6d4..84daace7ec 100644 --- a/src/_P076_HLW8012.ino +++ b/src/_P076_HLW8012.ino @@ -19,6 +19,12 @@ // /** Changelog: + * 2024-12-01 dsiggi: - The old measurement values (voltage, current and power factor) are now again the default to support old setups. + * - Removed spaces from value names + * - Seperated Output selector string an value string + * - Uncrustify + * 2024-11-24 dsiggi: - Added the ability to read reactive power, apparent power and energy + * - Added ability to reset the energy counter with console command hlwresetenergy * 2023-01-03 tonhuisman: Uncrustify source, apply some code improvements * Older changelog not registered. */ @@ -31,10 +37,6 @@ HLW8012 *Plugin_076_hlw = nullptr; # define PLUGIN_ID_076 76 # define PLUGIN_076_DEBUG false // activate extra log info in the debug # define PLUGIN_NAME_076 "Energy (AC) - HLW8012/BL0937" -# define PLUGIN_VALUENAME1_076 "Voltage" -# define PLUGIN_VALUENAME2_076 "Current" -# define PLUGIN_VALUENAME3_076 "Power" -# define PLUGIN_VALUENAME4_076 "PowerFactor" # define HLW_DELAYREADING 500 @@ -44,13 +46,28 @@ HLW8012 *Plugin_076_hlw = nullptr; # define HLW_VOLTAGE_RESISTOR_DOWN (1000) // Real 1.009k // ----------------------------------------------------------------------------------------------- int StoredTaskIndex = -1; -uint8_t p076_read_stage{}; -unsigned long p076_timer{}; - -float p076_hcurrent{}; -float p076_hvoltage{}; -float p076_hpower{}; -float p076_hpowfact{}; +uint8_t p076_read_stage {}; +unsigned long p076_timer {}; + +float p076_hcurrent {}; +float p076_hvoltage {}; +float p076_hpowerActive {}; +float p076_hpowerReactive {}; +float p076_hpowerApparent {}; +float p076_hpowfact {}; +float p076_henergy {}; + +# define P076_NR_OUTPUT_VALUES 4 +# define P076_NR_OUTPUT_OPTIONS 7 +# define P076_QUERY1_CONFIG_POS 0 +# define P076_QUERY1 PCONFIG(0) +# define P076_QUERY2 PCONFIG(1) +# define P076_QUERY3 PCONFIG(2) +# define P076_QUERY4 PCONFIG(3) +# define P076_QUERY1_DFLT 0 // Voltage (V) +# define P076_QUERY2_DFLT 1 // Current (A) +# define P076_QUERY3_DFLT 2 // Active Power (W) +# define P076_QUERY4_DFLT 5 // Power Factor (cosphi) # define P076_Custom 0 @@ -68,6 +85,10 @@ float p076_hpowfact{}; # define P076_Gosund 9 # define P076_Shelly_PLUG_S 10 +// Forward declaration helper function +const __FlashStringHelper* p076_getQueryString(uint8_t value_nr, + bool displayString); + # if ESP_IDF_VERSION_MAJOR >= 5 // FIXME TD-er: Must check if older (and ESP8266) envs need IRAM_ATTR in the function declaration. @@ -119,6 +140,7 @@ boolean Plugin_076(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].TimerOption = true; Device[deviceCount].PluginStats = true; Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); + Device[deviceCount].OutputDataType = Output_Data_type_t::Simple; break; } @@ -128,14 +150,25 @@ boolean Plugin_076(uint8_t function, struct EventStruct *event, String& string) } case PLUGIN_GET_DEVICEVALUENAMES: { - strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], - PSTR(PLUGIN_VALUENAME1_076)); - strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], - PSTR(PLUGIN_VALUENAME2_076)); - strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], - PSTR(PLUGIN_VALUENAME3_076)); - strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[3], - PSTR(PLUGIN_VALUENAME4_076)); + for (uint8_t i = 0; i < VARS_PER_TASK; ++i) { + if (i < P076_NR_OUTPUT_VALUES) { + uint8_t choice = PCONFIG(i + P076_QUERY1_CONFIG_POS); + ExtraTaskSettings.setTaskDeviceValueName(i, p076_getQueryString(choice, false)); + } else { + ExtraTaskSettings.clearTaskDeviceValueName(i); + } + } + break; + } + + case PLUGIN_SET_DEFAULTS: { + // Load some defaults + P076_QUERY1 = P076_QUERY1_DFLT; + P076_QUERY2 = P076_QUERY2_DFLT; + P076_QUERY3 = P076_QUERY3_DFLT; + P076_QUERY4 = P076_QUERY4_DFLT; + + success = true; break; } @@ -146,6 +179,24 @@ boolean Plugin_076(uint8_t function, struct EventStruct *event, String& string) break; } + case PLUGIN_WEBFORM_LOAD_OUTPUT_SELECTOR: { + // To select the data in the 4 fields. + const __FlashStringHelper *options[P076_NR_OUTPUT_OPTIONS]; + + for (uint8_t i = 0; i < P076_NR_OUTPUT_OPTIONS; ++i) { + options[i] = p076_getQueryString(i, true); + } + + for (uint8_t i = 0; i < P076_NR_OUTPUT_VALUES; ++i) { + const uint8_t pconfigIndex = i + P076_QUERY1_CONFIG_POS; + sensorTypeHelper_loadOutputSelector(event, pconfigIndex, i, P076_NR_OUTPUT_OPTIONS, options); + } + + success = true; + + break; + } + case PLUGIN_WEBFORM_LOAD: { uint8_t devicePinSettings = PCONFIG(7); @@ -287,7 +338,7 @@ boolean Plugin_076(uint8_t function, struct EventStruct *event, String& string) hlwMultipliers[2] = getFormItemFloat(F("powmult")); if ((hlwMultipliers[0] > 1.0) && (hlwMultipliers[1] > 1.0) && (hlwMultipliers[2] > 1.0)) { - SaveCustomTaskSettings(event->TaskIndex, reinterpret_cast(&hlwMultipliers), + SaveCustomTaskSettings(event->TaskIndex, reinterpret_cast < const uint8_t * > (&hlwMultipliers), sizeof(hlwMultipliers)); # if PLUGIN_076_DEBUG addLog(LOG_LEVEL_INFO, F("P076: Saved Calibration from Config Page")); @@ -305,10 +356,17 @@ boolean Plugin_076(uint8_t function, struct EventStruct *event, String& string) } # if PLUGIN_076_DEBUG - addLogMove(LOG_LEVEL_INFO, strformat(F("P076: PIN Settings curr_read: %d cf_edge: %d cf1_edge: %d") + addLogMove(LOG_LEVEL_INFO, strformat(F("P076: PIN Settings curr_read: %d cf_edge: %d cf1_edge: %d"), PCONFIG(4), PCONFIG(5), PCONFIG(6))); # endif // if PLUGIN_076_DEBUG + // Save output selector parameters. + for (uint8_t i = 0; i < P076_NR_OUTPUT_VALUES; ++i) { + const uint8_t pconfigIndex = i + P076_QUERY1_CONFIG_POS; + const uint8_t choice = PCONFIG(pconfigIndex); + sensorTypeHelper_saveOutputSelector(event, pconfigIndex, i, p076_getQueryString(choice, false)); + } + success = true; break; } @@ -339,9 +397,12 @@ boolean Plugin_076(uint8_t function, struct EventStruct *event, String& string) case 3: // Read voltage + active power + power factor if (timeOutReached(p076_timer)) { - p076_hvoltage = Plugin_076_hlw->getVoltage(valid); - p076_hpower = Plugin_076_hlw->getActivePower(valid); - p076_hpowfact = static_cast(100 * Plugin_076_hlw->getPowerFactor(valid)); + p076_hvoltage = Plugin_076_hlw->getVoltage(valid); + p076_hpowerActive = Plugin_076_hlw->getActivePower(valid); + p076_hpowerReactive = Plugin_076_hlw->getReactivePower(valid); + p076_hpowerApparent = Plugin_076_hlw->getApparentPower(valid); + p076_hpowfact = static_cast < int > (100 * Plugin_076_hlw->getPowerFactor(valid)); + p076_henergy = Plugin_076_hlw->getEnergy(); ++p076_read_stage; // Measurement is done, schedule a new PLUGIN_READ call @@ -363,41 +424,71 @@ boolean Plugin_076(uint8_t function, struct EventStruct *event, String& string) // Force a measurement start. // ++p076_read_stage; // } else if (p076_read_stage > 3) { - bool valid = false; - p076_hpower = Plugin_076_hlw->getActivePower(valid); + bool valid = false; + float HLW[7]; + p076_hpowerActive = Plugin_076_hlw->getActivePower(valid); if (valid) { + HLW[2] = p076_hpowerActive; success = true; } p076_hvoltage = Plugin_076_hlw->getVoltage(valid); if (valid) { + HLW[0] = p076_hvoltage; success = true; } + p076_hcurrent = Plugin_076_hlw->getCurrent(valid); if (valid) { + HLW[1] = p076_hcurrent; success = true; } - p076_hpowfact = static_cast(100 * Plugin_076_hlw->getPowerFactor(valid)); + + p076_hpowfact = static_cast < int > (100 * Plugin_076_hlw->getPowerFactor(valid)); if (valid) { + HLW[5] = p076_hpowfact; success = true; } - UserVar.setFloat(event->TaskIndex, 0, p076_hvoltage); - UserVar.setFloat(event->TaskIndex, 1, p076_hcurrent); - UserVar.setFloat(event->TaskIndex, 2, p076_hpower); - UserVar.setFloat(event->TaskIndex, 3, p076_hpowfact); + p076_hpowerReactive = Plugin_076_hlw->getReactivePower(valid); + + if (valid) { + HLW[3] = p076_hpowerReactive; + success = true; + } + + p076_hpowerApparent = Plugin_076_hlw->getApparentPower(valid); + + if (valid) { + HLW[4] = p076_hpowerApparent; + success = true; + } + + p076_henergy = Plugin_076_hlw->getEnergy(); + HLW[6] = p076_henergy; + + UserVar.setFloat(event->TaskIndex, 0, HLW[P076_QUERY1]); + UserVar.setFloat(event->TaskIndex, 1, HLW[P076_QUERY2]); + UserVar.setFloat(event->TaskIndex, 2, HLW[P076_QUERY3]); + UserVar.setFloat(event->TaskIndex, 3, HLW[P076_QUERY4]); // Measurement is complete. p076_read_stage = 0; # if PLUGIN_076_DEBUG addLogMove(LOG_LEVEL_INFO, - strformat(F("P076: Read values - V=%.2f - A=%.2f - W=%.2f - Pf%%=%.2f"), - p076_hvoltage, p076_hcurrent, p076_hpower, p076_hpowfact)); + strformat(F("P076: Read values - V=%.2f - A=%.2f - W=%.2f - VAR=%.2f - VA=%.2f - Pf%%=%.2f - Ws=%.2f"), + p076_hvoltage, + p076_hcurrent, + p076_hpowerActive, + p076_hpowerReactive, + p076_hpowerApparent, + p076_hpowfact, + p076_henergy)); # endif // if PLUGIN_076_DEBUG // Plugin_076_hlw->toggleMode(); @@ -420,7 +511,7 @@ boolean Plugin_076(uint8_t function, struct EventStruct *event, String& string) const uint8_t SEL_PIN = CONFIG_PIN1; if (validGpio(CF_PIN) && validGpio(CF1_PIN) && validGpio(SEL_PIN)) { - Plugin_076_hlw = new (std::nothrow) HLW8012; + Plugin_076_hlw = new(std::nothrow) HLW8012; if (Plugin_076_hlw) { const uint8_t currentRead = PCONFIG(4); @@ -467,6 +558,14 @@ boolean Plugin_076(uint8_t function, struct EventStruct *event, String& string) // Need a few seconds to read the first sample, so trigger a new read a few seconds after init. Scheduler.schedule_task_device_timer(event->TaskIndex, millis() + 5000); + + // Set default querys if not set + if ((P076_QUERY1 == 0) && (P076_QUERY2 == 0) && (P076_QUERY3 == 0) && (P076_QUERY4 == 0)) { + P076_QUERY1 = P076_QUERY1_DFLT; + P076_QUERY2 = P076_QUERY2_DFLT; + P076_QUERY3 = P076_QUERY3_DFLT; + P076_QUERY4 = P076_QUERY4_DFLT; + } success = true; } } @@ -522,12 +621,26 @@ boolean Plugin_076(uint8_t function, struct EventStruct *event, String& string) } success = true; } + + if (equals(command, F("hlwresetenergy"))) { + Plugin076_ResetEnergy(); + success = true; + } } break; } return success; } +void Plugin076_ResetEnergy() { + if (Plugin_076_hlw) { + Plugin_076_hlw->resetEnergy(); + # if PLUGIN_076_DEBUG + addLog(LOG_LEVEL_INFO, F("P076: Reset Energy counter to zero")); + # endif // if PLUGIN_076_DEBUG + } +} + void Plugin076_ResetMultipliers() { if (Plugin_076_hlw) { Plugin_076_hlw->resetMultipliers(); @@ -542,19 +655,19 @@ void Plugin076_SaveMultipliers() { if (StoredTaskIndex < 0) { return; // Not yet initialized. } - ESPEASY_RULES_FLOAT_TYPE hlwMultipliers[3]{}; + ESPEASY_RULES_FLOAT_TYPE hlwMultipliers[3] {}; if (Plugin076_ReadMultipliers(hlwMultipliers[0], hlwMultipliers[1], hlwMultipliers[2])) { # if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE - SaveCustomTaskSettings(StoredTaskIndex, reinterpret_cast(&hlwMultipliers), + SaveCustomTaskSettings(StoredTaskIndex, reinterpret_cast < const uint8_t * > (&hlwMultipliers), sizeof(hlwMultipliers)); # else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE - double hlwMultipliers_d[3]{}; + double hlwMultipliers_d[3] {}; hlwMultipliers_d[0] = hlwMultipliers[0]; hlwMultipliers_d[1] = hlwMultipliers[1]; hlwMultipliers_d[2] = hlwMultipliers[2]; - SaveCustomTaskSettings(StoredTaskIndex, reinterpret_cast(&hlwMultipliers_d), + SaveCustomTaskSettings(StoredTaskIndex, reinterpret_cast < const uint8_t * > (&hlwMultipliers_d), sizeof(hlwMultipliers_d)); # endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE } @@ -585,7 +698,7 @@ bool Plugin076_LoadMultipliers(taskIndex_t TaskIndex, } double hlwMultipliers[3]; - LoadCustomTaskSettings(TaskIndex, reinterpret_cast(&hlwMultipliers), + LoadCustomTaskSettings(TaskIndex, reinterpret_cast < uint8_t * > (&hlwMultipliers), sizeof(hlwMultipliers)); if (hlwMultipliers[0] > 1.0) { @@ -615,10 +728,13 @@ void Plugin076_Reset(taskIndex_t TaskIndex) { p076_read_stage = 0; p076_timer = 0; - p076_hcurrent = 0.0f; - p076_hvoltage = 0.0f; - p076_hpower = 0.0f; - p076_hpowfact = 0.0f; + p076_hcurrent = 0.0f; + p076_hvoltage = 0.0f; + p076_hpowerActive = 0.0f; + p076_hpowerReactive = 0.0f; + p076_hpowerApparent = 0.0f; + p076_hpowfact = 0.0f; + p076_henergy = 0.0f; } // When using interrupts we have to call the library entry point @@ -635,4 +751,17 @@ void IRAM_ATTR p076_hlw8012_cf_interrupt() { } } +const __FlashStringHelper* p076_getQueryString(uint8_t value_nr, bool displayString) { + switch (value_nr) { + case 0: return displayString ? F("Voltage") : F("Voltage"); + case 1: return displayString ? F("Current") : F("Current"); + case 2: return displayString ? F("Active Power") : F("Power"); + case 3: return displayString ? F("Reactive Power") : F("VAR"); + case 4: return displayString ? F("Apparent Power") : F("VA"); + case 5: return displayString ? F("Power Factor") : F("PowerFactor"); + case 6: return displayString ? F("Energy") : F("Ws"); + } + return F(""); +} + #endif // USES_P076