Skip to content

Commit

Permalink
[hlw8012]: new counter impl
Browse files Browse the repository at this point in the history
* does not use the pulse counter anymore
* needs less flash and ram than the old implementation
* isn't influenced by update interval jitter
* increases resolution with small loads (< ~50W) and fast update intervals (< 10s)
  • Loading branch information
mknjc committed May 13, 2024
1 parent 47a1710 commit aaa23d7
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 27 deletions.
60 changes: 46 additions & 14 deletions esphome/components/hlw8012/hlw8012.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,38 @@ namespace esphome {
namespace hlw8012 {

static const char *const TAG = "hlw8012";
static const float MICROSECONDS_IN_SECONDS = 1000000.0f;

// valid for HLW8012 and CSE7759
static const uint32_t HLW8012_CLOCK_FREQUENCY = 3579000;

void IRAM_ATTR HLW8012Component::cf_intr(HLW8012Component *arg) {
const uint32_t now = micros();

if (arg->cf_pulses_ == 0) {
arg->cf_first_pulse_micros_ = now;
}
arg->cf_pulses_++;
arg->cf_last_pulse_micros_ = now;
}
void IRAM_ATTR HLW8012Component::cf1_intr(HLW8012Component *arg) {
const uint32_t now = micros();

if (arg->cf1_pulses_ == 0) {
arg->cf1_first_pulse_micros_ = now;
}
arg->cf1_pulses_++;
arg->cf1_last_pulse_micros_ = now;
}

void HLW8012Component::setup() {
float reference_voltage = 0;
ESP_LOGCONFIG(TAG, "Setting up HLW8012...");
this->sel_pin_->setup();
this->sel_pin_->digital_write(this->current_mode_);
this->cf_store_.pulse_counter_setup(this->cf_pin_);
this->cf1_store_.pulse_counter_setup(this->cf1_pin_);

this->cf_pin_->attach_interrupt(HLW8012Component::cf_intr, this, gpio::INTERRUPT_RISING_EDGE);
this->cf1_pin_->attach_interrupt(HLW8012Component::cf1_intr, this, gpio::INTERRUPT_RISING_EDGE);

// Initialize multipliers
if (this->sensor_model_ == HLW8012_SENSOR_MODEL_BL0937) {
Expand Down Expand Up @@ -50,23 +71,32 @@ void HLW8012Component::dump_config() {
float HLW8012Component::get_setup_priority() const { return setup_priority::DATA; }
void HLW8012Component::update() {
// HLW8012 has 50% duty cycle
pulse_counter::pulse_counter_t raw_cf = this->cf_store_.read_raw_value();
pulse_counter::pulse_counter_t raw_cf1 = this->cf1_store_.read_raw_value();
float cf_hz = raw_cf / (this->get_update_interval() / 1000.0f);
if (raw_cf <= 1) {
// don't count single pulse as power
cf_hz = 0.0f;
}
float cf1_hz = raw_cf1 / (this->get_update_interval() / 1000.0f);
if (raw_cf1 <= 1) {
// don't count single pulse as anything
cf1_hz = 0.0f;

uint32_t cf_pulses;
uint32_t cf1_pulses;
uint32_t cf_diff_micros;
uint32_t cf1_diff_micros;

{
InterruptLock lock{};
cf_pulses = this->cf_pulses_;
cf1_pulses = this->cf1_pulses_;
cf_diff_micros = this->cf_last_pulse_micros_ - this->cf_first_pulse_micros_;
cf1_diff_micros = this->cf1_last_pulse_micros_ - this->cf1_first_pulse_micros_;
this->cf_pulses_ = 0;
this->cf1_pulses_ = 0;
}

if (this->nth_value_++ < 2) {
return;
}

ESP_LOGV(TAG, "CF: Got %" PRIu32 " pulses in %" PRIu32 " µs", cf_pulses, (cf_pulses == 0 ? 0 : cf_diff_micros));
ESP_LOGV(TAG, "CF1: Got %" PRIu32 " pulses in %" PRIu32 " µs", cf1_pulses, (cf1_pulses == 0 ? 0 : cf1_diff_micros));

float cf_hz = (cf_pulses > 1) ? (cf_pulses / (cf_diff_micros / MICROSECONDS_IN_SECONDS)) : 0.0f;
float cf1_hz = (cf1_pulses > 1) ? (cf1_pulses / (cf1_diff_micros / MICROSECONDS_IN_SECONDS)) : 0.0f;

float power = cf_hz * this->power_multiplier_;

if (this->change_mode_at_ != 0) {
Expand All @@ -84,14 +114,16 @@ void HLW8012Component::update() {
this->voltage_sensor_->publish_state(voltage);
}
}
} else {
ESP_LOGD(TAG, "Got power=%.1fW", power);
}

if (this->power_sensor_ != nullptr) {
this->power_sensor_->publish_state(power);
}

if (this->energy_sensor_ != nullptr) {
cf_total_pulses_ += raw_cf;
cf_total_pulses_ += cf_pulses;
float energy = cf_total_pulses_ * this->power_multiplier_ / 3600;
this->energy_sensor_->publish_state(energy);
}
Expand Down
20 changes: 9 additions & 11 deletions esphome/components/hlw8012/hlw8012.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/pulse_counter/pulse_counter_sensor.h"

#include <cinttypes>

Expand All @@ -18,16 +17,9 @@ enum HLW8012SensorModels {
HLW8012_SENSOR_MODEL_BL0937
};

#ifdef HAS_PCNT
#define USE_PCNT true
#else
#define USE_PCNT false
#endif

class HLW8012Component : public PollingComponent {
public:
HLW8012Component()
: cf_store_(*pulse_counter::get_storage(USE_PCNT)), cf1_store_(*pulse_counter::get_storage(USE_PCNT)) {}
HLW8012Component() {}

void setup() override;
void dump_config() override;
Expand All @@ -49,6 +41,8 @@ class HLW8012Component : public PollingComponent {
void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; }
void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; }

static void cf_intr(HLW8012Component *arg);
static void cf1_intr(HLW8012Component *arg);
protected:
uint32_t nth_value_{0};
bool current_mode_{false};
Expand All @@ -60,9 +54,13 @@ class HLW8012Component : public PollingComponent {
uint64_t cf_total_pulses_{0};
GPIOPin *sel_pin_;
InternalGPIOPin *cf_pin_;
pulse_counter::PulseCounterStorageBase &cf_store_;
uint32_t cf_first_pulse_micros_{0};
uint32_t cf_last_pulse_micros_{0};
uint32_t cf_pulses_{0};
InternalGPIOPin *cf1_pin_;
pulse_counter::PulseCounterStorageBase &cf1_store_;
uint32_t cf1_first_pulse_micros_{0};
uint32_t cf1_last_pulse_micros_{0};
uint32_t cf1_pulses_{0};
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *current_sensor_{nullptr};
sensor::Sensor *power_sensor_{nullptr};
Expand Down
2 changes: 0 additions & 2 deletions esphome/components/hlw8012/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@
UNIT_WATT_HOURS,
)

AUTO_LOAD = ["pulse_counter"]

hlw8012_ns = cg.esphome_ns.namespace("hlw8012")
HLW8012Component = hlw8012_ns.class_("HLW8012Component", cg.PollingComponent)
HLW8012InitialMode = hlw8012_ns.enum("HLW8012InitialMode")
Expand Down

0 comments on commit aaa23d7

Please sign in to comment.