[esp32_ble] Devirtualize BLE event handler dispatch (#15310)

This commit is contained in:
J. Nick Koston
2026-03-30 08:21:58 -10:00
committed by GitHub
parent ad3f6ae313
commit ffee4c22b3
10 changed files with 128 additions and 88 deletions
+57 -9
View File
@@ -134,10 +134,38 @@ class HandlerCounts:
_handler_counts = HandlerCounts()
def _add_callback(
parent_var: cg.MockObj,
method: str,
handler_var: cg.MockObj,
params: str,
call_args: str,
) -> None:
"""Generate a lambda callback that forwards to a handler method.
Uses a braced scope with a local pointer variable so the generated C++
lambda captures only that pointer, avoiding GCC warnings about capturing
variables with static storage duration.
"""
cg.add(
cg.RawStatement(
f"{{ auto *h = {handler_var}; "
f"{parent_var}->{method}("
f"[h]({params}) {{ h->{call_args}; }}); }}"
)
)
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))
_add_callback(
parent_var,
"add_gap_event_callback",
handler_var,
"esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param",
"gap_event_handler(event, param)",
)
def register_gap_scan_event_handler(
@@ -145,7 +173,13 @@ def register_gap_scan_event_handler(
) -> 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))
_add_callback(
parent_var,
"add_gap_scan_event_callback",
handler_var,
"const esphome::esp32_ble::BLEScanResult &scan_result",
"gap_scan_event_handler(scan_result)",
)
def register_gattc_event_handler(
@@ -153,7 +187,13 @@ def register_gattc_event_handler(
) -> 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))
_add_callback(
parent_var,
"add_gattc_event_callback",
handler_var,
"esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param",
"gattc_event_handler(event, gattc_if, param)",
)
def register_gatts_event_handler(
@@ -161,7 +201,13 @@ def register_gatts_event_handler(
) -> 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))
_add_callback(
parent_var,
"add_gatts_event_callback",
handler_var,
"esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param",
"gatts_event_handler(event, gatts_if, param)",
)
def register_ble_status_event_handler(
@@ -169,7 +215,13 @@ def register_ble_status_event_handler(
) -> 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))
_add_callback(
parent_var,
"add_ble_status_event_callback",
handler_var,
"",
"ble_before_disabled_event_handler()",
)
def register_bt_logger(*loggers: BTLoggers) -> None:
@@ -225,10 +277,6 @@ NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2]
esp32_ble_ns = cg.esphome_ns.namespace("esp32_ble")
ESP32BLE = esp32_ble_ns.class_("ESP32BLE", cg.Component)
GAPEventHandler = esp32_ble_ns.class_("GAPEventHandler")
GATTcEventHandler = esp32_ble_ns.class_("GATTcEventHandler")
GATTsEventHandler = esp32_ble_ns.class_("GATTsEventHandler")
BLEEnabledCondition = esp32_ble_ns.class_("BLEEnabledCondition", automation.Condition)
BLEEnableAction = esp32_ble_ns.class_("BLEEnableAction", automation.Action)
BLEDisableAction = esp32_ble_ns.class_("BLEDisableAction", automation.Action)
+5 -16
View File
@@ -408,9 +408,7 @@ void ESP32BLE::loop() {
esp_gatt_if_t gatts_if = ble_event->event_.gatts.gatts_if;
esp_ble_gatts_cb_param_t *param = &ble_event->event_.gatts.gatts_param;
ESP_LOGV(TAG, "gatts_event [esp_gatt_if: %d] - %d", gatts_if, event);
for (auto *gatts_handler : this->gatts_event_handlers_) {
gatts_handler->gatts_event_handler(event, gatts_if, param);
}
this->gatts_event_callbacks_.call(event, gatts_if, param);
break;
}
#endif
@@ -420,9 +418,7 @@ void ESP32BLE::loop() {
esp_gatt_if_t gattc_if = ble_event->event_.gattc.gattc_if;
esp_ble_gattc_cb_param_t *param = &ble_event->event_.gattc.gattc_param;
ESP_LOGV(TAG, "gattc_event [esp_gatt_if: %d] - %d", gattc_if, event);
for (auto *gattc_handler : this->gattc_event_handlers_) {
gattc_handler->gattc_event_handler(event, gattc_if, param);
}
this->gattc_event_callbacks_.call(event, gattc_if, param);
break;
}
#endif
@@ -431,10 +427,7 @@ void ESP32BLE::loop() {
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());
}
this->gap_scan_event_callbacks_.call(ble_event->scan_result());
#endif
break;
@@ -478,9 +471,7 @@ void ESP32BLE::loop() {
}
// clang-format on
// Dispatch to all registered handlers
for (auto *gap_handler : this->gap_event_handlers_) {
gap_handler->gap_event_handler(gap_event, param);
}
this->gap_event_callbacks_.call(gap_event, param);
}
#endif
break;
@@ -518,9 +509,7 @@ void ESP32BLE::loop_handle_state_transition_not_active_() {
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();
}
this->ble_status_event_callbacks_.call();
#endif
if (!ble_dismantle_()) {
+25 -44
View File
@@ -87,37 +87,6 @@ enum BLEComponentState : uint8_t {
BLE_COMPONENT_STATE_ACTIVE,
};
class GAPEventHandler {
public:
virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) = 0;
};
class GAPScanEventHandler {
public:
virtual void gap_scan_event_handler(const BLEScanResult &scan_result) = 0;
};
#ifdef USE_ESP32_BLE_CLIENT
class GATTcEventHandler {
public:
virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) = 0;
};
#endif
#ifdef USE_ESP32_BLE_SERVER
class GATTsEventHandler {
public:
virtual void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t *param) = 0;
};
#endif
class BLEStatusEventHandler {
public:
virtual void ble_before_disabled_event_handler() = 0;
};
class ESP32BLE : public Component {
public:
void set_io_capability(IoCapability io_capability) { this->io_cap_ = (esp_ble_io_cap_t) io_capability; }
@@ -154,22 +123,28 @@ class ESP32BLE : public Component {
#endif
#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT
void register_gap_event_handler(GAPEventHandler *handler) { this->gap_event_handlers_.push_back(handler); }
template<typename F> void add_gap_event_callback(F &&callback) {
this->gap_event_callbacks_.add(std::forward<F>(callback));
}
#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);
template<typename F> void add_gap_scan_event_callback(F &&callback) {
this->gap_scan_event_callbacks_.add(std::forward<F>(callback));
}
#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); }
template<typename F> void add_gattc_event_callback(F &&callback) {
this->gattc_event_callbacks_.add(std::forward<F>(callback));
}
#endif
#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); }
template<typename F> void add_gatts_event_callback(F &&callback) {
this->gatts_event_callbacks_.add(std::forward<F>(callback));
}
#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);
template<typename F> void add_ble_status_event_callback(F &&callback) {
this->ble_status_event_callbacks_.add(std::forward<F>(callback));
}
#endif
void set_enable_on_boot(bool enable_on_boot) { this->enable_on_boot_ = enable_on_boot; }
@@ -202,21 +177,27 @@ class ESP32BLE : public Component {
private:
template<typename... Args> friend void enqueue_ble_event(Args... args);
// 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_;
StaticCallbackManager<ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT,
void(esp_gap_ble_cb_event_t, esp_ble_gap_cb_param_t *)>
gap_event_callbacks_;
#endif
#ifdef ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT
StaticVector<GAPScanEventHandler *, ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT> gap_scan_event_handlers_;
StaticCallbackManager<ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT, void(const BLEScanResult &)>
gap_scan_event_callbacks_;
#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_;
StaticCallbackManager<ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT,
void(esp_gattc_cb_event_t, esp_gatt_if_t, esp_ble_gattc_cb_param_t *)>
gattc_event_callbacks_;
#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_;
StaticCallbackManager<ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT,
void(esp_gatts_cb_event_t, esp_gatt_if_t, esp_ble_gatts_cb_param_t *)>
gatts_event_callbacks_;
#endif
#ifdef ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT
StaticVector<BLEStatusEventHandler *, ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT> ble_status_event_handlers_;
StaticCallbackManager<ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT, void()> ble_status_event_callbacks_;
#endif
// Large objects (size depends on template parameters, but typically aligned to 4 bytes)
@@ -13,7 +13,6 @@ esp32_ble_beacon_ns = cg.esphome_ns.namespace("esp32_ble_beacon")
ESP32BLEBeacon = esp32_ble_beacon_ns.class_(
"ESP32BLEBeacon",
cg.Component,
esp32_ble.GAPEventHandler,
cg.Parented.template(esp32_ble.ESP32BLE),
)
CONF_MAJOR = "major"
@@ -35,7 +35,7 @@ using esp_ble_ibeacon_t = struct {
using namespace esp32_ble;
class ESP32BLEBeacon : public Component, public GAPEventHandler, public Parented<ESP32BLE> {
class ESP32BLEBeacon : public Component, public Parented<ESP32BLE> {
public:
explicit ESP32BLEBeacon(const std::array<uint8_t, 16> &uuid) : uuid_(uuid) {}
@@ -51,7 +51,7 @@ class ESP32BLEBeacon : public Component, public GAPEventHandler, public Parented
#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID
void set_tx_power(esp_power_level_t val) { this->tx_power_ = val; }
#endif
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
protected:
void on_advertise_();
@@ -72,7 +72,6 @@ BLECharacteristic_ns = esp32_ble_server_ns.namespace("BLECharacteristic")
BLEServer = esp32_ble_server_ns.class_(
"BLEServer",
cg.Component,
esp32_ble.GATTsEventHandler,
cg.Parented.template(esp32_ble.ESP32BLE),
)
esp32_ble_server_automations_ns = esp32_ble_server_ns.namespace(
@@ -24,7 +24,7 @@ namespace esp32_ble_server {
using namespace esp32_ble;
using namespace bytebuffer;
class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEventHandler, public Parented<ESP32BLE> {
class BLEServer : public Component, public Parented<ESP32BLE> {
public:
void setup() override;
void loop() override;
@@ -53,10 +53,9 @@ class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEv
const uint16_t *get_clients() const { return this->clients_; }
uint8_t get_client_count() const { return this->client_count_; }
void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t *param) override;
void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
void ble_before_disabled_event_handler() override;
void ble_before_disabled_event_handler();
// Direct callback registration - supports multiple callbacks
void on_connect(std::function<void(uint16_t)> &&callback) {
@@ -90,8 +90,6 @@ esp32_ble_tracker_ns = cg.esphome_ns.namespace("esp32_ble_tracker")
ESP32BLETracker = esp32_ble_tracker_ns.class_(
"ESP32BLETracker",
cg.Component,
esp32_ble.GAPEventHandler,
esp32_ble.GATTcEventHandler,
cg.Parented.template(esp32_ble.ESP32BLE),
)
ESPBTClient = esp32_ble_tracker_ns.class_("ESPBTClient")
@@ -291,10 +291,6 @@ class ESPBTClient : public ESPBTDeviceListener {
};
class ESP32BLETracker : public Component,
public GAPEventHandler,
public GAPScanEventHandler,
public GATTcEventHandler,
public BLEStatusEventHandler,
#ifdef USE_OTA_STATE_LISTENER
public ota::OTAGlobalStateListener,
#endif
@@ -325,11 +321,10 @@ class ESP32BLETracker : public Component,
void start_scan();
void stop_scan();
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
void gap_scan_event_handler(const BLEScanResult &scan_result) override;
void ble_before_disabled_event_handler() override;
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
void gap_scan_event_handler(const BLEScanResult &scan_result);
void ble_before_disabled_event_handler();
#ifdef USE_OTA_STATE_LISTENER
void on_ota_global_state(ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) override;
+32
View File
@@ -1830,6 +1830,38 @@ template<typename... Ts> class CallbackManager<void(Ts...)> {
std::vector<Callback<void(Ts...)>> callbacks_;
};
/** CallbackManager backed by StaticVector for compile-time-known callback counts.
*
* Drop-in replacement for CallbackManager that avoids std::vector template bloat
* (_M_realloc_insert, etc.) when the maximum number of callbacks is known at compile time.
*
* @tparam N Maximum number of callbacks (compile-time constant, typically from cg.add_define())
* @tparam Ts The arguments for the callbacks, wrapped in void().
*/
template<size_t N, typename... X> class StaticCallbackManager;
template<size_t N, typename... Ts> class StaticCallbackManager<N, void(Ts...)> {
public:
/// Add any callable. Small trivially-copyable callables (like [this] lambdas)
/// are stored inline without heap allocation.
template<typename F> void add(F &&callback) { this->add_(Callback<void(Ts...)>::create(std::forward<F>(callback))); }
/// Call all callbacks in this manager.
void call(Ts... args) {
for (auto &cb : this->callbacks_)
cb.call(args...);
}
size_t size() const { return this->callbacks_.size(); }
/// Call all callbacks in this manager.
void operator()(Ts... args) { call(args...); }
protected:
/// Non-template core to avoid code duplication per lambda type.
void add_(Callback<void(Ts...)> cb) { this->callbacks_.push_back(cb); }
StaticVector<Callback<void(Ts...)>, N> callbacks_;
};
template<typename... X> class LazyCallbackManager;
/** Lazy-allocating callback manager that only allocates memory when callbacks are registered.