Fix ESP32/LibreTiny without fast select: use FreeRTOS task notifications directly

This commit is contained in:
J. Nick Koston
2026-04-04 11:28:21 -10:00
parent 80ff9f365b
commit aa0e56bfde
2 changed files with 68 additions and 22 deletions
+10 -7
View File
@@ -12,17 +12,21 @@
namespace esphome {
// === ESP32 — IRAM_ATTR entry points (inline impls in wake.h) ===
#ifdef USE_ESP32
// === ESP32 — IRAM_ATTR entry points (fast-select only) ===
#if defined(USE_ESP32) && defined(USE_LWIP_FAST_SELECT)
void IRAM_ATTR wake_loop_isrsafe(int *px_higher_priority_task_woken) {
wake_loop_isrsafe_inline_(px_higher_priority_task_woken);
}
void IRAM_ATTR wake_loop_any_context() { wake_loop_any_context_inline_(); }
#endif
#endif // USE_ESP32
// === FreeRTOS task handle for non-fast-select ESP32/LibreTiny ===
#if (defined(USE_ESP32) || defined(USE_LIBRETINY)) && !defined(USE_LWIP_FAST_SELECT)
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
TaskHandle_t g_main_task_handle = nullptr;
#endif
// === ESP8266 / RP2040 ===
#if defined(USE_ESP8266) || defined(USE_RP2040)
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
volatile bool g_main_loop_woke = false;
@@ -35,12 +39,11 @@ void IRAM_ATTR wake_loop_any_context() { wake_loop_impl_(); }
// === Host (UDP loopback socket) ===
#ifdef USE_HOST
void wake_loop_threadsafe() {
// Wakes up select() in main loop by writing to connected loopback socket
if (App.wake_socket_fd_ >= 0) {
const char dummy = 1;
::send(App.wake_socket_fd_, &dummy, 1, 0);
}
}
#endif // USE_HOST
#endif
} // namespace esphome
+58 -15
View File
@@ -9,6 +9,8 @@
#ifdef USE_LWIP_FAST_SELECT
#include "esphome/core/lwip_fast_select.h"
#endif
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
#ifdef USE_ESP32
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
@@ -27,38 +29,81 @@
namespace esphome {
// === Wake flag for ESP8266/RP2040 ===
// Checked by wakeable_delay() to exit early. Set by wake functions.
#if defined(USE_ESP8266) || defined(USE_RP2040)
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
extern volatile bool g_main_loop_woke;
#endif
// === ESP32/LibreTiny (FreeRTOS) ===
#ifdef USE_LWIP_FAST_SELECT
// === ESP32/LibreTiny FreeRTOS task handle for non-fast-select path ===
#if (defined(USE_ESP32) || defined(USE_LIBRETINY)) && !defined(USE_LWIP_FAST_SELECT)
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
extern TaskHandle_t g_main_task_handle;
#endif
#ifdef USE_ESP32
/// Inline implementation — callers in IRAM get it inlined, keeping it IRAM-safe.
/// IRAM_ATTR entry points in wake.cpp exist for callers that aren't themselves IRAM.
// === ESP32 ===
#if defined(USE_ESP32)
#ifdef USE_LWIP_FAST_SELECT
inline void ESPHOME_ALWAYS_INLINE wake_loop_isrsafe_inline_(int *px_higher_priority_task_woken) {
esphome_lwip_wake_main_loop_from_isr(px_higher_priority_task_woken);
}
/// IRAM_ATTR entry point — defined in wake.cpp.
void wake_loop_isrsafe(int *px_higher_priority_task_woken);
inline void ESPHOME_ALWAYS_INLINE wake_loop_any_context_inline_() { esphome_lwip_wake_main_loop_any_context(); }
/// IRAM_ATTR entry point — defined in wake.cpp.
void wake_loop_any_context();
#else
/// LibreTiny: no working IRAM_ATTR — just use threadsafe version.
inline void wake_loop_any_context() { esphome_lwip_wake_main_loop(); }
#endif
inline void wake_loop_threadsafe() { esphome_lwip_wake_main_loop(); }
#else
inline void wake_loop_any_context() {
if (g_main_task_handle != nullptr)
xTaskNotifyGive(g_main_task_handle);
}
inline void wake_loop_threadsafe() {
if (g_main_task_handle != nullptr)
xTaskNotifyGive(g_main_task_handle);
}
#endif
namespace internal {
inline void wakeable_delay(uint32_t ms) {
#ifndef USE_LWIP_FAST_SELECT
// Cache main task handle on first call
if (g_main_task_handle == nullptr)
g_main_task_handle = xTaskGetCurrentTaskHandle();
#endif
if (ms == 0) {
yield();
return;
}
ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(ms));
}
} // namespace internal
// === LibreTiny ===
#elif defined(USE_LIBRETINY)
#ifdef USE_LWIP_FAST_SELECT
inline void wake_loop_any_context() { esphome_lwip_wake_main_loop(); }
inline void wake_loop_threadsafe() { esphome_lwip_wake_main_loop(); }
#else
inline void wake_loop_any_context() {
if (g_main_task_handle != nullptr)
xTaskNotifyGive(g_main_task_handle);
}
inline void wake_loop_threadsafe() {
if (g_main_task_handle != nullptr)
xTaskNotifyGive(g_main_task_handle);
}
#endif
namespace internal {
inline void wakeable_delay(uint32_t ms) {
#ifndef USE_LWIP_FAST_SELECT
if (g_main_task_handle == nullptr)
g_main_task_handle = xTaskGetCurrentTaskHandle();
#endif
if (ms == 0) {
yield();
return;
@@ -108,7 +153,6 @@ inline void wake_loop_threadsafe() {
namespace internal {
inline void wakeable_delay(uint32_t ms) {
// Function-local statics — safe because this is only called from the main loop.
static volatile bool s_delay_expired = false;
if (ms == 0) {
yield();
@@ -141,10 +185,9 @@ inline void wakeable_delay(uint32_t ms) {
// === Host (UDP loopback socket) ===
#else
/// Host platform: wakes select() via UDP loopback socket. Defined in application.cpp.
/// Defined in wake.cpp.
void wake_loop_threadsafe();
/// Host: no ISR, just use threadsafe version.
inline void wake_loop_any_context() { wake_loop_threadsafe(); }
#endif