mirror of
https://github.com/esphome/esphome.git
synced 2026-08-22 22:26:21 +00:00
[espnow] Stop polling the Wi-Fi driver every loop and disable loop when idle (#18027)
This commit is contained in:
@@ -13,7 +13,7 @@ from esphome.const import (
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_WIFI,
|
||||
)
|
||||
from esphome.core import HexInt
|
||||
from esphome.core import CORE, HexInt
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
@@ -151,6 +151,10 @@ async def to_code(config):
|
||||
|
||||
cg.add_define("USE_ESPNOW")
|
||||
cg.add_define("USE_ESPNOW_MAX_PAYLOAD_SIZE", config[CONF_MAX_PAYLOAD_SIZE])
|
||||
|
||||
if CONF_WIFI in CORE.config:
|
||||
# Track the Wi-Fi channel via connect events instead of polling every loop
|
||||
wifi.request_wifi_connect_state_listener()
|
||||
if wifi_channel := config.get(CONF_CHANNEL):
|
||||
cg.add(var.set_wifi_channel(wifi_channel))
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
@@ -75,6 +74,7 @@ void on_send_report(const uint8_t *mac_addr, esp_now_send_status_t status)
|
||||
if (packet == nullptr) {
|
||||
// No events available - queue is full or we're out of memory
|
||||
global_esp_now->receive_packet_queue_.increment_dropped_count();
|
||||
global_esp_now->enable_loop_soon_any_context();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -90,8 +90,8 @@ void on_send_report(const uint8_t *mac_addr, esp_now_send_status_t status)
|
||||
// Push always succeeds: pool is sized to queue capacity (SIZE-1), so if
|
||||
// allocate() returned non-null, the queue cannot be full.
|
||||
|
||||
// Wake main loop immediately to process ESP-NOW send event
|
||||
App.wake_loop_threadsafe();
|
||||
// Re-enable and wake the main loop to process the ESP-NOW send event
|
||||
global_esp_now->enable_loop_soon_any_context();
|
||||
}
|
||||
|
||||
void on_data_received(const esp_now_recv_info_t *info, const uint8_t *data, int size) {
|
||||
@@ -101,6 +101,7 @@ void on_data_received(const esp_now_recv_info_t *info, const uint8_t *data, int
|
||||
// larger frame would overflow packet_.receive.data.
|
||||
if (size < 0 || size > ESPNOW_MAX_DATA_LEN) {
|
||||
global_esp_now->receive_packet_queue_.increment_dropped_count();
|
||||
global_esp_now->enable_loop_soon_any_context();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -109,6 +110,7 @@ void on_data_received(const esp_now_recv_info_t *info, const uint8_t *data, int
|
||||
if (packet == nullptr) {
|
||||
// No events available - queue is full or we're out of memory
|
||||
global_esp_now->receive_packet_queue_.increment_dropped_count();
|
||||
global_esp_now->enable_loop_soon_any_context();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -120,8 +122,8 @@ void on_data_received(const esp_now_recv_info_t *info, const uint8_t *data, int
|
||||
// Push always succeeds: pool is sized to queue capacity (SIZE-1), so if
|
||||
// allocate() returned non-null, the queue cannot be full.
|
||||
|
||||
// Wake main loop immediately to process ESP-NOW receive event
|
||||
App.wake_loop_threadsafe();
|
||||
// Re-enable and wake the main loop to process the ESP-NOW receive event
|
||||
global_esp_now->enable_loop_soon_any_context();
|
||||
}
|
||||
|
||||
ESPNowComponent::ESPNowComponent() { global_esp_now = this; }
|
||||
@@ -156,6 +158,11 @@ bool ESPNowComponent::is_wifi_enabled() {
|
||||
}
|
||||
|
||||
void ESPNowComponent::setup() {
|
||||
#if defined(USE_WIFI) && defined(USE_WIFI_CONNECT_STATE_LISTENERS)
|
||||
if (wifi::global_wifi_component != nullptr) {
|
||||
wifi::global_wifi_component->add_connect_state_listener(this);
|
||||
}
|
||||
#endif
|
||||
if (this->enable_on_boot_) {
|
||||
this->enable_();
|
||||
} else {
|
||||
@@ -163,6 +170,19 @@ void ESPNowComponent::setup() {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(USE_WIFI) && defined(USE_WIFI_CONNECT_STATE_LISTENERS)
|
||||
void ESPNowComponent::on_wifi_connect_state(StringRef ssid, std::span<const uint8_t, 6> bssid) {
|
||||
if (ssid.empty()) {
|
||||
return; // Disconnected; the channel is only meaningful while associated
|
||||
}
|
||||
uint8_t old_channel = this->wifi_channel_;
|
||||
this->get_wifi_channel();
|
||||
if (this->wifi_channel_ != old_channel) {
|
||||
ESP_LOGI(TAG, "WiFi channel changed from %d to %d", old_channel, this->wifi_channel_);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void ESPNowComponent::enable() {
|
||||
if (this->state_ == ESPNOW_STATE_ENABLED)
|
||||
return;
|
||||
@@ -254,15 +274,6 @@ void ESPNowComponent::apply_wifi_channel() {
|
||||
}
|
||||
|
||||
void ESPNowComponent::loop() {
|
||||
#ifdef USE_WIFI
|
||||
if (wifi::global_wifi_component != nullptr && wifi::global_wifi_component->is_connected()) {
|
||||
int32_t new_channel = wifi::global_wifi_component->get_wifi_channel();
|
||||
if (new_channel != this->wifi_channel_) {
|
||||
ESP_LOGI(TAG, "Wifi Channel is changed from %d to %" PRId32 ".", this->wifi_channel_, new_channel);
|
||||
this->wifi_channel_ = new_channel;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// Process received packets
|
||||
ESPNowPacket *packet = this->receive_packet_queue_.pop();
|
||||
while (packet != nullptr) {
|
||||
@@ -348,6 +359,15 @@ void ESPNowComponent::loop() {
|
||||
if (send_dropped > 0) {
|
||||
ESP_LOGW(TAG, "Dropped %u send packets (queue full)", send_dropped);
|
||||
}
|
||||
|
||||
// Nothing left to do; sleep until a callback or send() re-enables the loop.
|
||||
// A packet in flight (current_send_packet_) needs no loop time even when more
|
||||
// packets are queued behind it: the send callback re-enables the loop when
|
||||
// the result arrives, and the SENT event handler above starts the next send.
|
||||
if (this->receive_packet_queue_.empty() &&
|
||||
(this->current_send_packet_ != nullptr || this->send_packet_queue_.empty())) {
|
||||
this->disable_loop();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ESPNowComponent::get_wifi_channel() {
|
||||
@@ -390,6 +410,9 @@ esp_err_t ESPNowComponent::send(const uint8_t *peer_address, const uint8_t *payl
|
||||
packet->load_data(peer_address, payload, size, callback);
|
||||
// Push the packet to the send queue
|
||||
this->send_packet_queue_.push(packet);
|
||||
// Loop may be disabled while idle; re-enable it to send the packet
|
||||
// (any-context variant so callers off the main loop are safe too)
|
||||
this->enable_loop_soon_any_context();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
@@ -9,6 +10,10 @@
|
||||
#include "esphome/core/lock_free_queue.h"
|
||||
#include "espnow_packet.h"
|
||||
|
||||
#if defined(USE_WIFI) && defined(USE_WIFI_CONNECT_STATE_LISTENERS)
|
||||
#include "esphome/components/wifi/wifi_component.h"
|
||||
#endif
|
||||
|
||||
#include <esp_idf_version.h>
|
||||
|
||||
#include <esp_mac.h>
|
||||
@@ -88,7 +93,11 @@ class ESPNowBroadcastHandler {
|
||||
virtual bool on_broadcast(const ESPNowRecvInfo &info, const uint8_t *data, uint16_t size) = 0;
|
||||
};
|
||||
|
||||
#if defined(USE_WIFI) && defined(USE_WIFI_CONNECT_STATE_LISTENERS)
|
||||
class ESPNowComponent final : public Component, public wifi::WiFiConnectStateListener {
|
||||
#else
|
||||
class ESPNowComponent final : public Component {
|
||||
#endif
|
||||
public:
|
||||
ESPNowComponent();
|
||||
void setup() override;
|
||||
@@ -114,6 +123,11 @@ class ESPNowComponent final : public Component {
|
||||
|
||||
void set_auto_add_peer(bool value) { this->auto_add_peer_ = value; }
|
||||
|
||||
#if defined(USE_WIFI) && defined(USE_WIFI_CONNECT_STATE_LISTENERS)
|
||||
// WiFiConnectStateListener interface: refresh the cached channel after each (re)connect
|
||||
void on_wifi_connect_state(StringRef ssid, std::span<const uint8_t, 6> bssid) override;
|
||||
#endif
|
||||
|
||||
void enable();
|
||||
void disable();
|
||||
bool is_disabled() const { return this->state_ == ESPNOW_STATE_DISABLED; };
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
wifi:
|
||||
ssid: MySSID
|
||||
password: password1
|
||||
|
||||
espnow:
|
||||
id: espnow_component
|
||||
auto_add_peer: true
|
||||
peers:
|
||||
- 11:22:33:44:55:66
|
||||
@@ -0,0 +1,2 @@
|
||||
packages:
|
||||
espnow: !include common-wifi.yaml
|
||||
Reference in New Issue
Block a user