diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 3642da2d2a0..665dec37d5a 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -6,6 +6,9 @@ #include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/helpers.h" +#ifdef USE_ESP32 +#include "esphome/core/lock_free_queue.h" +#endif #include "esphome/core/string_ref.h" #include @@ -727,6 +730,7 @@ class WiFiComponent final : public Component { #ifdef USE_ESP32 void wifi_process_event_(IDFWiFiEvent *data); + friend void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); #endif #ifdef USE_RP2040 @@ -871,6 +875,13 @@ class WiFiComponent final : public Component { bool is_high_performance_mode_{false}; #endif +#ifdef USE_ESP32 + // Lock-free SPSC queue for WiFi events from ESP-IDF event handler. + // 17 slots = 16 usable (ring buffer reserves one slot). WiFi events are rare. + // Placed at end of class to avoid padding between smaller fields. + LockFreeQueue event_queue_; +#endif + private: // Stores a pointer to a string literal (static storage duration). // ONLY set from Python-generated code with string literals - never dynamic strings. diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 9bf8192ee1d..dc8d7bab4a9 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -48,11 +48,7 @@ namespace esphome::wifi { static const char *const TAG = "wifi_esp32"; static EventGroupHandle_t s_wifi_event_group; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -// Lock-free SPSC queue replaces FreeRTOS xQueue to avoid kernel spinlock -// overhead on every loop iteration. WiFi events are rare so 64 slots is plenty. -// 65 slots = 64 usable (ring buffer reserves one slot to distinguish full from empty) -static LockFreeQueue s_event_queue; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -static esp_netif_t *s_sta_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static esp_netif_t *s_sta_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) #ifdef USE_WIFI_AP static esp_netif_t *s_ap_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) #endif // USE_WIFI_AP @@ -139,7 +135,7 @@ void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, voi // copy to heap — WiFi events are rare so heap alloc is fine auto *to_send = new IDFWiFiEvent; // NOLINT(cppcoreguidelines-owning-memory) memcpy(to_send, &event, sizeof(IDFWiFiEvent)); - if (!s_event_queue.push(to_send)) { + if (!global_wifi_component->event_queue_.push(to_send)) { delete to_send; // NOLINT(cppcoreguidelines-owning-memory) } } @@ -721,8 +717,13 @@ const char *get_disconnect_reason_str(uint8_t reason) { } void WiFiComponent::wifi_loop_() { + uint16_t dropped = this->event_queue_.get_and_reset_dropped_count(); + if (dropped > 0) { + ESP_LOGW(TAG, "Dropped %u WiFi events due to buffer overflow", dropped); + } + IDFWiFiEvent *data; - while ((data = s_event_queue.pop()) != nullptr) { + while ((data = this->event_queue_.pop()) != nullptr) { wifi_process_event_(data); delete data; // NOLINT(cppcoreguidelines-owning-memory) }