mirror of
https://github.com/esphome/esphome.git
synced 2025-08-07 19:07:45 +00:00
dry
This commit is contained in:
parent
0df454481e
commit
f76ce5d3bb
@ -79,10 +79,21 @@ class ESP32TouchComponent : public Component {
|
|||||||
void cleanup_touch_queue_();
|
void cleanup_touch_queue_();
|
||||||
void configure_wakeup_pads_();
|
void configure_wakeup_pads_();
|
||||||
|
|
||||||
|
// Helper methods for loop() logic
|
||||||
|
void process_setup_mode_logging_(uint32_t now);
|
||||||
|
bool should_check_for_releases_(uint32_t now);
|
||||||
|
void publish_initial_state_if_needed_(ESP32TouchBinarySensor *child, uint32_t now);
|
||||||
|
void check_and_disable_loop_if_all_released_(size_t pads_off);
|
||||||
|
void calculate_release_timeout_();
|
||||||
|
|
||||||
// Common members
|
// Common members
|
||||||
std::vector<ESP32TouchBinarySensor *> children_;
|
std::vector<ESP32TouchBinarySensor *> children_;
|
||||||
bool setup_mode_{false};
|
bool setup_mode_{false};
|
||||||
uint32_t setup_mode_last_log_print_{0};
|
uint32_t setup_mode_last_log_print_{0};
|
||||||
|
uint32_t last_release_check_{0};
|
||||||
|
uint32_t release_timeout_ms_{1500};
|
||||||
|
uint32_t release_check_interval_ms_{50};
|
||||||
|
bool initial_state_published_[TOUCH_PAD_MAX] = {false};
|
||||||
|
|
||||||
// Common configuration parameters
|
// Common configuration parameters
|
||||||
uint16_t sleep_cycle_{4095};
|
uint16_t sleep_cycle_{4095};
|
||||||
@ -117,9 +128,6 @@ class ESP32TouchComponent : public Component {
|
|||||||
// 4. Queue operations provide implicit memory barriers
|
// 4. Queue operations provide implicit memory barriers
|
||||||
// Using atomic/critical sections would add overhead without meaningful benefit
|
// Using atomic/critical sections would add overhead without meaningful benefit
|
||||||
uint32_t last_touch_time_[TOUCH_PAD_MAX] = {0};
|
uint32_t last_touch_time_[TOUCH_PAD_MAX] = {0};
|
||||||
bool initial_state_published_[TOUCH_PAD_MAX] = {false};
|
|
||||||
uint32_t release_timeout_ms_{1500};
|
|
||||||
uint32_t release_check_interval_ms_{50};
|
|
||||||
uint32_t iir_filter_{0};
|
uint32_t iir_filter_{0};
|
||||||
|
|
||||||
bool iir_filter_enabled_() const { return this->iir_filter_ > 0; }
|
bool iir_filter_enabled_() const { return this->iir_filter_ > 0; }
|
||||||
@ -129,10 +137,6 @@ class ESP32TouchComponent : public Component {
|
|||||||
static void touch_isr_handler(void *arg);
|
static void touch_isr_handler(void *arg);
|
||||||
QueueHandle_t touch_queue_{nullptr};
|
QueueHandle_t touch_queue_{nullptr};
|
||||||
|
|
||||||
// Timeout-based release detection (like v1)
|
|
||||||
uint32_t release_timeout_ms_{1500};
|
|
||||||
uint32_t release_check_interval_ms_{50};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Touch event structure for ESP32 v2 (S2/S3)
|
// Touch event structure for ESP32 v2 (S2/S3)
|
||||||
// Contains touch pad and interrupt mask for queue communication
|
// Contains touch pad and interrupt mask for queue communication
|
||||||
@ -141,9 +145,8 @@ class ESP32TouchComponent : public Component {
|
|||||||
uint32_t intr_mask;
|
uint32_t intr_mask;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Track last touch time and initial state for timeout-based release detection
|
// Track last touch time for timeout-based release detection
|
||||||
uint32_t last_touch_time_[TOUCH_PAD_MAX] = {0};
|
uint32_t last_touch_time_[TOUCH_PAD_MAX] = {0};
|
||||||
bool initial_state_published_[TOUCH_PAD_MAX] = {false};
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Filter configuration
|
// Filter configuration
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
|
||||||
|
#include "soc/rtc.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace esp32_touch {
|
namespace esp32_touch {
|
||||||
|
|
||||||
@ -85,6 +87,72 @@ void ESP32TouchComponent::configure_wakeup_pads_() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ESP32TouchComponent::process_setup_mode_logging_(uint32_t now) {
|
||||||
|
if (this->setup_mode_ && now - this->setup_mode_last_log_print_ > SETUP_MODE_LOG_INTERVAL_MS) {
|
||||||
|
for (auto *child : this->children_) {
|
||||||
|
#ifdef USE_ESP32_VARIANT_ESP32
|
||||||
|
ESP_LOGD(TAG, "Touch Pad '%s' (T%" PRIu32 "): %" PRIu32, child->get_name().c_str(),
|
||||||
|
(uint32_t) child->get_touch_pad(), child->value_);
|
||||||
|
#else
|
||||||
|
// Read the value being used for touch detection
|
||||||
|
uint32_t value = this->read_touch_value(child->get_touch_pad());
|
||||||
|
ESP_LOGD(TAG, "Touch Pad '%s' (T%d): %d", child->get_name().c_str(), child->get_touch_pad(), value);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
this->setup_mode_last_log_print_ = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ESP32TouchComponent::should_check_for_releases_(uint32_t now) {
|
||||||
|
if (now - this->last_release_check_ < this->release_check_interval_ms_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this->last_release_check_ = now;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESP32TouchComponent::publish_initial_state_if_needed_(ESP32TouchBinarySensor *child, uint32_t now) {
|
||||||
|
touch_pad_t pad = child->get_touch_pad();
|
||||||
|
if (!this->initial_state_published_[pad]) {
|
||||||
|
// Check if enough time has passed since startup
|
||||||
|
if (now > this->release_timeout_ms_) {
|
||||||
|
child->publish_initial_state(false);
|
||||||
|
this->initial_state_published_[pad] = true;
|
||||||
|
ESP_LOGV(TAG, "Touch Pad '%s' state: OFF (initial)", child->get_name().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESP32TouchComponent::check_and_disable_loop_if_all_released_(size_t pads_off) {
|
||||||
|
// Disable the loop to save CPU cycles when all pads are off and not in setup mode.
|
||||||
|
if (pads_off == this->children_.size() && !this->setup_mode_) {
|
||||||
|
this->disable_loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESP32TouchComponent::calculate_release_timeout_() {
|
||||||
|
// Calculate release timeout based on sleep cycle
|
||||||
|
// Design note: Hardware limitation - interrupts only fire reliably on touch (not release)
|
||||||
|
// We must use timeout-based detection for release events
|
||||||
|
// Formula: 3 sleep cycles converted to ms, with MINIMUM_RELEASE_TIME_MS minimum
|
||||||
|
// Per ESP-IDF docs: t_sleep = sleep_cycle / SOC_CLK_RC_SLOW_FREQ_APPROX
|
||||||
|
|
||||||
|
uint32_t rtc_freq = rtc_clk_slow_freq_get_hz();
|
||||||
|
|
||||||
|
// Calculate timeout as 3 sleep cycles
|
||||||
|
this->release_timeout_ms_ = (this->sleep_cycle_ * 1000 * 3) / rtc_freq;
|
||||||
|
|
||||||
|
if (this->release_timeout_ms_ < MINIMUM_RELEASE_TIME_MS) {
|
||||||
|
this->release_timeout_ms_ = MINIMUM_RELEASE_TIME_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for releases at 1/4 the timeout interval
|
||||||
|
// Since hardware doesn't generate reliable release interrupts, we must poll
|
||||||
|
// for releases in the main loop. Checking at 1/4 the timeout interval provides
|
||||||
|
// a good balance between responsiveness and efficiency.
|
||||||
|
this->release_check_interval_ms_ = this->release_timeout_ms_ / 4;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace esp32_touch
|
} // namespace esp32_touch
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
|
@ -10,8 +10,6 @@
|
|||||||
|
|
||||||
// Include HAL for ISR-safe touch reading
|
// Include HAL for ISR-safe touch reading
|
||||||
#include "hal/touch_sensor_ll.h"
|
#include "hal/touch_sensor_ll.h"
|
||||||
// Include for RTC clock frequency
|
|
||||||
#include "soc/rtc.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace esp32_touch {
|
namespace esp32_touch {
|
||||||
@ -59,20 +57,7 @@ void ESP32TouchComponent::setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calculate release timeout based on sleep cycle
|
// Calculate release timeout based on sleep cycle
|
||||||
// Design note: ESP32 v1 hardware limitation - interrupts only fire on touch (not release)
|
this->calculate_release_timeout_();
|
||||||
// We must use timeout-based detection for release events
|
|
||||||
// Formula: 3 sleep cycles converted to ms, with MINIMUM_RELEASE_TIME_MS minimum
|
|
||||||
// The division by 2 accounts for the fact that sleep_cycle is in half-cycles
|
|
||||||
uint32_t rtc_freq = rtc_clk_slow_freq_get_hz();
|
|
||||||
this->release_timeout_ms_ = (this->sleep_cycle_ * 1000 * 3) / (rtc_freq * 2);
|
|
||||||
if (this->release_timeout_ms_ < MINIMUM_RELEASE_TIME_MS) {
|
|
||||||
this->release_timeout_ms_ = MINIMUM_RELEASE_TIME_MS;
|
|
||||||
}
|
|
||||||
// Check for releases at 1/4 the timeout interval
|
|
||||||
// Since the ESP32 v1 hardware doesn't generate release interrupts, we must poll
|
|
||||||
// for releases in the main loop. Checking at 1/4 the timeout interval provides
|
|
||||||
// a good balance between responsiveness and efficiency.
|
|
||||||
this->release_check_interval_ms_ = this->release_timeout_ms_ / 4;
|
|
||||||
|
|
||||||
// Enable touch pad interrupt
|
// Enable touch pad interrupt
|
||||||
touch_pad_intr_enable();
|
touch_pad_intr_enable();
|
||||||
@ -98,13 +83,7 @@ void ESP32TouchComponent::loop() {
|
|||||||
const uint32_t now = App.get_loop_component_start_time();
|
const uint32_t now = App.get_loop_component_start_time();
|
||||||
|
|
||||||
// Print debug info for all pads in setup mode
|
// Print debug info for all pads in setup mode
|
||||||
if (this->setup_mode_ && now - this->setup_mode_last_log_print_ > SETUP_MODE_LOG_INTERVAL_MS) {
|
this->process_setup_mode_logging_(now);
|
||||||
for (auto *child : this->children_) {
|
|
||||||
ESP_LOGD(TAG, "Touch Pad '%s' (T%" PRIu32 "): %" PRIu32, child->get_name().c_str(),
|
|
||||||
(uint32_t) child->get_touch_pad(), child->value_);
|
|
||||||
}
|
|
||||||
this->setup_mode_last_log_print_ = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process any queued touch events from interrupts
|
// Process any queued touch events from interrupts
|
||||||
// Note: Events are only sent by ISR for pads that were measured in that cycle (value != 0)
|
// Note: Events are only sent by ISR for pads that were measured in that cycle (value != 0)
|
||||||
@ -142,25 +121,20 @@ void ESP32TouchComponent::loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for released pads periodically
|
// Check for released pads periodically
|
||||||
static uint32_t last_release_check = 0;
|
if (!this->should_check_for_releases_(now)) {
|
||||||
if (now - last_release_check < this->release_check_interval_ms_) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
last_release_check = now;
|
|
||||||
|
|
||||||
size_t pads_off = 0;
|
size_t pads_off = 0;
|
||||||
for (auto *child : this->children_) {
|
for (auto *child : this->children_) {
|
||||||
touch_pad_t pad = child->get_touch_pad();
|
touch_pad_t pad = child->get_touch_pad();
|
||||||
|
|
||||||
// Handle initial state publication after startup
|
// Handle initial state publication after startup
|
||||||
|
this->publish_initial_state_if_needed_(child, now);
|
||||||
|
|
||||||
if (!this->initial_state_published_[pad]) {
|
if (!this->initial_state_published_[pad]) {
|
||||||
// Check if enough time has passed since startup
|
// Not yet published, don't count as off
|
||||||
if (now > this->release_timeout_ms_) {
|
continue;
|
||||||
child->publish_initial_state(false);
|
|
||||||
this->initial_state_published_[pad] = true;
|
|
||||||
ESP_LOGV(TAG, "Touch Pad '%s' state: OFF (initial)", child->get_name().c_str());
|
|
||||||
pads_off++;
|
|
||||||
}
|
|
||||||
} else if (child->last_state_) {
|
} else if (child->last_state_) {
|
||||||
// Pad is currently in touched state - check for release timeout
|
// Pad is currently in touched state - check for release timeout
|
||||||
// Using subtraction handles 32-bit rollover correctly
|
// Using subtraction handles 32-bit rollover correctly
|
||||||
@ -186,9 +160,7 @@ void ESP32TouchComponent::loop() {
|
|||||||
// - v1 only generates interrupts on touch events (not releases)
|
// - v1 only generates interrupts on touch events (not releases)
|
||||||
// - We must poll for release timeouts in the main loop
|
// - We must poll for release timeouts in the main loop
|
||||||
// - We can only safely disable when no pads need timeout monitoring
|
// - We can only safely disable when no pads need timeout monitoring
|
||||||
if (pads_off == this->children_.size() && !this->setup_mode_) {
|
this->check_and_disable_loop_if_all_released_(pads_off);
|
||||||
this->disable_loop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESP32TouchComponent::on_shutdown() {
|
void ESP32TouchComponent::on_shutdown() {
|
||||||
|
@ -135,6 +135,9 @@ void ESP32TouchComponent::setup() {
|
|||||||
// Start FSM
|
// Start FSM
|
||||||
touch_pad_fsm_start();
|
touch_pad_fsm_start();
|
||||||
|
|
||||||
|
// Calculate release timeout based on sleep cycle
|
||||||
|
this->calculate_release_timeout_();
|
||||||
|
|
||||||
// Initialize tracking arrays
|
// Initialize tracking arrays
|
||||||
for (size_t i = 0; i < TOUCH_PAD_MAX; i++) {
|
for (size_t i = 0; i < TOUCH_PAD_MAX; i++) {
|
||||||
this->last_touch_time_[i] = 0;
|
this->last_touch_time_[i] = 0;
|
||||||
@ -279,15 +282,7 @@ void ESP32TouchComponent::loop() {
|
|||||||
// This prevents false releases if we missed interrupts
|
// This prevents false releases if we missed interrupts
|
||||||
|
|
||||||
// In setup mode, periodically log all pad values
|
// In setup mode, periodically log all pad values
|
||||||
if (this->setup_mode_ && now - this->setup_mode_last_log_print_ > SETUP_MODE_LOG_INTERVAL_MS) {
|
this->process_setup_mode_logging_(now);
|
||||||
for (auto *child : this->children_) {
|
|
||||||
// Read the value being used for touch detection
|
|
||||||
uint32_t value = this->read_touch_value(child->get_touch_pad());
|
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Touch Pad '%s' (T%d): %d", child->get_name().c_str(), child->get_touch_pad(), value);
|
|
||||||
}
|
|
||||||
this->setup_mode_last_log_print_ = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process any queued touch events from interrupts
|
// Process any queued touch events from interrupts
|
||||||
TouchPadEventV2 event;
|
TouchPadEventV2 event;
|
||||||
@ -320,25 +315,20 @@ void ESP32TouchComponent::loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for released pads periodically (like v1)
|
// Check for released pads periodically (like v1)
|
||||||
static uint32_t last_release_check = 0;
|
if (!this->should_check_for_releases_(now)) {
|
||||||
if (now - last_release_check < this->release_check_interval_ms_) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
last_release_check = now;
|
|
||||||
|
|
||||||
size_t pads_off = 0;
|
size_t pads_off = 0;
|
||||||
for (auto *child : this->children_) {
|
for (auto *child : this->children_) {
|
||||||
touch_pad_t pad = child->get_touch_pad();
|
touch_pad_t pad = child->get_touch_pad();
|
||||||
|
|
||||||
// Handle initial state publication after startup
|
// Handle initial state publication after startup
|
||||||
|
this->publish_initial_state_if_needed_(child, now);
|
||||||
|
|
||||||
if (!this->initial_state_published_[pad]) {
|
if (!this->initial_state_published_[pad]) {
|
||||||
// Check if enough time has passed since startup
|
// Not yet published, don't count as off
|
||||||
if (now > this->release_timeout_ms_) {
|
continue;
|
||||||
child->publish_initial_state(false);
|
|
||||||
this->initial_state_published_[pad] = true;
|
|
||||||
ESP_LOGV(TAG, "Touch Pad '%s' state: OFF (initial)", child->get_name().c_str());
|
|
||||||
pads_off++;
|
|
||||||
}
|
|
||||||
} else if (child->last_state_) {
|
} else if (child->last_state_) {
|
||||||
// Pad is currently in touched state - check for release timeout
|
// Pad is currently in touched state - check for release timeout
|
||||||
// Using subtraction handles 32-bit rollover correctly
|
// Using subtraction handles 32-bit rollover correctly
|
||||||
@ -369,9 +359,7 @@ void ESP32TouchComponent::loop() {
|
|||||||
|
|
||||||
// Disable the loop when all pads are off and not in setup mode (like v1)
|
// Disable the loop when all pads are off and not in setup mode (like v1)
|
||||||
// We need to keep checking for timeouts, so only disable when all pads are confirmed off
|
// We need to keep checking for timeouts, so only disable when all pads are confirmed off
|
||||||
if (pads_off == this->children_.size() && !this->setup_mode_) {
|
this->check_and_disable_loop_if_all_released_(pads_off);
|
||||||
this->disable_loop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESP32TouchComponent::on_shutdown() {
|
void ESP32TouchComponent::on_shutdown() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user