mirror of
https://github.com/esphome/esphome.git
synced 2025-10-15 23:17:54 +02:00
[esp32_ble] Replace handler vectors with StaticVector for 560B-2KB memory savings (#11200)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
7c02f2f10a
commit
5bb69a968c
@ -1,4 +1,5 @@
|
||||
from collections.abc import Callable, MutableMapping
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
import logging
|
||||
import re
|
||||
@ -16,7 +17,7 @@ from esphome.const import (
|
||||
CONF_NAME,
|
||||
CONF_NAME_ADD_MAC_SUFFIX,
|
||||
)
|
||||
from esphome.core import CORE, TimePeriod
|
||||
from esphome.core import CORE, CoroPriority, TimePeriod, coroutine_with_priority
|
||||
import esphome.final_validate as fv
|
||||
|
||||
DEPENDENCIES = ["esp32"]
|
||||
@ -111,6 +112,58 @@ class BTLoggers(Enum):
|
||||
_required_loggers: set[BTLoggers] = set()
|
||||
|
||||
|
||||
# Dataclass for handler registration counts
|
||||
@dataclass
|
||||
class HandlerCounts:
|
||||
gap_event: int = 0
|
||||
gap_scan_event: int = 0
|
||||
gattc_event: int = 0
|
||||
gatts_event: int = 0
|
||||
ble_status_event: int = 0
|
||||
|
||||
|
||||
# Track handler registration counts for StaticVector sizing
|
||||
_handler_counts = HandlerCounts()
|
||||
|
||||
|
||||
def register_gap_event_handler(parent_var: cg.MockObj, handler_var: cg.MockObj) -> None:
|
||||
"""Register a GAP event handler and track the count."""
|
||||
_handler_counts.gap_event += 1
|
||||
cg.add(parent_var.register_gap_event_handler(handler_var))
|
||||
|
||||
|
||||
def register_gap_scan_event_handler(
|
||||
parent_var: cg.MockObj, handler_var: cg.MockObj
|
||||
) -> None:
|
||||
"""Register a GAP scan event handler and track the count."""
|
||||
_handler_counts.gap_scan_event += 1
|
||||
cg.add(parent_var.register_gap_scan_event_handler(handler_var))
|
||||
|
||||
|
||||
def register_gattc_event_handler(
|
||||
parent_var: cg.MockObj, handler_var: cg.MockObj
|
||||
) -> None:
|
||||
"""Register a GATTc event handler and track the count."""
|
||||
_handler_counts.gattc_event += 1
|
||||
cg.add(parent_var.register_gattc_event_handler(handler_var))
|
||||
|
||||
|
||||
def register_gatts_event_handler(
|
||||
parent_var: cg.MockObj, handler_var: cg.MockObj
|
||||
) -> None:
|
||||
"""Register a GATTs event handler and track the count."""
|
||||
_handler_counts.gatts_event += 1
|
||||
cg.add(parent_var.register_gatts_event_handler(handler_var))
|
||||
|
||||
|
||||
def register_ble_status_event_handler(
|
||||
parent_var: cg.MockObj, handler_var: cg.MockObj
|
||||
) -> None:
|
||||
"""Register a BLE status event handler and track the count."""
|
||||
_handler_counts.ble_status_event += 1
|
||||
cg.add(parent_var.register_ble_status_event_handler(handler_var))
|
||||
|
||||
|
||||
def register_bt_logger(*loggers: BTLoggers) -> None:
|
||||
"""Register Bluetooth logger categories that a component needs.
|
||||
|
||||
@ -374,6 +427,36 @@ def final_validation(config):
|
||||
FINAL_VALIDATE_SCHEMA = final_validation
|
||||
|
||||
|
||||
# This needs to be run as a job with CoroPriority.FINAL priority so that all components have
|
||||
# a chance to register their handlers before the counts are added to defines.
|
||||
@coroutine_with_priority(CoroPriority.FINAL)
|
||||
async def _add_ble_handler_defines():
|
||||
# Add defines for StaticVector sizing based on handler registration counts
|
||||
# Only define if count > 0 to avoid allocating unnecessary memory
|
||||
if _handler_counts.gap_event > 0:
|
||||
cg.add_define(
|
||||
"ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT", _handler_counts.gap_event
|
||||
)
|
||||
if _handler_counts.gap_scan_event > 0:
|
||||
cg.add_define(
|
||||
"ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT",
|
||||
_handler_counts.gap_scan_event,
|
||||
)
|
||||
if _handler_counts.gattc_event > 0:
|
||||
cg.add_define(
|
||||
"ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT", _handler_counts.gattc_event
|
||||
)
|
||||
if _handler_counts.gatts_event > 0:
|
||||
cg.add_define(
|
||||
"ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT", _handler_counts.gatts_event
|
||||
)
|
||||
if _handler_counts.ble_status_event > 0:
|
||||
cg.add_define(
|
||||
"ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT",
|
||||
_handler_counts.ble_status_event,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
cg.add(var.set_enable_on_boot(config[CONF_ENABLE_ON_BOOT]))
|
||||
@ -428,6 +511,9 @@ async def to_code(config):
|
||||
cg.add_define("USE_ESP32_BLE_ADVERTISING")
|
||||
cg.add_define("USE_ESP32_BLE_UUID")
|
||||
|
||||
# Schedule the handler defines to be added after all components register
|
||||
CORE.add_job(_add_ble_handler_defines)
|
||||
|
||||
|
||||
@automation.register_condition("ble.enabled", BLEEnabledCondition, cv.Schema({}))
|
||||
async def ble_enabled_to_code(config, condition_id, template_arg, args):
|
||||
|
@ -181,31 +181,27 @@ bool ESP32BLE::ble_setup_() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this->gap_event_handlers_.empty()) {
|
||||
err = esp_ble_gap_register_callback(ESP32BLE::gap_event_handler);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ble_gap_register_callback failed: %d", err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_ESP32_BLE_SERVER
|
||||
if (!this->gatts_event_handlers_.empty()) {
|
||||
err = esp_ble_gatts_register_callback(ESP32BLE::gatts_event_handler);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ble_gatts_register_callback failed: %d", err);
|
||||
return false;
|
||||
}
|
||||
#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT
|
||||
err = esp_ble_gap_register_callback(ESP32BLE::gap_event_handler);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ble_gap_register_callback failed: %d", err);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP32_BLE_CLIENT
|
||||
if (!this->gattc_event_handlers_.empty()) {
|
||||
err = esp_ble_gattc_register_callback(ESP32BLE::gattc_event_handler);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ble_gattc_register_callback failed: %d", err);
|
||||
return false;
|
||||
}
|
||||
#if defined(USE_ESP32_BLE_SERVER) && defined(ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT)
|
||||
err = esp_ble_gatts_register_callback(ESP32BLE::gatts_event_handler);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ble_gatts_register_callback failed: %d", err);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(USE_ESP32_BLE_CLIENT) && defined(ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT)
|
||||
err = esp_ble_gattc_register_callback(ESP32BLE::gattc_event_handler);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ble_gattc_register_callback failed: %d", err);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -302,9 +298,11 @@ void ESP32BLE::loop() {
|
||||
case BLE_COMPONENT_STATE_DISABLE: {
|
||||
ESP_LOGD(TAG, "Disabling");
|
||||
|
||||
#ifdef ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT
|
||||
for (auto *ble_event_handler : this->ble_status_event_handlers_) {
|
||||
ble_event_handler->ble_before_disabled_event_handler();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!ble_dismantle_()) {
|
||||
ESP_LOGE(TAG, "Could not be dismantled");
|
||||
@ -334,7 +332,7 @@ void ESP32BLE::loop() {
|
||||
BLEEvent *ble_event = this->ble_events_.pop();
|
||||
while (ble_event != nullptr) {
|
||||
switch (ble_event->type_) {
|
||||
#ifdef USE_ESP32_BLE_SERVER
|
||||
#if defined(USE_ESP32_BLE_SERVER) && defined(ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT)
|
||||
case BLEEvent::GATTS: {
|
||||
esp_gatts_cb_event_t event = ble_event->event_.gatts.gatts_event;
|
||||
esp_gatt_if_t gatts_if = ble_event->event_.gatts.gatts_if;
|
||||
@ -346,7 +344,7 @@ void ESP32BLE::loop() {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_ESP32_BLE_CLIENT
|
||||
#if defined(USE_ESP32_BLE_CLIENT) && defined(ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT)
|
||||
case BLEEvent::GATTC: {
|
||||
esp_gattc_cb_event_t event = ble_event->event_.gattc.gattc_event;
|
||||
esp_gatt_if_t gattc_if = ble_event->event_.gattc.gattc_if;
|
||||
@ -362,10 +360,12 @@ void ESP32BLE::loop() {
|
||||
esp_gap_ble_cb_event_t gap_event = ble_event->event_.gap.gap_event;
|
||||
switch (gap_event) {
|
||||
case ESP_GAP_BLE_SCAN_RESULT_EVT:
|
||||
#ifdef ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT
|
||||
// Use the new scan event handler - no memcpy!
|
||||
for (auto *scan_handler : this->gap_scan_event_handlers_) {
|
||||
scan_handler->gap_scan_event_handler(ble_event->scan_result());
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
// Scan complete events
|
||||
@ -377,10 +377,12 @@ void ESP32BLE::loop() {
|
||||
// This is verified at compile-time by static_assert checks in ble_event.h
|
||||
// The struct already contains our copy of the status (copied in BLEEvent constructor)
|
||||
ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
|
||||
#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT
|
||||
for (auto *gap_handler : this->gap_event_handlers_) {
|
||||
gap_handler->gap_event_handler(
|
||||
gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.scan_complete));
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
// Advertising complete events
|
||||
@ -391,19 +393,23 @@ void ESP32BLE::loop() {
|
||||
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
|
||||
// All advertising complete events have the same structure with just status
|
||||
ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
|
||||
#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT
|
||||
for (auto *gap_handler : this->gap_event_handlers_) {
|
||||
gap_handler->gap_event_handler(
|
||||
gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.adv_complete));
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
// RSSI complete event
|
||||
case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
|
||||
ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
|
||||
#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT
|
||||
for (auto *gap_handler : this->gap_event_handlers_) {
|
||||
gap_handler->gap_event_handler(
|
||||
gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.read_rssi_complete));
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
// Security events
|
||||
@ -413,10 +419,12 @@ void ESP32BLE::loop() {
|
||||
case ESP_GAP_BLE_PASSKEY_REQ_EVT:
|
||||
case ESP_GAP_BLE_NC_REQ_EVT:
|
||||
ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
|
||||
#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT
|
||||
for (auto *gap_handler : this->gap_event_handlers_) {
|
||||
gap_handler->gap_event_handler(
|
||||
gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.security));
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -125,19 +125,25 @@ class ESP32BLE : public Component {
|
||||
void advertising_register_raw_advertisement_callback(std::function<void(bool)> &&callback);
|
||||
#endif
|
||||
|
||||
#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT
|
||||
void register_gap_event_handler(GAPEventHandler *handler) { this->gap_event_handlers_.push_back(handler); }
|
||||
#endif
|
||||
#ifdef ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT
|
||||
void register_gap_scan_event_handler(GAPScanEventHandler *handler) {
|
||||
this->gap_scan_event_handlers_.push_back(handler);
|
||||
}
|
||||
#ifdef USE_ESP32_BLE_CLIENT
|
||||
#endif
|
||||
#if defined(USE_ESP32_BLE_CLIENT) && defined(ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT)
|
||||
void register_gattc_event_handler(GATTcEventHandler *handler) { this->gattc_event_handlers_.push_back(handler); }
|
||||
#endif
|
||||
#ifdef USE_ESP32_BLE_SERVER
|
||||
#if defined(USE_ESP32_BLE_SERVER) && defined(ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT)
|
||||
void register_gatts_event_handler(GATTsEventHandler *handler) { this->gatts_event_handlers_.push_back(handler); }
|
||||
#endif
|
||||
#ifdef ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT
|
||||
void register_ble_status_event_handler(BLEStatusEventHandler *handler) {
|
||||
this->ble_status_event_handlers_.push_back(handler);
|
||||
}
|
||||
#endif
|
||||
void set_enable_on_boot(bool enable_on_boot) { this->enable_on_boot_ = enable_on_boot; }
|
||||
|
||||
protected:
|
||||
@ -159,16 +165,22 @@ class ESP32BLE : public Component {
|
||||
private:
|
||||
template<typename... Args> friend void enqueue_ble_event(Args... args);
|
||||
|
||||
// Vectors (12 bytes each on 32-bit, naturally aligned to 4 bytes)
|
||||
std::vector<GAPEventHandler *> gap_event_handlers_;
|
||||
std::vector<GAPScanEventHandler *> gap_scan_event_handlers_;
|
||||
#ifdef USE_ESP32_BLE_CLIENT
|
||||
std::vector<GATTcEventHandler *> gattc_event_handlers_;
|
||||
// Handler vectors - use StaticVector when counts are known at compile time
|
||||
#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT
|
||||
StaticVector<GAPEventHandler *, ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT> gap_event_handlers_;
|
||||
#endif
|
||||
#ifdef USE_ESP32_BLE_SERVER
|
||||
std::vector<GATTsEventHandler *> gatts_event_handlers_;
|
||||
#ifdef ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT
|
||||
StaticVector<GAPScanEventHandler *, ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT> gap_scan_event_handlers_;
|
||||
#endif
|
||||
#if defined(USE_ESP32_BLE_CLIENT) && defined(ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT)
|
||||
StaticVector<GATTcEventHandler *, ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT> gattc_event_handlers_;
|
||||
#endif
|
||||
#if defined(USE_ESP32_BLE_SERVER) && defined(ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT)
|
||||
StaticVector<GATTsEventHandler *, ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT> gatts_event_handlers_;
|
||||
#endif
|
||||
#ifdef ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT
|
||||
StaticVector<BLEStatusEventHandler *, ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT> ble_status_event_handlers_;
|
||||
#endif
|
||||
std::vector<BLEStatusEventHandler *> ble_status_event_handlers_;
|
||||
|
||||
// Large objects (size depends on template parameters, but typically aligned to 4 bytes)
|
||||
esphome::LockFreeQueue<BLEEvent, MAX_BLE_QUEUE_SIZE> ble_events_;
|
||||
|
@ -74,7 +74,7 @@ async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID], uuid_arr)
|
||||
|
||||
parent = await cg.get_variable(config[esp32_ble.CONF_BLE_ID])
|
||||
cg.add(parent.register_gap_event_handler(var))
|
||||
esp32_ble.register_gap_event_handler(parent, var)
|
||||
|
||||
await cg.register_component(var, config)
|
||||
cg.add(var.set_major(config[CONF_MAJOR]))
|
||||
|
@ -546,8 +546,8 @@ async def to_code(config):
|
||||
await cg.register_component(var, config)
|
||||
|
||||
parent = await cg.get_variable(config[esp32_ble.CONF_BLE_ID])
|
||||
cg.add(parent.register_gatts_event_handler(var))
|
||||
cg.add(parent.register_ble_status_event_handler(var))
|
||||
esp32_ble.register_gatts_event_handler(parent, var)
|
||||
esp32_ble.register_ble_status_event_handler(parent, var)
|
||||
cg.add(var.set_parent(parent))
|
||||
cg.add(parent.advertising_set_appearance(config[CONF_APPEARANCE]))
|
||||
if CONF_MANUFACTURER_DATA in config:
|
||||
|
@ -246,10 +246,10 @@ async def to_code(config):
|
||||
await cg.register_component(var, config)
|
||||
|
||||
parent = await cg.get_variable(config[esp32_ble.CONF_BLE_ID])
|
||||
cg.add(parent.register_gap_event_handler(var))
|
||||
cg.add(parent.register_gap_scan_event_handler(var))
|
||||
cg.add(parent.register_gattc_event_handler(var))
|
||||
cg.add(parent.register_ble_status_event_handler(var))
|
||||
esp32_ble.register_gap_event_handler(parent, var)
|
||||
esp32_ble.register_gap_scan_event_handler(parent, var)
|
||||
esp32_ble.register_gattc_event_handler(parent, var)
|
||||
esp32_ble.register_ble_status_event_handler(parent, var)
|
||||
cg.add(var.set_parent(parent))
|
||||
|
||||
params = config[CONF_SCAN_PARAMETERS]
|
||||
|
@ -178,6 +178,11 @@
|
||||
#define USE_ESP32_BLE_SERVER_ON_DISCONNECT
|
||||
#define ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT 1
|
||||
#define ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT 1
|
||||
#define ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT 2
|
||||
#define ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT 1
|
||||
#define ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT 1
|
||||
#define ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT 1
|
||||
#define ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT 2
|
||||
#define USE_ESP32_CAMERA_JPEG_ENCODER
|
||||
#define USE_I2C
|
||||
#define USE_IMPROV
|
||||
|
Loading…
x
Reference in New Issue
Block a user