mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	dry
This commit is contained in:
		| @@ -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() { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user