Calculating the AC only component of the samples (#1906)

Co-authored-by: Synco Reynders <synco@deviceware.co.nz>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
synco 2021-09-20 19:12:50 +12:00 committed by GitHub
parent 272ceadbb0
commit 5f21b925da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 18 additions and 43 deletions

View file

@ -8,18 +8,6 @@ namespace ct_clamp {
static const char *const TAG = "ct_clamp"; static const char *const TAG = "ct_clamp";
void CTClampSensor::setup() {
this->is_calibrating_offset_ = true;
this->high_freq_.start();
this->set_timeout("calibrate_offset", this->sample_duration_, [this]() {
this->high_freq_.stop();
this->is_calibrating_offset_ = false;
if (this->num_samples_ != 0) {
this->offset_ = this->sample_sum_ / this->num_samples_;
}
});
}
void CTClampSensor::dump_config() { void CTClampSensor::dump_config() {
LOG_SENSOR("", "CT Clamp Sensor", this); LOG_SENSOR("", "CT Clamp Sensor", this);
ESP_LOGCONFIG(TAG, " Sample Duration: %.2fs", this->sample_duration_ / 1e3f); ESP_LOGCONFIG(TAG, " Sample Duration: %.2fs", this->sample_duration_ / 1e3f);
@ -27,9 +15,6 @@ void CTClampSensor::dump_config() {
} }
void CTClampSensor::update() { void CTClampSensor::update() {
if (this->is_calibrating_offset_)
return;
// Update only starts the sampling phase, in loop() the actual sampling is happening. // Update only starts the sampling phase, in loop() the actual sampling is happening.
// Request a high loop() execution interval during sampling phase. // Request a high loop() execution interval during sampling phase.
@ -46,20 +31,23 @@ void CTClampSensor::update() {
return; return;
} }
float raw = this->sample_sum_ / this->num_samples_; float dc = this->sample_sum_ / this->num_samples_;
float irms = std::sqrt(raw); float var = (this->sample_squared_sum_ / this->num_samples_) - dc * dc;
ESP_LOGD(TAG, "'%s' - Raw Value: %.2fA", this->name_.c_str(), irms); float ac = std::sqrt(var);
this->publish_state(irms); ESP_LOGD(TAG, "'%s' - Got %d samples", this->name_.c_str(), this->num_samples_);
ESP_LOGD(TAG, "'%s' - Raw AC Value: %.3fA", this->name_.c_str(), ac);
this->publish_state(ac);
}); });
// Set sampling values // Set sampling values
this->is_sampling_ = true; this->is_sampling_ = true;
this->num_samples_ = 0; this->num_samples_ = 0;
this->sample_sum_ = 0.0f; this->sample_sum_ = 0.0f;
this->sample_squared_sum_ = 0.0f;
} }
void CTClampSensor::loop() { void CTClampSensor::loop() {
if (!this->is_sampling_ && !this->is_calibrating_offset_) if (!this->is_sampling_)
return; return;
// Perform a single sample // Perform a single sample
@ -67,22 +55,8 @@ void CTClampSensor::loop() {
if (isnan(value)) if (isnan(value))
return; return;
if (this->is_calibrating_offset_) { this->sample_sum_ += value;
this->sample_sum_ += value; this->sample_squared_sum_ += value * value;
this->num_samples_++;
return;
}
// Adjust DC offset via low pass filter (exponential moving average)
const float alpha = 0.001f;
this->offset_ = this->offset_ * (1 - alpha) + value * alpha;
// Filtered value centered around the mid-point (0V)
float filtered = value - this->offset_;
// IRMS is sqrt(∑v_i²)
float sq = filtered * filtered;
this->sample_sum_ += sq;
this->num_samples_++; this->num_samples_++;
} }

View file

@ -10,7 +10,6 @@ namespace ct_clamp {
class CTClampSensor : public sensor::Sensor, public PollingComponent { class CTClampSensor : public sensor::Sensor, public PollingComponent {
public: public:
void setup() override;
void update() override; void update() override;
void loop() override; void loop() override;
void dump_config() override; void dump_config() override;
@ -35,17 +34,19 @@ class CTClampSensor : public sensor::Sensor, public PollingComponent {
* *
* Diagram: https://learn.openenergymonitor.org/electricity-monitoring/ct-sensors/interface-with-arduino * Diagram: https://learn.openenergymonitor.org/electricity-monitoring/ct-sensors/interface-with-arduino
* *
* This is automatically calculated with an exponential moving average/digital low pass filter. * The current clamp only measures AC, so any DC component is an unwanted artifact from the
* * sampling circuit. The AC component is essentially the same as the calculating the Standard-Deviation,
* 0.5 is a good initial approximation to start with for most ESP8266 setups. * which can be done by cumulating 3 values per sample:
* 1) Number of samples
* 2) Sum of samples
* 3) Sum of sample squared
* https://en.wikipedia.org/wiki/Root_mean_square
*/ */
float offset_ = 0.5f;
float sample_sum_ = 0.0f; float sample_sum_ = 0.0f;
float sample_squared_sum_ = 0.0f;
uint32_t num_samples_ = 0; uint32_t num_samples_ = 0;
bool is_sampling_ = false; bool is_sampling_ = false;
/// Calibrate offset value once at boot
bool is_calibrating_offset_ = false;
}; };
} // namespace ct_clamp } // namespace ct_clamp