mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	preen
This commit is contained in:
		| @@ -11,6 +11,7 @@ | ||||
| #include <driver/touch_sensor.h> | ||||
| #include <freertos/FreeRTOS.h> | ||||
| #include <freertos/queue.h> | ||||
| #include <freertos/ringbuf.h> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace esp32_touch { | ||||
| @@ -83,13 +84,16 @@ class ESP32TouchComponent : public Component { | ||||
|   static constexpr uint32_t MINIMUM_RELEASE_TIME_MS = 100; | ||||
|  | ||||
|   static void touch_isr_handler(void *arg); | ||||
|   QueueHandle_t touch_queue_{nullptr}; | ||||
|  | ||||
|   // Ring buffer handle for FreeRTOS ring buffer | ||||
|   RingbufHandle_t ring_buffer_handle_{nullptr}; | ||||
|   uint32_t ring_buffer_overflow_count_{0}; | ||||
|  | ||||
|   // Design note: last_touch_time_ does not require synchronization primitives because: | ||||
|   // 1. ESP32 guarantees atomic 32-bit aligned reads/writes | ||||
|   // 2. ISR only writes timestamps, main loop only reads (except sentinel value 1) | ||||
|   // 3. Timing tolerance allows for occasional stale reads (50ms check interval) | ||||
|   // 4. Queue operations provide implicit memory barriers | ||||
|   // 4. Ring buffer operations provide implicit memory barriers | ||||
|   // Using atomic/critical sections would add overhead without meaningful benefit | ||||
|   uint32_t last_touch_time_[TOUCH_PAD_MAX] = {0}; | ||||
|   uint32_t release_timeout_ms_{1500}; | ||||
|   | ||||
| @@ -12,16 +12,19 @@ | ||||
| #include "hal/touch_sensor_ll.h" | ||||
| // Include for RTC clock frequency | ||||
| #include "soc/rtc.h" | ||||
| // Include FreeRTOS ring buffer | ||||
| #include "freertos/ringbuf.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace esp32_touch { | ||||
|  | ||||
| static const char *const TAG = "esp32_touch"; | ||||
|  | ||||
| struct TouchPadEventV1 { | ||||
|   touch_pad_t pad; | ||||
|   uint32_t value; | ||||
|   bool is_touched; | ||||
| // Structure for a single pad's state in the ring buffer | ||||
| struct TouchPadState { | ||||
|   uint8_t pad;      // touch_pad_t | ||||
|   uint32_t value;   // Current reading | ||||
|   bool is_touched;  // Touch state | ||||
| }; | ||||
|  | ||||
| void ESP32TouchComponent::setup() { | ||||
| @@ -30,14 +33,19 @@ void ESP32TouchComponent::setup() { | ||||
|   touch_pad_init(); | ||||
|   touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER); | ||||
|  | ||||
|   // Create queue for touch events | ||||
|   size_t queue_size = this->children_.size() * 4; | ||||
|   if (queue_size < 8) | ||||
|     queue_size = 8; | ||||
|   // Create ring buffer for touch events | ||||
|   // Size calculation: We need space for multiple snapshots | ||||
|   // Each snapshot contains: array of TouchPadState structures | ||||
|   size_t pad_state_size = sizeof(TouchPadState); | ||||
|   size_t snapshot_size = this->children_.size() * pad_state_size; | ||||
|  | ||||
|   this->touch_queue_ = xQueueCreate(queue_size, sizeof(TouchPadEventV1)); | ||||
|   if (this->touch_queue_ == nullptr) { | ||||
|     ESP_LOGE(TAG, "Failed to create touch event queue of size %d", queue_size); | ||||
|   // Allow for 4 snapshots in the buffer to handle normal operation and bursts | ||||
|   size_t buffer_size = snapshot_size * 4; | ||||
|  | ||||
|   // Create a byte buffer ring buffer (allows variable sized items) | ||||
|   this->ring_buffer_handle_ = xRingbufferCreate(buffer_size, RINGBUF_TYPE_BYTEBUF); | ||||
|   if (this->ring_buffer_handle_ == nullptr) { | ||||
|     ESP_LOGE(TAG, "Failed to create ring buffer of size %d", buffer_size); | ||||
|     this->mark_failed(); | ||||
|     return; | ||||
|   } | ||||
| @@ -65,8 +73,8 @@ void ESP32TouchComponent::setup() { | ||||
|   esp_err_t err = touch_pad_isr_register(touch_isr_handler, this); | ||||
|   if (err != ESP_OK) { | ||||
|     ESP_LOGE(TAG, "Failed to register touch ISR: %s", esp_err_to_name(err)); | ||||
|     vQueueDelete(this->touch_queue_); | ||||
|     this->touch_queue_ = nullptr; | ||||
|     vRingbufferDelete(this->ring_buffer_handle_); | ||||
|     this->ring_buffer_handle_ = nullptr; | ||||
|     this->mark_failed(); | ||||
|     return; | ||||
|   } | ||||
| @@ -114,33 +122,44 @@ void ESP32TouchComponent::loop() { | ||||
|     this->setup_mode_last_log_print_ = now; | ||||
|   } | ||||
|  | ||||
|   // Process any queued touch events from interrupts | ||||
|   TouchPadEventV1 event; | ||||
|   while (xQueueReceive(this->touch_queue_, &event, 0) == pdTRUE) { | ||||
|     // Find the corresponding sensor | ||||
|     for (auto *child : this->children_) { | ||||
|       if (child->get_touch_pad() == event.pad) { | ||||
|         child->value_ = event.value; | ||||
|   // Process ring buffer entries | ||||
|   size_t item_size; | ||||
|   TouchPadState *pad_states; | ||||
|  | ||||
|         // The interrupt gives us the touch state directly | ||||
|         bool new_state = event.is_touched; | ||||
|   // Receive all available items from ring buffer (non-blocking) | ||||
|   while ((pad_states = (TouchPadState *) xRingbufferReceive(this->ring_buffer_handle_, &item_size, 0)) != nullptr) { | ||||
|     // Calculate number of pads in this snapshot | ||||
|     size_t num_pads = item_size / sizeof(TouchPadState); | ||||
|  | ||||
|         // Track when we last saw this pad as touched | ||||
|         if (new_state) { | ||||
|           this->last_touch_time_[event.pad] = now; | ||||
|     // Process each pad in the snapshot | ||||
|     for (size_t i = 0; i < num_pads; i++) { | ||||
|       const TouchPadState &pad_state = pad_states[i]; | ||||
|  | ||||
|       // Find the corresponding sensor | ||||
|       for (auto *child : this->children_) { | ||||
|         if (child->get_touch_pad() == static_cast<touch_pad_t>(pad_state.pad)) { | ||||
|           child->value_ = pad_state.value; | ||||
|  | ||||
|           // Track when we last saw this pad as touched | ||||
|           if (pad_state.is_touched) { | ||||
|             this->last_touch_time_[pad_state.pad] = now; | ||||
|           } | ||||
|  | ||||
|           // Only publish if state changed | ||||
|           if (pad_state.is_touched != child->last_state_) { | ||||
|             child->last_state_ = pad_state.is_touched; | ||||
|             child->publish_state(pad_state.is_touched); | ||||
|             ESP_LOGV(TAG, "Touch Pad '%s' state: %s (value: %" PRIu32 ", threshold: %" PRIu32 ")", | ||||
|                      child->get_name().c_str(), pad_state.is_touched ? "ON" : "OFF", pad_state.value, | ||||
|                      child->get_threshold()); | ||||
|           } | ||||
|           break; | ||||
|         } | ||||
|  | ||||
|         // Only publish if state changed | ||||
|         if (new_state != child->last_state_) { | ||||
|           child->last_state_ = new_state; | ||||
|           child->publish_state(new_state); | ||||
|           // Original ESP32: ISR only fires when touched, release is detected by timeout | ||||
|           ESP_LOGV(TAG, "Touch Pad '%s' state: ON (value: %" PRIu32 ", threshold: %" PRIu32 ")", | ||||
|                    child->get_name().c_str(), event.value, child->get_threshold()); | ||||
|         } | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // Return item to ring buffer | ||||
|     vRingbufferReturnItem(this->ring_buffer_handle_, (void *) pad_states); | ||||
|   } | ||||
|  | ||||
|   // Check for released pads periodically | ||||
| @@ -184,8 +203,10 @@ void ESP32TouchComponent::loop() { | ||||
| void ESP32TouchComponent::on_shutdown() { | ||||
|   touch_pad_intr_disable(); | ||||
|   touch_pad_isr_deregister(touch_isr_handler, this); | ||||
|   if (this->touch_queue_) { | ||||
|     vQueueDelete(this->touch_queue_); | ||||
|  | ||||
|   if (this->ring_buffer_handle_) { | ||||
|     vRingbufferDelete(this->ring_buffer_handle_); | ||||
|     this->ring_buffer_handle_ = nullptr; | ||||
|   } | ||||
|  | ||||
|   bool is_wakeup_source = false; | ||||
| @@ -218,7 +239,23 @@ void IRAM_ATTR ESP32TouchComponent::touch_isr_handler(void *arg) { | ||||
|  | ||||
|   touch_pad_clear_status(); | ||||
|  | ||||
|   // Process all configured pads to check their current state | ||||
|   // Calculate size needed for this snapshot | ||||
|   size_t num_pads = component->children_.size(); | ||||
|   size_t snapshot_size = num_pads * sizeof(TouchPadState); | ||||
|  | ||||
|   // Allocate space in ring buffer (ISR-safe version) | ||||
|   void *buffer = xRingbufferSendAcquireFromISR(component->ring_buffer_handle_, snapshot_size); | ||||
|   if (buffer == nullptr) { | ||||
|     // Buffer full - track overflow | ||||
|     component->ring_buffer_overflow_count_++; | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   // Fill the buffer with pad states | ||||
|   TouchPadState *pad_states = (TouchPadState *) buffer; | ||||
|  | ||||
|   // Process all configured pads | ||||
|   size_t pad_index = 0; | ||||
|   for (auto *child : component->children_) { | ||||
|     touch_pad_t pad = child->get_touch_pad(); | ||||
|  | ||||
| @@ -238,21 +275,24 @@ void IRAM_ATTR ESP32TouchComponent::touch_isr_handler(void *arg) { | ||||
|       continue; | ||||
|     } | ||||
|  | ||||
|     // Store pad state | ||||
|     pad_states[pad_index].pad = static_cast<uint8_t>(pad); | ||||
|     pad_states[pad_index].value = value; | ||||
|     // For original ESP32, lower value means touched | ||||
|     bool is_touched = value < child->get_threshold(); | ||||
|     pad_states[pad_index].is_touched = value < child->get_threshold(); | ||||
|  | ||||
|     // Always send the current state - the main loop will filter for changes | ||||
|     TouchPadEventV1 event; | ||||
|     event.pad = pad; | ||||
|     event.value = value; | ||||
|     event.is_touched = is_touched; | ||||
|     pad_index++; | ||||
|   } | ||||
|  | ||||
|     // Send to queue from ISR | ||||
|     BaseType_t x_higher_priority_task_woken = pdFALSE; | ||||
|     xQueueSendFromISR(component->touch_queue_, &event, &x_higher_priority_task_woken); | ||||
|     if (x_higher_priority_task_woken) { | ||||
|       portYIELD_FROM_ISR(); | ||||
|     } | ||||
|   // Adjust size if we skipped any pads | ||||
|   size_t actual_size = pad_index * sizeof(TouchPadState); | ||||
|  | ||||
|   // Send the item | ||||
|   BaseType_t higher_priority_task_woken = pdFALSE; | ||||
|   xRingbufferSendCompleteFromISR(component->ring_buffer_handle_, buffer, actual_size, &higher_priority_task_woken); | ||||
|  | ||||
|   if (higher_priority_task_woken) { | ||||
|     portYIELD_FROM_ISR(); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user