mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	dry
This commit is contained in:
		| @@ -79,10 +79,21 @@ class ESP32TouchComponent : public Component { | ||||
|   void cleanup_touch_queue_(); | ||||
|   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 | ||||
|   std::vector<ESP32TouchBinarySensor *> children_; | ||||
|   bool setup_mode_{false}; | ||||
|   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 | ||||
|   uint16_t sleep_cycle_{4095}; | ||||
| @@ -117,9 +128,6 @@ class ESP32TouchComponent : public Component { | ||||
|   // 4. Queue operations provide implicit memory barriers | ||||
|   // Using atomic/critical sections would add overhead without meaningful benefit | ||||
|   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}; | ||||
|  | ||||
|   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); | ||||
|   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: | ||||
|   // Touch event structure for ESP32 v2 (S2/S3) | ||||
|   // Contains touch pad and interrupt mask for queue communication | ||||
| @@ -141,9 +145,8 @@ class ESP32TouchComponent : public Component { | ||||
|     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}; | ||||
|   bool initial_state_published_[TOUCH_PAD_MAX] = {false}; | ||||
|  | ||||
|  protected: | ||||
|   // Filter configuration | ||||
|   | ||||
| @@ -4,6 +4,8 @@ | ||||
| #include "esphome/core/log.h" | ||||
| #include <cinttypes> | ||||
|  | ||||
| #include "soc/rtc.h" | ||||
|  | ||||
| namespace esphome { | ||||
| 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 esphome | ||||
|  | ||||
|   | ||||
| @@ -10,8 +10,6 @@ | ||||
|  | ||||
| // Include HAL for ISR-safe touch reading | ||||
| #include "hal/touch_sensor_ll.h" | ||||
| // Include for RTC clock frequency | ||||
| #include "soc/rtc.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace esp32_touch { | ||||
| @@ -59,20 +57,7 @@ void ESP32TouchComponent::setup() { | ||||
|   } | ||||
|  | ||||
|   // Calculate release timeout based on sleep cycle | ||||
|   // Design note: ESP32 v1 hardware limitation - interrupts only fire 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 | ||||
|   // 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; | ||||
|   this->calculate_release_timeout_(); | ||||
|  | ||||
|   // Enable touch pad interrupt | ||||
|   touch_pad_intr_enable(); | ||||
| @@ -98,13 +83,7 @@ void ESP32TouchComponent::loop() { | ||||
|   const uint32_t now = App.get_loop_component_start_time(); | ||||
|  | ||||
|   // 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) { | ||||
|     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; | ||||
|   } | ||||
|   this->process_setup_mode_logging_(now); | ||||
|  | ||||
|   // Process any queued touch events from interrupts | ||||
|   // 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 | ||||
|   static uint32_t last_release_check = 0; | ||||
|   if (now - last_release_check < this->release_check_interval_ms_) { | ||||
|   if (!this->should_check_for_releases_(now)) { | ||||
|     return; | ||||
|   } | ||||
|   last_release_check = now; | ||||
|  | ||||
|   size_t pads_off = 0; | ||||
|   for (auto *child : this->children_) { | ||||
|     touch_pad_t pad = child->get_touch_pad(); | ||||
|  | ||||
|     // Handle initial state publication after startup | ||||
|     this->publish_initial_state_if_needed_(child, now); | ||||
|  | ||||
|     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()); | ||||
|         pads_off++; | ||||
|       } | ||||
|       // Not yet published, don't count as off | ||||
|       continue; | ||||
|     } else if (child->last_state_) { | ||||
|       // Pad is currently in touched state - check for release timeout | ||||
|       // Using subtraction handles 32-bit rollover correctly | ||||
| @@ -186,9 +160,7 @@ void ESP32TouchComponent::loop() { | ||||
|   // - v1 only generates interrupts on touch events (not releases) | ||||
|   // - We must poll for release timeouts in the main loop | ||||
|   // - We can only safely disable when no pads need timeout monitoring | ||||
|   if (pads_off == this->children_.size() && !this->setup_mode_) { | ||||
|     this->disable_loop(); | ||||
|   } | ||||
|   this->check_and_disable_loop_if_all_released_(pads_off); | ||||
| } | ||||
|  | ||||
| void ESP32TouchComponent::on_shutdown() { | ||||
|   | ||||
| @@ -135,6 +135,9 @@ void ESP32TouchComponent::setup() { | ||||
|   // Start FSM | ||||
|   touch_pad_fsm_start(); | ||||
|  | ||||
|   // Calculate release timeout based on sleep cycle | ||||
|   this->calculate_release_timeout_(); | ||||
|  | ||||
|   // Initialize tracking arrays | ||||
|   for (size_t i = 0; i < TOUCH_PAD_MAX; i++) { | ||||
|     this->last_touch_time_[i] = 0; | ||||
| @@ -279,15 +282,7 @@ void ESP32TouchComponent::loop() { | ||||
|   //    This prevents false releases if we missed interrupts | ||||
|  | ||||
|   // In setup mode, periodically log all pad values | ||||
|   if (this->setup_mode_ && now - this->setup_mode_last_log_print_ > SETUP_MODE_LOG_INTERVAL_MS) { | ||||
|     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; | ||||
|   } | ||||
|   this->process_setup_mode_logging_(now); | ||||
|  | ||||
|   // Process any queued touch events from interrupts | ||||
|   TouchPadEventV2 event; | ||||
| @@ -320,25 +315,20 @@ void ESP32TouchComponent::loop() { | ||||
|   } | ||||
|  | ||||
|   // Check for released pads periodically (like v1) | ||||
|   static uint32_t last_release_check = 0; | ||||
|   if (now - last_release_check < this->release_check_interval_ms_) { | ||||
|   if (!this->should_check_for_releases_(now)) { | ||||
|     return; | ||||
|   } | ||||
|   last_release_check = now; | ||||
|  | ||||
|   size_t pads_off = 0; | ||||
|   for (auto *child : this->children_) { | ||||
|     touch_pad_t pad = child->get_touch_pad(); | ||||
|  | ||||
|     // Handle initial state publication after startup | ||||
|     this->publish_initial_state_if_needed_(child, now); | ||||
|  | ||||
|     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()); | ||||
|         pads_off++; | ||||
|       } | ||||
|       // Not yet published, don't count as off | ||||
|       continue; | ||||
|     } else if (child->last_state_) { | ||||
|       // Pad is currently in touched state - check for release timeout | ||||
|       // 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) | ||||
|   // 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->disable_loop(); | ||||
|   } | ||||
|   this->check_and_disable_loop_if_all_released_(pads_off); | ||||
| } | ||||
|  | ||||
| void ESP32TouchComponent::on_shutdown() { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user