mirror of
https://github.com/esphome/esphome.git
synced 2024-11-27 12:37:45 +01:00
Self calibration for ESP32 touch controls
This commit is contained in:
parent
ef7c5c6055
commit
344992c670
@ -1,27 +1,74 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_INITIAL_VALUE,
|
||||
CONF_MODE,
|
||||
CONF_PIN,
|
||||
CONF_THRESHOLD,
|
||||
CONF_ID,
|
||||
CONF_VALUE,
|
||||
)
|
||||
from . import esp32_touch_ns, ESP32TouchComponent, validate_touch_pad
|
||||
|
||||
from . import ESP32TouchComponent, esp32_touch_ns, validate_touch_pad
|
||||
|
||||
DEPENDENCIES = ["esp32_touch", "esp32"]
|
||||
|
||||
CONF_ESP32_TOUCH_ID = "esp32_touch_id"
|
||||
CONF_WAKEUP_THRESHOLD = "wakeup_threshold"
|
||||
CONF_LOOKBACK_NUM_VALUES = "lookback_num_values"
|
||||
CONF_SCAN_INTERVAL = "scan_interval"
|
||||
CONF_MAX_DEVIATION = "max_deviation"
|
||||
CONF_MAX_CONSECUTIVE_ANOMALIES = "max_consecutive_anomalies"
|
||||
|
||||
THRESHOLD_MODE_STATIC = "static"
|
||||
THRESHOLD_MODE_DYNAMIC = "dynamic"
|
||||
|
||||
ESP32TouchBinarySensor = esp32_touch_ns.class_(
|
||||
"ESP32TouchBinarySensor", binary_sensor.BinarySensor
|
||||
)
|
||||
|
||||
THRESHOLD_SCHEMA_STATIC = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_MODE): THRESHOLD_MODE_STATIC,
|
||||
cv.Required(CONF_VALUE): cv.uint32_t,
|
||||
}
|
||||
)
|
||||
|
||||
THRESHOLD_SCHEMA_DYNAMIC = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_MODE): THRESHOLD_MODE_DYNAMIC,
|
||||
cv.Optional(CONF_INITIAL_VALUE, default=0): cv.uint32_t,
|
||||
cv.Optional(CONF_LOOKBACK_NUM_VALUES, default=5): cv.int_range(min=1),
|
||||
cv.Optional(
|
||||
CONF_SCAN_INTERVAL, default="1s"
|
||||
): cv.positive_time_period_milliseconds,
|
||||
cv.Optional(CONF_MAX_DEVIATION, default="0.5%"): cv.percentage,
|
||||
cv.Optional(CONF_MAX_CONSECUTIVE_ANOMALIES, default=10): cv.positive_int,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _convert_int_to_threshold(config):
|
||||
"""Convert legacy threshold value to new format."""
|
||||
try:
|
||||
threshold_value = cv.uint32_t(config)
|
||||
return {CONF_MODE: THRESHOLD_MODE_STATIC, CONF_VALUE: threshold_value}
|
||||
except cv.Invalid:
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(ESP32TouchBinarySensor).extend(
|
||||
{
|
||||
cv.GenerateID(CONF_ESP32_TOUCH_ID): cv.use_id(ESP32TouchComponent),
|
||||
cv.Required(CONF_PIN): validate_touch_pad,
|
||||
cv.Required(CONF_THRESHOLD): cv.uint32_t,
|
||||
cv.Required(CONF_THRESHOLD): cv.All(
|
||||
_convert_int_to_threshold,
|
||||
cv.Any(
|
||||
THRESHOLD_SCHEMA_STATIC.schema,
|
||||
THRESHOLD_SCHEMA_DYNAMIC.schema,
|
||||
),
|
||||
),
|
||||
cv.Optional(CONF_WAKEUP_THRESHOLD, default=0): cv.uint32_t,
|
||||
}
|
||||
)
|
||||
@ -32,8 +79,26 @@ async def to_code(config):
|
||||
var = cg.new_Pvariable(
|
||||
config[CONF_ID],
|
||||
config[CONF_PIN],
|
||||
config[CONF_THRESHOLD],
|
||||
(
|
||||
config[CONF_THRESHOLD][CONF_VALUE]
|
||||
if config[CONF_THRESHOLD][CONF_MODE] == THRESHOLD_MODE_STATIC
|
||||
else config[CONF_THRESHOLD][CONF_INITIAL_VALUE]
|
||||
),
|
||||
config[CONF_WAKEUP_THRESHOLD],
|
||||
)
|
||||
await binary_sensor.register_binary_sensor(var, config)
|
||||
cg.add(hub.register_touch_pad(var))
|
||||
|
||||
if config[CONF_THRESHOLD][CONF_MODE] == THRESHOLD_MODE_DYNAMIC:
|
||||
cg.add(var.set_max_deviation(config[CONF_THRESHOLD][CONF_MAX_DEVIATION]))
|
||||
cg.add(
|
||||
var.set_max_consecutive_anomalies(
|
||||
config[CONF_THRESHOLD][CONF_MAX_CONSECUTIVE_ANOMALIES]
|
||||
)
|
||||
)
|
||||
cg.add(
|
||||
var.start_calibration(
|
||||
config[CONF_THRESHOLD][CONF_SCAN_INTERVAL],
|
||||
config[CONF_THRESHOLD][CONF_LOOKBACK_NUM_VALUES],
|
||||
)
|
||||
)
|
||||
|
@ -287,6 +287,10 @@ void ESP32TouchComponent::loop() {
|
||||
bool should_print = this->setup_mode_ && now - this->setup_mode_last_log_print_ > 250;
|
||||
for (auto *child : this->children_) {
|
||||
child->value_ = this->component_touch_pad_read(child->get_touch_pad());
|
||||
if (child->dynamic_calibration_ && (now - child->last_calibration_timestamp_ > child->calibration_interval_)) {
|
||||
child->insert_value_();
|
||||
child->last_calibration_timestamp_ += child->last_calibration_timestamp_ ? child->calibration_interval_ : now;
|
||||
}
|
||||
#if !(defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3))
|
||||
child->publish_state(child->value_ < child->get_threshold());
|
||||
#else
|
||||
@ -340,6 +344,57 @@ void ESP32TouchComponent::on_shutdown() {
|
||||
ESP32TouchBinarySensor::ESP32TouchBinarySensor(touch_pad_t touch_pad, uint32_t threshold, uint32_t wakeup_threshold)
|
||||
: touch_pad_(touch_pad), threshold_(threshold), wakeup_threshold_(wakeup_threshold) {}
|
||||
|
||||
void ESP32TouchBinarySensor::set_max_deviation(float max_deviation) { this->max_deviation_ = max_deviation; }
|
||||
|
||||
void ESP32TouchBinarySensor::set_max_consecutive_anomalies(float max_consecutive_anomalies) {
|
||||
this->max_consecutive_anomalies_ = max_consecutive_anomalies;
|
||||
}
|
||||
|
||||
void ESP32TouchBinarySensor::start_calibration(uint32_t interval, uint16_t num_values) {
|
||||
this->dynamic_calibration_ = true;
|
||||
this->max_prev_values_ = num_values;
|
||||
this->prev_values_.resize(0);
|
||||
this->prev_values_.push_front(this->threshold_);
|
||||
this->sum_values_ = this->threshold_;
|
||||
this->calibration_interval_ = interval;
|
||||
this->last_calibration_timestamp_ = 0;
|
||||
this->consecutive_anomalies_ = 0;
|
||||
}
|
||||
|
||||
float ESP32TouchBinarySensor::get_average_value_() {
|
||||
if (this->prev_values_.size() == 0)
|
||||
return 0;
|
||||
|
||||
return uint32_t(this->sum_values_ / float(this->prev_values_.size()));
|
||||
}
|
||||
|
||||
void ESP32TouchBinarySensor::insert_value_() {
|
||||
float avg = this->get_average_value_();
|
||||
if (fabs(float(this->value_) - avg) / avg > this->max_deviation_) {
|
||||
this->consecutive_anomalies_++;
|
||||
if (this->consecutive_anomalies_ < this->max_consecutive_anomalies_)
|
||||
return;
|
||||
}
|
||||
|
||||
this->consecutive_anomalies_ = 0;
|
||||
|
||||
this->prev_values_.push_front(this->value_);
|
||||
this->sum_values_ += this->value_;
|
||||
|
||||
while (this->prev_values_.size() > this->max_prev_values_) {
|
||||
this->sum_values_ -= this->prev_values_.back();
|
||||
this->prev_values_.pop_back();
|
||||
}
|
||||
|
||||
#if !(defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3))
|
||||
this->threshold_ =
|
||||
uint32_t(float(this->sum_values_) / float(this->prev_values_.size()) * (1.0 - this->max_deviation_));
|
||||
#else
|
||||
this->threshold_ =
|
||||
uint32_t(float(this->sum_values_) / float(this->prev_values_.size()) * (1.0 + this->max_deviation_));
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace esp32_touch
|
||||
} // namespace esphome
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <esp_idf_version.h>
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
#include <driver/touch_sensor.h>
|
||||
|
||||
@ -106,6 +107,9 @@ class ESP32TouchBinarySensor : public binary_sensor::BinarySensor {
|
||||
void set_threshold(uint32_t threshold) { this->threshold_ = threshold; }
|
||||
uint32_t get_value() const { return this->value_; }
|
||||
uint32_t get_wakeup_threshold() const { return this->wakeup_threshold_; }
|
||||
void set_max_deviation(float max_deviation);
|
||||
void set_max_consecutive_anomalies(float max_consecutive_anomalies);
|
||||
void start_calibration(uint32_t interval, uint16_t num_values);
|
||||
|
||||
protected:
|
||||
friend ESP32TouchComponent;
|
||||
@ -114,6 +118,19 @@ class ESP32TouchBinarySensor : public binary_sensor::BinarySensor {
|
||||
uint32_t threshold_{0};
|
||||
uint32_t value_{0};
|
||||
const uint32_t wakeup_threshold_{0};
|
||||
|
||||
bool dynamic_calibration_{false};
|
||||
float max_deviation_{0};
|
||||
std::deque<uint32_t> prev_values_;
|
||||
uint32_t sum_values_{0};
|
||||
uint16_t max_prev_values_{0};
|
||||
uint32_t last_calibration_timestamp_{0};
|
||||
uint32_t calibration_interval_{0};
|
||||
uint16_t consecutive_anomalies_{0};
|
||||
uint16_t max_consecutive_anomalies_{0};
|
||||
|
||||
float get_average_value_();
|
||||
void insert_value_();
|
||||
};
|
||||
|
||||
} // namespace esp32_touch
|
||||
|
Loading…
Reference in New Issue
Block a user