mirror of
https://github.com/esphome/esphome.git
synced 2026-07-10 17:05:36 +00:00
Merge remote-tracking branch 'upstream/ota-disable-loop-when-idle' into integration
# Conflicts: # esphome/core/application.cpp
This commit is contained in:
@@ -43,3 +43,11 @@ wave_4_26.extend(
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
ssd1677.extend(
|
||||
"waveshare-3.97in",
|
||||
width=800,
|
||||
height=480,
|
||||
mirror_x=True,
|
||||
)
|
||||
|
||||
@@ -155,6 +155,8 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_auth_password(config[CONF_PASSWORD]))
|
||||
cg.add_define("USE_OTA_PASSWORD")
|
||||
cg.add_define("USE_OTA_VERSION", config[CONF_VERSION])
|
||||
# Build flag so lwip_fast_select.c (a .c file that can't include defines.h) sees it.
|
||||
cg.add_build_flag("-DUSE_OTA_PLATFORM_ESPHOME")
|
||||
|
||||
await cg.register_component(var, config)
|
||||
await ota_to_code(var, config)
|
||||
|
||||
@@ -31,6 +31,17 @@ static constexpr size_t OTA_BUFFER_SIZE = 1024; // buffer size
|
||||
static constexpr uint32_t OTA_SOCKET_TIMEOUT_HANDSHAKE = 20000; // milliseconds for initial handshake
|
||||
static constexpr uint32_t OTA_SOCKET_TIMEOUT_DATA = 90000; // milliseconds for data transfer
|
||||
|
||||
// Single-instance pointer — multi-port configs are rejected in final_validate.
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static ESPHomeOTAComponent *global_esphome_ota_component = nullptr;
|
||||
|
||||
// Called from any context (LwIP TCP/IP task, RP2040 user-IRQ).
|
||||
extern "C" void esphome_wake_ota_component_any_context() {
|
||||
if (global_esphome_ota_component != nullptr) {
|
||||
global_esphome_ota_component->enable_loop_soon_any_context();
|
||||
}
|
||||
}
|
||||
|
||||
void ESPHomeOTAComponent::setup() {
|
||||
this->server_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0).release(); // monitored for incoming connections
|
||||
if (this->server_ == nullptr) {
|
||||
@@ -70,7 +81,7 @@ void ESPHomeOTAComponent::setup() {
|
||||
}
|
||||
|
||||
// loop() self-disables on its first idle tick; no explicit disable_loop() needed here.
|
||||
App.set_ota_wake_component(this);
|
||||
global_esphome_ota_component = this;
|
||||
#ifdef USE_LWIP_FAST_SELECT
|
||||
// Filter fast-select wakes to this listener only. If the sock lookup returns nullptr,
|
||||
// no wakes fire and loop() falls back to the self-disable safety net.
|
||||
|
||||
@@ -103,9 +103,6 @@ BASE_OTA_SCHEMA = cv.Schema(
|
||||
@coroutine_with_priority(CoroPriority.OTA_UPDATES)
|
||||
async def to_code(config):
|
||||
cg.add_define("USE_OTA")
|
||||
# Separate -D flag so .c files can see it — .c can't include defines.h (macros.h
|
||||
# pulls in Arduino.h). Different name avoids USE_OTA redefinition warnings.
|
||||
cg.add_build_flag("-DESPHOME_USE_OTA")
|
||||
CORE.add_job(final_step)
|
||||
|
||||
if CORE.is_rp2040 and CORE.using_arduino:
|
||||
|
||||
@@ -10,8 +10,9 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/wake.h"
|
||||
#include "esphome/core/log.h"
|
||||
#ifdef USE_OTA
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
#ifdef USE_OTA_PLATFORM_ESPHOME
|
||||
extern "C" void esphome_wake_ota_component_any_context();
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
@@ -857,10 +858,9 @@ err_t LWIPRawListenImpl::accept_fn_(struct tcp_pcb *newpcb, err_t err) {
|
||||
tcp_err(newpcb, LWIPRawListenImpl::s_queued_err_fn);
|
||||
tcp_recv(newpcb, LWIPRawListenImpl::s_queued_recv_fn);
|
||||
LWIP_LOG("Accepted connection, queue size: %d", this->accepted_socket_count_);
|
||||
#ifdef USE_OTA
|
||||
// Mark OTA pending-enable before the wake below — flags must be visible before the
|
||||
// main task runs. accept_fn_ is per-pcb, so filtering is implicit here.
|
||||
esphome::App.wake_ota_component_any_context();
|
||||
#ifdef USE_OTA_PLATFORM_ESPHOME
|
||||
// Must run before wake_loop_any_context() so flags are visible when the main task wakes.
|
||||
esphome_wake_ota_component_any_context();
|
||||
#endif
|
||||
// Wake the main loop immediately so it can accept the new connection.
|
||||
esphome::wake_loop_any_context();
|
||||
|
||||
@@ -463,12 +463,32 @@ void Application::enable_pending_loops_() {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(USE_OTA) && defined(USE_LWIP_FAST_SELECT)
|
||||
// C trampoline called from lwip_fast_select.c when the listener filter matches.
|
||||
extern "C" void esphome_wake_ota_component_any_context() { App.wake_ota_component_any_context(); }
|
||||
#endif
|
||||
#ifdef USE_LWIP_FAST_SELECT
|
||||
bool Application::register_socket(struct lwip_sock *sock) {
|
||||
// It modifies monitored_sockets_ without locking — must only be called from the main loop.
|
||||
if (sock == nullptr)
|
||||
return false;
|
||||
esphome_lwip_hook_socket(sock);
|
||||
this->monitored_sockets_.push_back(sock);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef USE_HOST
|
||||
void Application::unregister_socket(struct lwip_sock *sock) {
|
||||
// It modifies monitored_sockets_ without locking — must only be called from the main loop.
|
||||
for (size_t i = 0; i < this->monitored_sockets_.size(); i++) {
|
||||
if (this->monitored_sockets_[i] != sock)
|
||||
continue;
|
||||
|
||||
// Swap with last element and pop - O(1) removal since order doesn't matter.
|
||||
// No need to unhook the netconn callback — all LwIP sockets share the same
|
||||
// static event_callback, and the socket will be closed by the caller.
|
||||
if (i < this->monitored_sockets_.size() - 1)
|
||||
this->monitored_sockets_[i] = this->monitored_sockets_.back();
|
||||
this->monitored_sockets_.pop_back();
|
||||
return;
|
||||
}
|
||||
}
|
||||
#elif defined(USE_HOST)
|
||||
bool Application::register_socket_fd(int fd) {
|
||||
// WARNING: This function is NOT thread-safe and must only be called from the main loop
|
||||
// It modifies socket_fds_ and related variables without locking
|
||||
@@ -548,12 +568,6 @@ void Application::yield_with_select_(uint32_t delay_ms) {
|
||||
// ret > 0: socket(s) have data ready - normal and expected
|
||||
// ret == 0: timeout occurred - normal and expected
|
||||
if (ret >= 0) [[likely]] {
|
||||
#ifdef USE_OTA
|
||||
// No-op today — host has no esphome OTA platform, so ota_wake_component_ is null.
|
||||
if (ret > 0) {
|
||||
this->wake_ota_component_any_context();
|
||||
}
|
||||
#endif
|
||||
// Yield if zero timeout since select(0) only polls without yielding
|
||||
if (delay_ms == 0) [[unlikely]] {
|
||||
yield();
|
||||
|
||||
@@ -314,18 +314,6 @@ class Application {
|
||||
/// Wake from any context (ISR, thread, callback).
|
||||
static void IRAM_ATTR wake_loop_any_context() { esphome::wake_loop_any_context(); }
|
||||
|
||||
#ifdef USE_OTA
|
||||
void set_ota_wake_component(Component *component) { this->ota_wake_component_ = component; }
|
||||
// Marks OTA pending-enable from socket wake paths. Does NOT wake the main task —
|
||||
// every caller already does. Callable from LwIP TCP/IP task and user-IRQ context.
|
||||
void wake_ota_component_any_context() {
|
||||
if (this->ota_wake_component_ != nullptr) {
|
||||
this->ota_wake_component_->pending_enable_loop_ = true;
|
||||
this->has_pending_enable_loop_requests_ = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
friend Component;
|
||||
#ifdef USE_HOST
|
||||
@@ -401,9 +389,6 @@ class Application {
|
||||
|
||||
// Pointer-sized members first
|
||||
Component *current_component_{nullptr};
|
||||
#ifdef USE_OTA
|
||||
Component *ota_wake_component_{nullptr};
|
||||
#endif
|
||||
|
||||
// std::vector (3 pointers each: begin, end, capacity)
|
||||
// Partitioned vector design for looping components
|
||||
|
||||
@@ -157,11 +157,9 @@ _Static_assert(offsetof(struct lwip_sock, rcvevent) == ESPHOME_LWIP_SOCK_RCVEVEN
|
||||
// Saved original event_callback pointer — written once in first hook_socket(), read from TCP/IP task.
|
||||
static netconn_callback s_original_callback = NULL;
|
||||
|
||||
#ifdef ESPHOME_USE_OTA
|
||||
// ESPHOME_USE_OTA (not USE_OTA): this .c file can't include defines.h — macros.h →
|
||||
// Arduino.h would break the C compile. ota/__init__.py emits -DESPHOME_USE_OTA.
|
||||
#ifdef USE_OTA_PLATFORM_ESPHOME
|
||||
static struct netconn *s_ota_listener_conn = NULL;
|
||||
extern void esphome_wake_ota_component_any_context(void); // trampoline in application.cpp
|
||||
extern void esphome_wake_ota_component_any_context(void);
|
||||
|
||||
void esphome_fast_select_set_ota_listener_sock(struct lwip_sock *sock) {
|
||||
s_ota_listener_conn = (sock != NULL) ? sock->conn : NULL;
|
||||
@@ -184,7 +182,7 @@ static void esphome_socket_event_callback(struct netconn *conn, enum netconn_evt
|
||||
// (rcvevent++ with a NULL pbuf or error in recvmbox), so error conditions
|
||||
// already wake the main loop through the RCVPLUS path.
|
||||
if (evt == NETCONN_EVT_RCVPLUS) {
|
||||
#ifdef ESPHOME_USE_OTA
|
||||
#ifdef USE_OTA_PLATFORM_ESPHOME
|
||||
// Mark OTA pending-enable only for events on its listen socket. MUST happen
|
||||
// before xTaskNotifyGive so the flags are visible when the main task wakes.
|
||||
if (conn == s_ota_listener_conn) {
|
||||
|
||||
Reference in New Issue
Block a user