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_TRIGGER_ID,
|
||||||
CONF_WIFI,
|
CONF_WIFI,
|
||||||
)
|
)
|
||||||
from esphome.core import HexInt
|
from esphome.core import CORE, HexInt
|
||||||
from esphome.types import ConfigType
|
from esphome.types import ConfigType
|
||||||
|
|
||||||
CODEOWNERS = ["@jesserockz"]
|
CODEOWNERS = ["@jesserockz"]
|
||||||
@@ -151,6 +151,10 @@ async def to_code(config):
|
|||||||
|
|
||||||
cg.add_define("USE_ESPNOW")
|
cg.add_define("USE_ESPNOW")
|
||||||
cg.add_define("USE_ESPNOW_MAX_PAYLOAD_SIZE", config[CONF_MAX_PAYLOAD_SIZE])
|
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):
|
if wifi_channel := config.get(CONF_CHANNEL):
|
||||||
cg.add(var.set_wifi_channel(wifi_channel))
|
cg.add(var.set_wifi_channel(wifi_channel))
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
|
||||||
#include "esphome/core/application.h"
|
|
||||||
#include "esphome/core/defines.h"
|
#include "esphome/core/defines.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/log.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) {
|
if (packet == nullptr) {
|
||||||
// No events available - queue is full or we're out of memory
|
// No events available - queue is full or we're out of memory
|
||||||
global_esp_now->receive_packet_queue_.increment_dropped_count();
|
global_esp_now->receive_packet_queue_.increment_dropped_count();
|
||||||
|
global_esp_now->enable_loop_soon_any_context();
|
||||||
return;
|
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
|
// Push always succeeds: pool is sized to queue capacity (SIZE-1), so if
|
||||||
// allocate() returned non-null, the queue cannot be full.
|
// allocate() returned non-null, the queue cannot be full.
|
||||||
|
|
||||||
// Wake main loop immediately to process ESP-NOW send event
|
// Re-enable and wake the main loop to process the ESP-NOW send event
|
||||||
App.wake_loop_threadsafe();
|
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) {
|
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.
|
// larger frame would overflow packet_.receive.data.
|
||||||
if (size < 0 || size > ESPNOW_MAX_DATA_LEN) {
|
if (size < 0 || size > ESPNOW_MAX_DATA_LEN) {
|
||||||
global_esp_now->receive_packet_queue_.increment_dropped_count();
|
global_esp_now->receive_packet_queue_.increment_dropped_count();
|
||||||
|
global_esp_now->enable_loop_soon_any_context();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +110,7 @@ void on_data_received(const esp_now_recv_info_t *info, const uint8_t *data, int
|
|||||||
if (packet == nullptr) {
|
if (packet == nullptr) {
|
||||||
// No events available - queue is full or we're out of memory
|
// No events available - queue is full or we're out of memory
|
||||||
global_esp_now->receive_packet_queue_.increment_dropped_count();
|
global_esp_now->receive_packet_queue_.increment_dropped_count();
|
||||||
|
global_esp_now->enable_loop_soon_any_context();
|
||||||
return;
|
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
|
// Push always succeeds: pool is sized to queue capacity (SIZE-1), so if
|
||||||
// allocate() returned non-null, the queue cannot be full.
|
// allocate() returned non-null, the queue cannot be full.
|
||||||
|
|
||||||
// Wake main loop immediately to process ESP-NOW receive event
|
// Re-enable and wake the main loop to process the ESP-NOW receive event
|
||||||
App.wake_loop_threadsafe();
|
global_esp_now->enable_loop_soon_any_context();
|
||||||
}
|
}
|
||||||
|
|
||||||
ESPNowComponent::ESPNowComponent() { global_esp_now = this; }
|
ESPNowComponent::ESPNowComponent() { global_esp_now = this; }
|
||||||
@@ -156,6 +158,11 @@ bool ESPNowComponent::is_wifi_enabled() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ESPNowComponent::setup() {
|
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_) {
|
if (this->enable_on_boot_) {
|
||||||
this->enable_();
|
this->enable_();
|
||||||
} else {
|
} 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() {
|
void ESPNowComponent::enable() {
|
||||||
if (this->state_ == ESPNOW_STATE_ENABLED)
|
if (this->state_ == ESPNOW_STATE_ENABLED)
|
||||||
return;
|
return;
|
||||||
@@ -254,15 +274,6 @@ void ESPNowComponent::apply_wifi_channel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ESPNowComponent::loop() {
|
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
|
// Process received packets
|
||||||
ESPNowPacket *packet = this->receive_packet_queue_.pop();
|
ESPNowPacket *packet = this->receive_packet_queue_.pop();
|
||||||
while (packet != nullptr) {
|
while (packet != nullptr) {
|
||||||
@@ -348,6 +359,15 @@ void ESPNowComponent::loop() {
|
|||||||
if (send_dropped > 0) {
|
if (send_dropped > 0) {
|
||||||
ESP_LOGW(TAG, "Dropped %u send packets (queue full)", send_dropped);
|
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() {
|
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);
|
packet->load_data(peer_address, payload, size, callback);
|
||||||
// Push the packet to the send queue
|
// Push the packet to the send queue
|
||||||
this->send_packet_queue_.push(packet);
|
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;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "esphome/core/automation.h"
|
#include "esphome/core/automation.h"
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
@@ -9,6 +10,10 @@
|
|||||||
#include "esphome/core/lock_free_queue.h"
|
#include "esphome/core/lock_free_queue.h"
|
||||||
#include "espnow_packet.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_idf_version.h>
|
||||||
|
|
||||||
#include <esp_mac.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;
|
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 {
|
class ESPNowComponent final : public Component {
|
||||||
|
#endif
|
||||||
public:
|
public:
|
||||||
ESPNowComponent();
|
ESPNowComponent();
|
||||||
void setup() override;
|
void setup() override;
|
||||||
@@ -114,6 +123,11 @@ class ESPNowComponent final : public Component {
|
|||||||
|
|
||||||
void set_auto_add_peer(bool value) { this->auto_add_peer_ = value; }
|
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 enable();
|
||||||
void disable();
|
void disable();
|
||||||
bool is_disabled() const { return this->state_ == ESPNOW_STATE_DISABLED; };
|
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