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