1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-12 23:03:46 +01:00

[esp32_ble_tracker] Replace std::vector with StaticVector for listeners and clients (#11173)

This commit is contained in:
J. Nick Koston
2025-10-11 05:47:42 -10:00
committed by GitHub
parent cb602c9b1a
commit 9bd9b043c8
4 changed files with 69 additions and 3 deletions

View File

@@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
import logging import logging
from esphome import automation from esphome import automation
@@ -52,9 +53,19 @@ class BLEFeatures(StrEnum):
ESP_BT_DEVICE = "ESP_BT_DEVICE" ESP_BT_DEVICE = "ESP_BT_DEVICE"
# Dataclass for registration counts
@dataclass
class RegistrationCounts:
listeners: int = 0
clients: int = 0
# Set to track which features are needed by components # Set to track which features are needed by components
_required_features: set[BLEFeatures] = set() _required_features: set[BLEFeatures] = set()
# Track registration counts for StaticVector sizing
_registration_counts = RegistrationCounts()
def register_ble_features(features: set[BLEFeatures]) -> None: def register_ble_features(features: set[BLEFeatures]) -> None:
"""Register BLE features that a component needs. """Register BLE features that a component needs.
@@ -257,12 +268,14 @@ async def to_code(config):
register_ble_features({BLEFeatures.ESP_BT_DEVICE}) register_ble_features({BLEFeatures.ESP_BT_DEVICE})
for conf in config.get(CONF_ON_BLE_ADVERTISE, []): for conf in config.get(CONF_ON_BLE_ADVERTISE, []):
_registration_counts.listeners += 1
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
if CONF_MAC_ADDRESS in conf: if CONF_MAC_ADDRESS in conf:
addr_list = [it.as_hex for it in conf[CONF_MAC_ADDRESS]] addr_list = [it.as_hex for it in conf[CONF_MAC_ADDRESS]]
cg.add(trigger.set_addresses(addr_list)) cg.add(trigger.set_addresses(addr_list))
await automation.build_automation(trigger, [(ESPBTDeviceConstRef, "x")], conf) await automation.build_automation(trigger, [(ESPBTDeviceConstRef, "x")], conf)
for conf in config.get(CONF_ON_BLE_SERVICE_DATA_ADVERTISE, []): for conf in config.get(CONF_ON_BLE_SERVICE_DATA_ADVERTISE, []):
_registration_counts.listeners += 1
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
if len(conf[CONF_SERVICE_UUID]) == len(bt_uuid16_format): if len(conf[CONF_SERVICE_UUID]) == len(bt_uuid16_format):
cg.add(trigger.set_service_uuid16(as_hex(conf[CONF_SERVICE_UUID]))) cg.add(trigger.set_service_uuid16(as_hex(conf[CONF_SERVICE_UUID])))
@@ -275,6 +288,7 @@ async def to_code(config):
cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex)) cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex))
await automation.build_automation(trigger, [(adv_data_t_const_ref, "x")], conf) await automation.build_automation(trigger, [(adv_data_t_const_ref, "x")], conf)
for conf in config.get(CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE, []): for conf in config.get(CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE, []):
_registration_counts.listeners += 1
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
if len(conf[CONF_MANUFACTURER_ID]) == len(bt_uuid16_format): if len(conf[CONF_MANUFACTURER_ID]) == len(bt_uuid16_format):
cg.add(trigger.set_manufacturer_uuid16(as_hex(conf[CONF_MANUFACTURER_ID]))) cg.add(trigger.set_manufacturer_uuid16(as_hex(conf[CONF_MANUFACTURER_ID])))
@@ -287,6 +301,7 @@ async def to_code(config):
cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex)) cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex))
await automation.build_automation(trigger, [(adv_data_t_const_ref, "x")], conf) await automation.build_automation(trigger, [(adv_data_t_const_ref, "x")], conf)
for conf in config.get(CONF_ON_SCAN_END, []): for conf in config.get(CONF_ON_SCAN_END, []):
_registration_counts.listeners += 1
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf) await automation.build_automation(trigger, [], conf)
@@ -320,6 +335,17 @@ async def _add_ble_features():
cg.add_define("USE_ESP32_BLE_DEVICE") cg.add_define("USE_ESP32_BLE_DEVICE")
cg.add_define("USE_ESP32_BLE_UUID") cg.add_define("USE_ESP32_BLE_UUID")
# Add defines for StaticVector sizing based on registration counts
# Only define if count > 0 to avoid allocating unnecessary memory
if _registration_counts.listeners > 0:
cg.add_define(
"ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT", _registration_counts.listeners
)
if _registration_counts.clients > 0:
cg.add_define(
"ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT", _registration_counts.clients
)
ESP32_BLE_START_SCAN_ACTION_SCHEMA = cv.Schema( ESP32_BLE_START_SCAN_ACTION_SCHEMA = cv.Schema(
{ {
@@ -369,6 +395,7 @@ async def register_ble_device(
var: cg.SafeExpType, config: ConfigType var: cg.SafeExpType, config: ConfigType
) -> cg.SafeExpType: ) -> cg.SafeExpType:
register_ble_features({BLEFeatures.ESP_BT_DEVICE}) register_ble_features({BLEFeatures.ESP_BT_DEVICE})
_registration_counts.listeners += 1
paren = await cg.get_variable(config[CONF_ESP32_BLE_ID]) paren = await cg.get_variable(config[CONF_ESP32_BLE_ID])
cg.add(paren.register_listener(var)) cg.add(paren.register_listener(var))
return var return var
@@ -376,6 +403,7 @@ async def register_ble_device(
async def register_client(var: cg.SafeExpType, config: ConfigType) -> cg.SafeExpType: async def register_client(var: cg.SafeExpType, config: ConfigType) -> cg.SafeExpType:
register_ble_features({BLEFeatures.ESP_BT_DEVICE}) register_ble_features({BLEFeatures.ESP_BT_DEVICE})
_registration_counts.clients += 1
paren = await cg.get_variable(config[CONF_ESP32_BLE_ID]) paren = await cg.get_variable(config[CONF_ESP32_BLE_ID])
cg.add(paren.register_client(var)) cg.add(paren.register_client(var))
return var return var
@@ -389,6 +417,7 @@ async def register_raw_ble_device(
This does NOT register the ESP_BT_DEVICE feature, meaning ESPBTDevice This does NOT register the ESP_BT_DEVICE feature, meaning ESPBTDevice
will not be compiled in if this is the only registration method used. will not be compiled in if this is the only registration method used.
""" """
_registration_counts.listeners += 1
paren = await cg.get_variable(config[CONF_ESP32_BLE_ID]) paren = await cg.get_variable(config[CONF_ESP32_BLE_ID])
cg.add(paren.register_listener(var)) cg.add(paren.register_listener(var))
return var return var
@@ -402,6 +431,7 @@ async def register_raw_client(
This does NOT register the ESP_BT_DEVICE feature, meaning ESPBTDevice This does NOT register the ESP_BT_DEVICE feature, meaning ESPBTDevice
will not be compiled in if this is the only registration method used. will not be compiled in if this is the only registration method used.
""" """
_registration_counts.clients += 1
paren = await cg.get_variable(config[CONF_ESP32_BLE_ID]) paren = await cg.get_variable(config[CONF_ESP32_BLE_ID])
cg.add(paren.register_client(var)) cg.add(paren.register_client(var))
return var return var

View File

@@ -74,9 +74,11 @@ void ESP32BLETracker::setup() {
[this](ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) { [this](ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) {
if (state == ota::OTA_STARTED) { if (state == ota::OTA_STARTED) {
this->stop_scan(); this->stop_scan();
#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT
for (auto *client : this->clients_) { for (auto *client : this->clients_) {
client->disconnect(); client->disconnect();
} }
#endif
} }
}); });
#endif #endif
@@ -206,8 +208,10 @@ void ESP32BLETracker::start_scan_(bool first) {
this->set_scanner_state_(ScannerState::STARTING); this->set_scanner_state_(ScannerState::STARTING);
ESP_LOGD(TAG, "Starting scan, set scanner state to STARTING."); ESP_LOGD(TAG, "Starting scan, set scanner state to STARTING.");
if (!first) { if (!first) {
#ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT
for (auto *listener : this->listeners_) for (auto *listener : this->listeners_)
listener->on_scan_end(); listener->on_scan_end();
#endif
} }
#ifdef USE_ESP32_BLE_DEVICE #ifdef USE_ESP32_BLE_DEVICE
this->already_discovered_.clear(); this->already_discovered_.clear();
@@ -236,20 +240,25 @@ void ESP32BLETracker::start_scan_(bool first) {
} }
void ESP32BLETracker::register_client(ESPBTClient *client) { void ESP32BLETracker::register_client(ESPBTClient *client) {
#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT
client->app_id = ++this->app_id_; client->app_id = ++this->app_id_;
this->clients_.push_back(client); this->clients_.push_back(client);
this->recalculate_advertisement_parser_types(); this->recalculate_advertisement_parser_types();
#endif
} }
void ESP32BLETracker::register_listener(ESPBTDeviceListener *listener) { void ESP32BLETracker::register_listener(ESPBTDeviceListener *listener) {
#ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT
listener->set_parent(this); listener->set_parent(this);
this->listeners_.push_back(listener); this->listeners_.push_back(listener);
this->recalculate_advertisement_parser_types(); this->recalculate_advertisement_parser_types();
#endif
} }
void ESP32BLETracker::recalculate_advertisement_parser_types() { void ESP32BLETracker::recalculate_advertisement_parser_types() {
this->raw_advertisements_ = false; this->raw_advertisements_ = false;
this->parse_advertisements_ = false; this->parse_advertisements_ = false;
#ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT
for (auto *listener : this->listeners_) { for (auto *listener : this->listeners_) {
if (listener->get_advertisement_parser_type() == AdvertisementParserType::PARSED_ADVERTISEMENTS) { if (listener->get_advertisement_parser_type() == AdvertisementParserType::PARSED_ADVERTISEMENTS) {
this->parse_advertisements_ = true; this->parse_advertisements_ = true;
@@ -257,6 +266,8 @@ void ESP32BLETracker::recalculate_advertisement_parser_types() {
this->raw_advertisements_ = true; this->raw_advertisements_ = true;
} }
} }
#endif
#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT
for (auto *client : this->clients_) { for (auto *client : this->clients_) {
if (client->get_advertisement_parser_type() == AdvertisementParserType::PARSED_ADVERTISEMENTS) { if (client->get_advertisement_parser_type() == AdvertisementParserType::PARSED_ADVERTISEMENTS) {
this->parse_advertisements_ = true; this->parse_advertisements_ = true;
@@ -264,6 +275,7 @@ void ESP32BLETracker::recalculate_advertisement_parser_types() {
this->raw_advertisements_ = true; this->raw_advertisements_ = true;
} }
} }
#endif
} }
void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
@@ -282,10 +294,12 @@ void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_ga
default: default:
break; break;
} }
// Forward all events to clients (scan results are handled separately via gap_scan_event_handler) // Forward all events to clients (scan results are handled separately via gap_scan_event_handler)
#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT
for (auto *client : this->clients_) { for (auto *client : this->clients_) {
client->gap_event_handler(event, param); client->gap_event_handler(event, param);
} }
#endif
} }
void ESP32BLETracker::gap_scan_event_handler(const BLEScanResult &scan_result) { void ESP32BLETracker::gap_scan_event_handler(const BLEScanResult &scan_result) {
@@ -348,9 +362,11 @@ void ESP32BLETracker::gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_
void ESP32BLETracker::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, void ESP32BLETracker::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) { esp_ble_gattc_cb_param_t *param) {
#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT
for (auto *client : this->clients_) { for (auto *client : this->clients_) {
client->gattc_event_handler(event, gattc_if, param); client->gattc_event_handler(event, gattc_if, param);
} }
#endif
} }
void ESP32BLETracker::set_scanner_state_(ScannerState state) { void ESP32BLETracker::set_scanner_state_(ScannerState state) {
@@ -704,12 +720,16 @@ bool ESPBTDevice::resolve_irk(const uint8_t *irk) const {
void ESP32BLETracker::process_scan_result_(const BLEScanResult &scan_result) { void ESP32BLETracker::process_scan_result_(const BLEScanResult &scan_result) {
// Process raw advertisements // Process raw advertisements
if (this->raw_advertisements_) { if (this->raw_advertisements_) {
#ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT
for (auto *listener : this->listeners_) { for (auto *listener : this->listeners_) {
listener->parse_devices(&scan_result, 1); listener->parse_devices(&scan_result, 1);
} }
#endif
#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT
for (auto *client : this->clients_) { for (auto *client : this->clients_) {
client->parse_devices(&scan_result, 1); client->parse_devices(&scan_result, 1);
} }
#endif
} }
// Process parsed advertisements // Process parsed advertisements
@@ -719,16 +739,20 @@ void ESP32BLETracker::process_scan_result_(const BLEScanResult &scan_result) {
device.parse_scan_rst(scan_result); device.parse_scan_rst(scan_result);
bool found = false; bool found = false;
#ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT
for (auto *listener : this->listeners_) { for (auto *listener : this->listeners_) {
if (listener->parse_device(device)) if (listener->parse_device(device))
found = true; found = true;
} }
#endif
#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT
for (auto *client : this->clients_) { for (auto *client : this->clients_) {
if (client->parse_device(device)) { if (client->parse_device(device)) {
found = true; found = true;
} }
} }
#endif
if (!found && !this->scan_continuous_) { if (!found && !this->scan_continuous_) {
this->print_bt_device_info(device); this->print_bt_device_info(device);
@@ -745,8 +769,10 @@ void ESP32BLETracker::cleanup_scan_state_(bool is_stop_complete) {
// Reset timeout state machine instead of cancelling scheduler timeout // Reset timeout state machine instead of cancelling scheduler timeout
this->scan_timeout_state_ = ScanTimeoutState::INACTIVE; this->scan_timeout_state_ = ScanTimeoutState::INACTIVE;
#ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT
for (auto *listener : this->listeners_) for (auto *listener : this->listeners_)
listener->on_scan_end(); listener->on_scan_end();
#endif
this->set_scanner_state_(ScannerState::IDLE); this->set_scanner_state_(ScannerState::IDLE);
} }
@@ -770,6 +796,7 @@ void ESP32BLETracker::handle_scanner_failure_() {
void ESP32BLETracker::try_promote_discovered_clients_() { void ESP32BLETracker::try_promote_discovered_clients_() {
// Only promote the first discovered client to avoid multiple simultaneous connections // Only promote the first discovered client to avoid multiple simultaneous connections
#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT
for (auto *client : this->clients_) { for (auto *client : this->clients_) {
if (client->state() != ClientState::DISCOVERED) { if (client->state() != ClientState::DISCOVERED) {
continue; continue;
@@ -791,6 +818,7 @@ void ESP32BLETracker::try_promote_discovered_clients_() {
client->connect(); client->connect();
break; break;
} }
#endif
} }
const char *ESP32BLETracker::scanner_state_to_string_(ScannerState state) const { const char *ESP32BLETracker::scanner_state_to_string_(ScannerState state) const {

View File

@@ -302,6 +302,7 @@ class ESP32BLETracker : public Component,
/// Count clients in each state /// Count clients in each state
ClientStateCounts count_client_states_() const { ClientStateCounts count_client_states_() const {
ClientStateCounts counts; ClientStateCounts counts;
#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT
for (auto *client : this->clients_) { for (auto *client : this->clients_) {
switch (client->state()) { switch (client->state()) {
case ClientState::DISCONNECTING: case ClientState::DISCONNECTING:
@@ -317,12 +318,17 @@ class ESP32BLETracker : public Component,
break; break;
} }
} }
#endif
return counts; return counts;
} }
// Group 1: Large objects (12+ bytes) - vectors and callback manager // Group 1: Large objects (12+ bytes) - vectors and callback manager
std::vector<ESPBTDeviceListener *> listeners_; #ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT
std::vector<ESPBTClient *> clients_; StaticVector<ESPBTDeviceListener *, ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT> listeners_;
#endif
#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT
StaticVector<ESPBTClient *, ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT> clients_;
#endif
CallbackManager<void(ScannerState)> scanner_state_callbacks_; CallbackManager<void(ScannerState)> scanner_state_callbacks_;
#ifdef USE_ESP32_BLE_DEVICE #ifdef USE_ESP32_BLE_DEVICE
/// Vector of addresses that have already been printed in print_bt_device_info /// Vector of addresses that have already been printed in print_bt_device_info

View File

@@ -175,6 +175,8 @@
#define USE_ESP32_BLE_SERVER_DESCRIPTOR_ON_WRITE #define USE_ESP32_BLE_SERVER_DESCRIPTOR_ON_WRITE
#define USE_ESP32_BLE_SERVER_ON_CONNECT #define USE_ESP32_BLE_SERVER_ON_CONNECT
#define USE_ESP32_BLE_SERVER_ON_DISCONNECT #define USE_ESP32_BLE_SERVER_ON_DISCONNECT
#define ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT 1
#define ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT 1
#define USE_ESP32_CAMERA_JPEG_ENCODER #define USE_ESP32_CAMERA_JPEG_ENCODER
#define USE_I2C #define USE_I2C
#define USE_IMPROV #define USE_IMPROV