1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-12 16:22:22 +01:00

address review comments

This commit is contained in:
J. Nick Koston
2025-06-14 21:34:21 -05:00
parent 2dc85f5a42
commit e6dc10a440
2 changed files with 26 additions and 17 deletions

View File

@@ -19,6 +19,11 @@ namespace esp32_touch {
// - ESP32 v1 (original): Touch detected when value < threshold (capacitance increase causes value decrease) // - ESP32 v1 (original): Touch detected when value < threshold (capacitance increase causes value decrease)
// - ESP32-S2/S3 v2: Touch detected when value > threshold (capacitance increase causes value increase) // - ESP32-S2/S3 v2: Touch detected when value > threshold (capacitance increase causes value increase)
// This inversion is due to different hardware implementations between chip generations. // This inversion is due to different hardware implementations between chip generations.
//
// INTERRUPT BEHAVIOR:
// - ESP32 v1: Interrupts fire when ANY pad is touched and continue while touched.
// Releases are detected by timeout since hardware doesn't generate release interrupts.
// - ESP32-S2/S3 v2: Interrupts can be configured per-pad with both touch and release events.
static const uint32_t SETUP_MODE_LOG_INTERVAL_MS = 250; static const uint32_t SETUP_MODE_LOG_INTERVAL_MS = 250;
@@ -105,11 +110,12 @@ class ESP32TouchComponent : public Component {
protected: protected:
// Design note: last_touch_time_ does not require synchronization primitives because: // Design note: last_touch_time_ does not require synchronization primitives because:
// 1. ESP32 guarantees atomic 32-bit aligned reads/writes // 1. ESP32 guarantees atomic 32-bit aligned reads/writes
// 2. ISR only writes timestamps, main loop only reads (except sentinel value 1) // 2. ISR only writes timestamps, main loop only reads
// 3. Timing tolerance allows for occasional stale reads (50ms check interval) // 3. Timing tolerance allows for occasional stale reads (50ms check interval)
// 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_timeout_ms_{1500};
uint32_t release_check_interval_ms_{50}; uint32_t release_check_interval_ms_{50};
uint32_t iir_filter_{0}; uint32_t iir_filter_{0};

View File

@@ -147,29 +147,25 @@ void ESP32TouchComponent::loop() {
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();
uint32_t last_time = this->last_touch_time_[pad];
// Design note: Sentinel value pattern explanation // Handle initial state publication after startup
// - 0: Never touched since boot (waiting for initial timeout) if (!this->initial_state_published_[pad]) {
// - 1: Initial OFF state has been published (prevents repeated publishes) // Check if enough time has passed since startup
// - >1: Actual timestamp of last touch event if (now > this->release_timeout_ms_) {
// This avoids needing a separate boolean flag for initial state tracking
// If we've never seen this pad touched (last_time == 0) and enough time has passed
// since startup, publish OFF state and mark as published with value 1
if (last_time == 0 && now > this->release_timeout_ms_) {
child->publish_initial_state(false); child->publish_initial_state(false);
this->last_touch_time_[pad] = 1; // Mark as "initial state published" this->initial_state_published_[pad] = true;
ESP_LOGV(TAG, "Touch Pad '%s' state: OFF (initial)", child->get_name().c_str()); ESP_LOGV(TAG, "Touch Pad '%s' state: OFF (initial)", child->get_name().c_str());
} else if (child->last_state_ && last_time > 1) { // last_time > 1 means it's a real timestamp }
uint32_t time_diff = now - last_time; } else if (child->last_state_) {
// Pad is currently in touched state - check for release timeout
// Using subtraction handles 32-bit rollover correctly
uint32_t time_diff = now - this->last_touch_time_[pad];
// Check if we haven't seen this pad recently // Check if we haven't seen this pad recently
if (time_diff > this->release_timeout_ms_) { if (time_diff > this->release_timeout_ms_) {
// Haven't seen this pad recently, assume it's released // Haven't seen this pad recently, assume it's released
child->last_state_ = false; child->last_state_ = false;
child->publish_state(false); child->publish_state(false);
this->last_touch_time_[pad] = 1; // Reset to "initial published" state
ESP_LOGV(TAG, "Touch Pad '%s' state: OFF (timeout)", child->get_name().c_str()); ESP_LOGV(TAG, "Touch Pad '%s' state: OFF (timeout)", child->get_name().c_str());
} }
} }
@@ -195,6 +191,13 @@ void IRAM_ATTR ESP32TouchComponent::touch_isr_handler(void *arg) {
touch_pad_clear_status(); touch_pad_clear_status();
// INTERRUPT BEHAVIOR: On ESP32 v1 hardware, the interrupt fires when ANY configured
// touch pad detects a touch (value goes below threshold). The hardware does NOT
// generate interrupts on release - only on touch events.
// The interrupt will continue to fire periodically (based on sleep_cycle) as long
// as any pad remains touched. This allows us to detect both new touches and
// continued touches, but releases must be detected by timeout in the main loop.
// Process all configured pads to check their current state // Process all configured pads to check their current state
// Note: ESP32 v1 doesn't tell us which specific pad triggered the interrupt, // Note: ESP32 v1 doesn't tell us which specific pad triggered the interrupt,
// so we must scan all configured pads to find which ones were touched // so we must scan all configured pads to find which ones were touched