1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-28 16:12:24 +01:00

[esp32_ble_server] Optimize notification and action managers for typical use cases

This commit is contained in:
J. Nick Koston
2025-09-26 09:14:29 -05:00
parent 1560b8b8e2
commit baf09e2eed
4 changed files with 89 additions and 22 deletions

View File

@@ -51,11 +51,11 @@ void BLECharacteristic::notify() {
for (auto &client : this->service_->get_server()->get_clients()) { for (auto &client : this->service_->get_server()->get_clients()) {
size_t length = this->value_.size(); size_t length = this->value_.size();
// If the client is not in the list of clients to notify, skip it // Find the client in the list of clients to notify
if (this->clients_to_notify_.count(client) == 0) auto *entry = this->find_client_in_notify_list_(client);
if (entry == nullptr)
continue; continue;
// If the client is in the list of clients to notify, check if it requires an ack (i.e. INDICATE) bool require_ack = entry->indicate;
bool require_ack = this->clients_to_notify_[client];
// TODO: Remove this block when INDICATE acknowledgment is supported // TODO: Remove this block when INDICATE acknowledgment is supported
if (require_ack) { if (require_ack) {
ESP_LOGW(TAG, "INDICATE acknowledgment is not yet supported (i.e. it works as a NOTIFY)"); ESP_LOGW(TAG, "INDICATE acknowledgment is not yet supported (i.e. it works as a NOTIFY)");
@@ -79,10 +79,11 @@ void BLECharacteristic::add_descriptor(BLEDescriptor *descriptor) {
uint16_t cccd = encode_uint16(value[1], value[0]); uint16_t cccd = encode_uint16(value[1], value[0]);
bool notify = (cccd & 1) != 0; bool notify = (cccd & 1) != 0;
bool indicate = (cccd & 2) != 0; bool indicate = (cccd & 2) != 0;
// Remove existing entry if present
this->remove_client_from_notify_list_(conn_id);
// Add new entry if needed
if (notify || indicate) { if (notify || indicate) {
this->clients_to_notify_[conn_id] = indicate; this->clients_to_notify_.push_back({conn_id, indicate});
} else {
this->clients_to_notify_.erase(conn_id);
} }
}); });
} }
@@ -307,6 +308,30 @@ void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt
} }
} }
void BLECharacteristic::remove_client_from_notify_list_(uint16_t conn_id) {
// Since we typically have very few clients (often just 1), we can optimize
// for the common case by swapping with the last element and popping
for (size_t i = 0; i < this->clients_to_notify_.size(); i++) {
if (this->clients_to_notify_[i].conn_id == conn_id) {
// Swap with last element and pop
if (i != this->clients_to_notify_.size() - 1) {
this->clients_to_notify_[i] = this->clients_to_notify_.back();
}
this->clients_to_notify_.pop_back();
return;
}
}
}
BLECharacteristic::ClientNotificationEntry *BLECharacteristic::find_client_in_notify_list_(uint16_t conn_id) {
for (auto &entry : this->clients_to_notify_) {
if (entry.conn_id == conn_id) {
return &entry;
}
}
return nullptr;
}
} // namespace esp32_ble_server } // namespace esp32_ble_server
} // namespace esphome } // namespace esphome

View File

@@ -6,7 +6,6 @@
#include "esphome/components/bytebuffer/bytebuffer.h" #include "esphome/components/bytebuffer/bytebuffer.h"
#include <vector> #include <vector>
#include <unordered_map>
#ifdef USE_ESP32 #ifdef USE_ESP32
@@ -89,7 +88,15 @@ class BLECharacteristic : public EventEmitter<BLECharacteristicEvt::VectorEvt, s
SemaphoreHandle_t set_value_lock_; SemaphoreHandle_t set_value_lock_;
std::vector<BLEDescriptor *> descriptors_; std::vector<BLEDescriptor *> descriptors_;
std::unordered_map<uint16_t, bool> clients_to_notify_;
struct ClientNotificationEntry {
uint16_t conn_id;
bool indicate; // true = indicate, false = notify
};
std::vector<ClientNotificationEntry> clients_to_notify_;
void remove_client_from_notify_list_(uint16_t conn_id);
ClientNotificationEntry *find_client_in_notify_list_(uint16_t conn_id);
esp_gatt_perm_t permissions_ = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; esp_gatt_perm_t permissions_ = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;

View File

@@ -45,17 +45,16 @@ Trigger<uint16_t> *BLETriggers::create_server_on_disconnect_trigger(BLEServer *s
void BLECharacteristicSetValueActionManager::set_listener(BLECharacteristic *characteristic, void BLECharacteristicSetValueActionManager::set_listener(BLECharacteristic *characteristic,
EventEmitterListenerID listener_id, EventEmitterListenerID listener_id,
const std::function<void()> &pre_notify_listener) { const std::function<void()> &pre_notify_listener) {
// Check if there is already a listener for this characteristic // Find and remove existing listener for this characteristic
if (this->listeners_.count(characteristic) > 0) { auto *existing = this->find_listener_(characteristic);
// Unpack the pair listener_id, pre_notify_listener_id if (existing != nullptr) {
auto listener_pairs = this->listeners_[characteristic];
EventEmitterListenerID old_listener_id = listener_pairs.first;
EventEmitterListenerID old_pre_notify_listener_id = listener_pairs.second;
// Remove the previous listener // Remove the previous listener
characteristic->EventEmitter<BLECharacteristicEvt::EmptyEvt, uint16_t>::off(BLECharacteristicEvt::EmptyEvt::ON_READ, characteristic->EventEmitter<BLECharacteristicEvt::EmptyEvt, uint16_t>::off(BLECharacteristicEvt::EmptyEvt::ON_READ,
old_listener_id); existing->listener_id);
// Remove the pre-notify listener // Remove the pre-notify listener
this->off(BLECharacteristicSetValueActionEvt::PRE_NOTIFY, old_pre_notify_listener_id); this->off(BLECharacteristicSetValueActionEvt::PRE_NOTIFY, existing->pre_notify_listener_id);
// Remove from vector
this->remove_listener_(characteristic);
} }
// Create a new listener for the pre-notify event // Create a new listener for the pre-notify event
EventEmitterListenerID pre_notify_listener_id = EventEmitterListenerID pre_notify_listener_id =
@@ -66,8 +65,32 @@ void BLECharacteristicSetValueActionManager::set_listener(BLECharacteristic *cha
pre_notify_listener(); pre_notify_listener();
} }
}); });
// Save the pair listener_id, pre_notify_listener_id to the map // Save the entry to the vector
this->listeners_[characteristic] = std::make_pair(listener_id, pre_notify_listener_id); this->listeners_.push_back({characteristic, listener_id, pre_notify_listener_id});
}
BLECharacteristicSetValueActionManager::ListenerEntry *BLECharacteristicSetValueActionManager::find_listener_(
BLECharacteristic *characteristic) {
for (auto &entry : this->listeners_) {
if (entry.characteristic == characteristic) {
return &entry;
}
}
return nullptr;
}
void BLECharacteristicSetValueActionManager::remove_listener_(BLECharacteristic *characteristic) {
// Since we typically have very few listeners, optimize by swapping with back and popping
for (size_t i = 0; i < this->listeners_.size(); i++) {
if (this->listeners_[i].characteristic == characteristic) {
// Swap with last element and pop
if (i != this->listeners_.size() - 1) {
this->listeners_[i] = this->listeners_.back();
}
this->listeners_.pop_back();
return;
}
}
} }
} // namespace esp32_ble_server_automations } // namespace esp32_ble_server_automations

View File

@@ -8,7 +8,6 @@
#include "esphome/core/automation.h" #include "esphome/core/automation.h"
#include <vector> #include <vector>
#include <unordered_map>
#include <functional> #include <functional>
#ifdef USE_ESP32 #ifdef USE_ESP32
@@ -46,14 +45,27 @@ class BLECharacteristicSetValueActionManager
void set_listener(BLECharacteristic *characteristic, EventEmitterListenerID listener_id, void set_listener(BLECharacteristic *characteristic, EventEmitterListenerID listener_id,
const std::function<void()> &pre_notify_listener); const std::function<void()> &pre_notify_listener);
EventEmitterListenerID get_listener(BLECharacteristic *characteristic) { EventEmitterListenerID get_listener(BLECharacteristic *characteristic) {
return this->listeners_[characteristic].first; for (const auto &entry : this->listeners_) {
if (entry.characteristic == characteristic) {
return entry.listener_id;
}
}
return 0;
} }
void emit_pre_notify(BLECharacteristic *characteristic) { void emit_pre_notify(BLECharacteristic *characteristic) {
this->emit_(BLECharacteristicSetValueActionEvt::PRE_NOTIFY, characteristic); this->emit_(BLECharacteristicSetValueActionEvt::PRE_NOTIFY, characteristic);
} }
private: private:
std::unordered_map<BLECharacteristic *, std::pair<EventEmitterListenerID, EventEmitterListenerID>> listeners_; struct ListenerEntry {
BLECharacteristic *characteristic;
EventEmitterListenerID listener_id;
EventEmitterListenerID pre_notify_listener_id;
};
std::vector<ListenerEntry> listeners_;
ListenerEntry *find_listener_(BLECharacteristic *characteristic);
void remove_listener_(BLECharacteristic *characteristic);
}; };
template<typename... Ts> class BLECharacteristicSetValueAction : public Action<Ts...> { template<typename... Ts> class BLECharacteristicSetValueAction : public Action<Ts...> {