diff --git a/lib/HLW8012_1.1.1/src/HLW8012.cpp b/lib/HLW8012_1.1.1/src/HLW8012.cpp index 086e6dd38c..53397e0957 100644 --- a/lib/HLW8012_1.1.1/src/HLW8012.cpp +++ b/lib/HLW8012_1.1.1/src/HLW8012.cpp @@ -22,6 +22,8 @@ along with this program. If not, see . #include #include "HLW8012.h" +#include + #ifndef CORE_POST_3_0_0 #ifdef ESP8266 #define IRAM_ATTR ICACHE_RAM_ATTR @@ -74,7 +76,7 @@ hlw8012_mode_t HLW8012::toggleMode() { return new_mode; } -double HLW8012::getCurrent() { +float HLW8012::getCurrent() { // Power measurements are more sensitive to switch offs, // so we first check if power is 0 to set _current to 0 too @@ -89,40 +91,40 @@ double HLW8012::getCurrent() { } const unsigned int current_pulse_width = _current_pulse_width; - _current = (current_pulse_width > 0) ? _current_multiplier / current_pulse_width / 2 : 0; + _current = (current_pulse_width > 0) ? _current_multiplier / static_cast(current_pulse_width) / 2.0f : 0.0f; return _current; } -unsigned int HLW8012::getVoltage() { +float HLW8012::getVoltage() { if (_use_interrupts) { _checkCF1Signal(); } else if (_mode != _current_mode) { _voltage_pulse_width = pulseIn(_cf1_pin, HIGH, _pulse_timeout); } const unsigned int voltage_pulse_width = _voltage_pulse_width; - _voltage = (voltage_pulse_width > 0) ? _voltage_multiplier / voltage_pulse_width / 2 : 0; + _voltage = (voltage_pulse_width > 0) ? _voltage_multiplier / static_cast(voltage_pulse_width) / 2.0f : 0.0f; return _voltage; } -unsigned int HLW8012::getActivePower() { +float HLW8012::getActivePower() { if (_use_interrupts) { _checkCFSignal(); } else { _power_pulse_width = pulseIn(_cf_pin, HIGH, _pulse_timeout); } const unsigned int power_pulse_width = _power_pulse_width; - _power = (power_pulse_width > 0) ? _power_multiplier / power_pulse_width / 2 : 0; + _power = (power_pulse_width > 0) ? _power_multiplier / static_cast(power_pulse_width) / 2.0f : 0.0f; return _power; } -unsigned int HLW8012::getApparentPower() { - double current = getCurrent(); +float HLW8012::getApparentPower() { + float current = getCurrent(); unsigned int voltage = getVoltage(); return voltage * current; } -unsigned int HLW8012::getReactivePower() { +float HLW8012::getReactivePower() { unsigned int active = getActivePower(); unsigned int apparent = getApparentPower(); if (apparent > active) { @@ -132,15 +134,15 @@ unsigned int HLW8012::getReactivePower() { } } -double HLW8012::getPowerFactor() { - unsigned int active = getActivePower(); - unsigned int apparent = getApparentPower(); - if (active > apparent) return 1; - if (apparent == 0) return 0; - return (double) active / apparent; +float HLW8012::getPowerFactor() { + const float active = getActivePower(); + const float apparent = getApparentPower(); + if (active > apparent) return 1.0f; + if (apparent == 0) return 0.0f; + return active / apparent; } -unsigned long HLW8012::getEnergy() { +float HLW8012::getEnergy() { // Counting pulses only works in IRQ mode if (!_use_interrupts) return 0; @@ -151,34 +153,35 @@ unsigned long HLW8012::getEnergy() { f = N/t (N=pulse count, t = time) E = P*t = m*N (E=energy) */ - return _pulse_count * _power_multiplier / 1000000. / 2; + const float pulse_count = _cf_pulse_count; + return pulse_count * _power_multiplier / 1000000.0f / 2.0f; } void HLW8012::resetEnergy() { - _pulse_count = 0; + _cf_pulse_count = 0; } -void HLW8012::expectedCurrent(double value) { - if (_current == 0) getCurrent(); - if (_current > 0) _current_multiplier *= (value / _current); +void HLW8012::expectedCurrent(float value) { + if (static_cast(_current) == 0) getCurrent(); + if (static_cast(_current) > 0) _current_multiplier *= (value / _current); } -void HLW8012::expectedVoltage(unsigned int value) { - if (_voltage == 0) getVoltage(); - if (_voltage > 0) _voltage_multiplier *= ((double) value / _voltage); +void HLW8012::expectedVoltage(float value) { + if (static_cast(_voltage) == 0) getVoltage(); + if (static_cast(_voltage) > 0) _voltage_multiplier *= (value / _voltage); } -void HLW8012::expectedActivePower(unsigned int value) { - if (_power == 0) getActivePower(); - if (_power > 0) _power_multiplier *= ((double) value / _power); +void HLW8012::expectedActivePower(float value) { + if (static_cast(_power) == 0) getActivePower(); + if (static_cast(_power) > 0) _power_multiplier *= (value / _power); } void HLW8012::resetMultipliers() { _calculateDefaultMultipliers(); } -void HLW8012::setResistors(double current, double voltage_upstream, double voltage_downstream) { +void HLW8012::setResistors(float current, float voltage_upstream, float voltage_downstream) { if (voltage_downstream > 0) { _current_resistor = current; _voltage_resistor = (voltage_upstream + voltage_downstream) / voltage_downstream; @@ -190,33 +193,44 @@ void IRAM_ATTR HLW8012::cf_interrupt() { const unsigned long now = micros(); _power_pulse_width = now - _last_cf_interrupt; _last_cf_interrupt = now; - _pulse_count++; + _cf_pulse_count++; } void IRAM_ATTR HLW8012::cf1_interrupt() { const unsigned long now = micros(); - - if ((now - _first_cf1_interrupt) > _pulse_timeout) { - - unsigned long pulse_width; + const unsigned long time_since_first = now - _first_cf1_interrupt; + + // The first few pulses after switching will be unstable + // Collect pulses in this mode for some time + // On very few pulses, use the last one collected in this period. + // On many pulses, compute the average over a longer period to get a more stable reading. + // This may also increase resolution on higher frequencies. + if (time_since_first > _pulse_timeout) { + // Copy value first as it is volatile + const unsigned long last_cf1_interrupt = _last_cf1_interrupt; + const unsigned long pulse_width = + (last_cf1_interrupt == _first_cf1_interrupt) + ? 0 + : (_cf1_pulse_count < 10) + ? (now - last_cf1_interrupt) // long pulses, use the last one as it is probably the most stable one + : (time_since_first / _cf1_pulse_count); - if (_last_cf1_interrupt == _first_cf1_interrupt) { - pulse_width = 0; - } else { - pulse_width = now - _last_cf1_interrupt; - } - if (_mode == _current_mode) { _current_pulse_width = pulse_width; } else { _voltage_pulse_width = pulse_width; } - - _mode = 1 - _mode; - digitalWrite(_sel_pin, _mode); + + // Copy value first as it is volatile + const unsigned char mode = 1 - _mode; + DIRECT_pinWrite_ISR(_sel_pin, mode); + _mode = mode; + // Keep track of when the SEL pin was switched. _first_cf1_interrupt = now; - + _cf1_pulse_count = 0; + } else { + ++_cf1_pulse_count; } _last_cf1_interrupt = now; @@ -228,13 +242,20 @@ void HLW8012::_checkCFSignal() { } void HLW8012::_checkCF1Signal() { - if ((micros() - _last_cf1_interrupt) > _pulse_timeout) { + const unsigned long now = micros(); + if ((now - _last_cf1_interrupt) > _pulse_timeout) { if (_mode == _current_mode) { _current_pulse_width = 0; } else { _voltage_pulse_width = 0; } - toggleMode(); + // Copy value first as it is volatile + const unsigned char mode = 1 - _mode; + DIRECT_pinWrite(_sel_pin, mode); + _mode = mode; + if (_use_interrupts) { + _last_cf1_interrupt = _first_cf1_interrupt = now; + } } } diff --git a/lib/HLW8012_1.1.1/src/HLW8012.h b/lib/HLW8012_1.1.1/src/HLW8012.h index 99ea860537..8454d7f753 100644 --- a/lib/HLW8012_1.1.1/src/HLW8012.h +++ b/lib/HLW8012_1.1.1/src/HLW8012.h @@ -79,31 +79,32 @@ class HLW8012 { unsigned long pulse_timeout = PULSE_TIMEOUT); void setMode(hlw8012_mode_t mode); + hlw8012_mode_t getMode(); hlw8012_mode_t toggleMode(); - double getCurrent(); - unsigned int getVoltage(); - unsigned int getActivePower(); - unsigned int getApparentPower(); - double getPowerFactor(); - unsigned int getReactivePower(); - unsigned long getEnergy(); //in Ws + float getCurrent(); + float getVoltage(); + float getActivePower(); + float getApparentPower(); + float getPowerFactor(); + float getReactivePower(); + float getEnergy(); //in Ws void resetEnergy(); - void setResistors(double current, double voltage_upstream, double voltage_downstream); + void setResistors(float current, float voltage_upstream, float voltage_downstream); - void expectedCurrent(double current); - void expectedVoltage(unsigned int current); - void expectedActivePower(unsigned int power); + void expectedCurrent(float current); + void expectedVoltage(float current); + void expectedActivePower(float power); - double getCurrentMultiplier() { return _current_multiplier; }; - double getVoltageMultiplier() { return _voltage_multiplier; }; - double getPowerMultiplier() { return _power_multiplier; }; + float getCurrentMultiplier() { return _current_multiplier; }; + float getVoltageMultiplier() { return _voltage_multiplier; }; + float getPowerMultiplier() { return _power_multiplier; }; - void setCurrentMultiplier(double current_multiplier) { _current_multiplier = current_multiplier; }; - void setVoltageMultiplier(double voltage_multiplier) { _voltage_multiplier = voltage_multiplier; }; - void setPowerMultiplier(double power_multiplier) { _power_multiplier = power_multiplier; }; + void setCurrentMultiplier(float current_multiplier) { _current_multiplier = current_multiplier; }; + void setVoltageMultiplier(float voltage_multiplier) { _voltage_multiplier = voltage_multiplier; }; + void setPowerMultiplier(float power_multiplier) { _power_multiplier = power_multiplier; }; void resetMultipliers(); private: @@ -112,22 +113,21 @@ class HLW8012 { unsigned char _cf1_pin = 0; unsigned char _sel_pin = 0; - double _current_resistor = R_CURRENT; - double _voltage_resistor = R_VOLTAGE; + float _current_resistor = R_CURRENT; + float _voltage_resistor = R_VOLTAGE; - double _current_multiplier = 0.0; // Unit: us/A - double _voltage_multiplier = 0.0; // Unit: us/V - double _power_multiplier = 0.0; // Unit: us/W + float _current_multiplier = 0.0; // Unit: us/A + float _voltage_multiplier = 0.0; // Unit: us/V + float _power_multiplier = 0.0; // Unit: us/W unsigned long _pulse_timeout = PULSE_TIMEOUT; //Unit: us volatile unsigned long _voltage_pulse_width = 0; //Unit: us volatile unsigned long _current_pulse_width = 0; //Unit: us volatile unsigned long _power_pulse_width = 0; //Unit: us - volatile unsigned long _pulse_count = 0; - double _current = 0; - unsigned int _voltage = 0; - unsigned int _power = 0; + float _current = 0; + float _voltage = 0; + float _power = 0; unsigned char _current_mode = HIGH; volatile unsigned char _mode = 0; @@ -136,6 +136,8 @@ class HLW8012 { volatile unsigned long _last_cf_interrupt = 0; volatile unsigned long _last_cf1_interrupt = 0; volatile unsigned long _first_cf1_interrupt = 0; + volatile unsigned long _cf_pulse_count = 0; + volatile unsigned long _cf1_pulse_count = 0; void _checkCFSignal(); void _checkCF1Signal(); diff --git a/src/_P076_HLW8012.ino b/src/_P076_HLW8012.ino index cd7c5aa349..106dc45f2a 100644 --- a/src/_P076_HLW8012.ino +++ b/src/_P076_HLW8012.ino @@ -41,10 +41,10 @@ int StoredTaskIndex = -1; uint8_t p076_read_stage = 0; unsigned long p076_timer = 0; -double p076_hcurrent = 0.0f; -unsigned int p076_hvoltage = 0; -unsigned int p076_hpower = 0; -unsigned int p076_hpowfact = 0; +float p076_hcurrent = 0.0f; +float p076_hvoltage = 0; +float p076_hpower = 0; +float p076_hpowfact = 0; #define P076_Custom 0 @@ -450,12 +450,12 @@ boolean Plugin_076(uint8_t function, struct EventStruct *event, String &string) } if (command.equalsIgnoreCase(F("hlwcalibrate"))) { - unsigned int CalibVolt = 0; - double CalibCurr = 0; - unsigned int CalibAcPwr = 0; - if (validUIntFromString(parseString(string, 2), CalibVolt)) { - if (validDoubleFromString(parseString(string, 3), CalibCurr)) { - validUIntFromString(parseString(string, 4), CalibAcPwr); + float CalibVolt = 0; + float CalibCurr = 0; + float CalibAcPwr = 0; + if (validFloatFromString(parseString(string, 2), CalibVolt)) { + if (validFloatFromString(parseString(string, 3), CalibCurr)) { + validFloatFromString(parseString(string, 4), CalibAcPwr); } } #ifndef BUILD_NO_DEBUG