[esp32_ble] Add stack-based UUID formatting to avoid heap allocations (#12510)

This commit is contained in:
J. Nick Koston
2025-12-19 11:18:32 -10:00
committed by GitHub
parent ebc3d28ade
commit 81e91c2a8f
9 changed files with 68 additions and 23 deletions
+10 -7
View File
@@ -143,9 +143,8 @@ bool ESPBTUUID::operator==(const ESPBTUUID &uuid) const {
return this->as_128bit() == uuid.as_128bit(); return this->as_128bit() == uuid.as_128bit();
} }
esp_bt_uuid_t ESPBTUUID::get_uuid() const { return this->uuid_; } esp_bt_uuid_t ESPBTUUID::get_uuid() const { return this->uuid_; }
std::string ESPBTUUID::to_string() const { void ESPBTUUID::to_str(std::span<char, UUID_STR_LEN> output) const {
char buf[40]; // Enough for 128-bit UUID with dashes char *pos = output.data();
char *pos = buf;
switch (this->uuid_.len) { switch (this->uuid_.len) {
case ESP_UUID_LEN_16: case ESP_UUID_LEN_16:
@@ -156,7 +155,7 @@ std::string ESPBTUUID::to_string() const {
*pos++ = format_hex_pretty_char((this->uuid_.uuid.uuid16 >> 4) & 0x0F); *pos++ = format_hex_pretty_char((this->uuid_.uuid.uuid16 >> 4) & 0x0F);
*pos++ = format_hex_pretty_char(this->uuid_.uuid.uuid16 & 0x0F); *pos++ = format_hex_pretty_char(this->uuid_.uuid.uuid16 & 0x0F);
*pos = '\0'; *pos = '\0';
return std::string(buf); return;
case ESP_UUID_LEN_32: case ESP_UUID_LEN_32:
*pos++ = '0'; *pos++ = '0';
@@ -165,7 +164,7 @@ std::string ESPBTUUID::to_string() const {
*pos++ = format_hex_pretty_char((this->uuid_.uuid.uuid32 >> shift) & 0x0F); *pos++ = format_hex_pretty_char((this->uuid_.uuid.uuid32 >> shift) & 0x0F);
} }
*pos = '\0'; *pos = '\0';
return std::string(buf); return;
default: default:
case ESP_UUID_LEN_128: case ESP_UUID_LEN_128:
@@ -179,9 +178,13 @@ std::string ESPBTUUID::to_string() const {
} }
} }
*pos = '\0'; *pos = '\0';
return std::string(buf); return;
} }
return ""; }
std::string ESPBTUUID::to_string() const {
char buf[UUID_STR_LEN];
this->to_str(buf);
return std::string(buf);
} }
} // namespace esphome::esp32_ble } // namespace esphome::esp32_ble
+5
View File
@@ -7,11 +7,15 @@
#ifdef USE_ESP32 #ifdef USE_ESP32
#ifdef USE_ESP32_BLE_UUID #ifdef USE_ESP32_BLE_UUID
#include <span>
#include <string> #include <string>
#include <esp_bt_defs.h> #include <esp_bt_defs.h>
namespace esphome::esp32_ble { namespace esphome::esp32_ble {
/// Buffer size for UUID string: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\0"
static constexpr size_t UUID_STR_LEN = 37;
class ESPBTUUID { class ESPBTUUID {
public: public:
ESPBTUUID(); ESPBTUUID();
@@ -37,6 +41,7 @@ class ESPBTUUID {
esp_bt_uuid_t get_uuid() const; esp_bt_uuid_t get_uuid() const;
std::string to_string() const; std::string to_string() const;
void to_str(std::span<char, UUID_STR_LEN> output) const;
protected: protected:
esp_bt_uuid_t uuid_; esp_bt_uuid_t uuid_;
@@ -50,8 +50,12 @@ void BLECharacteristic::parse_descriptors() {
desc->handle = result.handle; desc->handle = result.handle;
desc->characteristic = this; desc->characteristic = this;
this->descriptors.push_back(desc); this->descriptors.push_back(desc);
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
char uuid_buf[espbt::UUID_STR_LEN];
desc->uuid.to_str(uuid_buf);
ESP_LOGV(TAG, "[%d] [%s] descriptor %s, handle 0x%x", this->service->client->get_connection_index(), ESP_LOGV(TAG, "[%d] [%s] descriptor %s, handle 0x%x", this->service->client->get_connection_index(),
this->service->client->address_str(), desc->uuid.to_string().c_str(), desc->handle); this->service->client->address_str(), uuid_buf, desc->handle);
#endif
offset++; offset++;
} }
} }
@@ -411,12 +411,15 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
this->update_conn_params_(MEDIUM_MIN_CONN_INTERVAL, MEDIUM_MAX_CONN_INTERVAL, 0, MEDIUM_CONN_TIMEOUT, "medium"); this->update_conn_params_(MEDIUM_MIN_CONN_INTERVAL, MEDIUM_MAX_CONN_INTERVAL, 0, MEDIUM_CONN_TIMEOUT, "medium");
} else if (this->connection_type_ != espbt::ConnectionType::V3_WITH_CACHE) { } else if (this->connection_type_ != espbt::ConnectionType::V3_WITH_CACHE) {
#ifdef USE_ESP32_BLE_DEVICE #ifdef USE_ESP32_BLE_DEVICE
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
for (auto &svc : this->services_) { for (auto &svc : this->services_) {
ESP_LOGV(TAG, "[%d] [%s] Service UUID: %s", this->connection_index_, this->address_str_, char uuid_buf[espbt::UUID_STR_LEN];
svc->uuid.to_string().c_str()); svc->uuid.to_str(uuid_buf);
ESP_LOGV(TAG, "[%d] [%s] Service UUID: %s", this->connection_index_, this->address_str_, uuid_buf);
ESP_LOGV(TAG, "[%d] [%s] start_handle: 0x%x end_handle: 0x%x", this->connection_index_, this->address_str_, ESP_LOGV(TAG, "[%d] [%s] start_handle: 0x%x end_handle: 0x%x", this->connection_index_, this->address_str_,
svc->start_handle, svc->end_handle); svc->start_handle, svc->end_handle);
} }
#endif
#endif #endif
} }
ESP_LOGI(TAG, "[%d] [%s] Service discovery complete", this->connection_index_, this->address_str_); ESP_LOGI(TAG, "[%d] [%s] Service discovery complete", this->connection_index_, this->address_str_);
@@ -64,9 +64,12 @@ void BLEService::parse_characteristics() {
characteristic->handle = result.char_handle; characteristic->handle = result.char_handle;
characteristic->service = this; characteristic->service = this;
this->characteristics.push_back(characteristic); this->characteristics.push_back(characteristic);
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
char uuid_buf[espbt::UUID_STR_LEN];
characteristic->uuid.to_str(uuid_buf);
ESP_LOGV(TAG, "[%d] [%s] characteristic %s, handle 0x%x, properties 0x%x", this->client->get_connection_index(), ESP_LOGV(TAG, "[%d] [%s] characteristic %s, handle 0x%x, properties 0x%x", this->client->get_connection_index(),
this->client->address_str(), characteristic->uuid.to_string().c_str(), characteristic->handle, this->client->address_str(), uuid_buf, characteristic->handle, characteristic->properties);
characteristic->properties); #endif
offset++; offset++;
} }
} }
@@ -109,7 +109,11 @@ void BLECharacteristic::do_create(BLEService *service) {
esp_attr_control_t control; esp_attr_control_t control;
control.auto_rsp = ESP_GATT_RSP_BY_APP; control.auto_rsp = ESP_GATT_RSP_BY_APP;
ESP_LOGV(TAG, "Creating characteristic - %s", this->uuid_.to_string().c_str()); #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
char uuid_buf[esp32_ble::UUID_STR_LEN];
this->uuid_.to_str(uuid_buf);
ESP_LOGV(TAG, "Creating characteristic - %s", uuid_buf);
#endif
esp_bt_uuid_t uuid = this->uuid_.get_uuid(); esp_bt_uuid_t uuid = this->uuid_.get_uuid();
esp_err_t err = esp_ble_gatts_add_char(service->get_handle(), &uuid, static_cast<esp_gatt_perm_t>(this->permissions_), esp_err_t err = esp_ble_gatts_add_char(service->get_handle(), &uuid, static_cast<esp_gatt_perm_t>(this->permissions_),
@@ -34,7 +34,11 @@ void BLEDescriptor::do_create(BLECharacteristic *characteristic) {
esp_attr_control_t control; esp_attr_control_t control;
control.auto_rsp = ESP_GATT_AUTO_RSP; control.auto_rsp = ESP_GATT_AUTO_RSP;
ESP_LOGV(TAG, "Creating descriptor - %s", this->uuid_.to_string().c_str()); #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
char uuid_buf[esp32_ble::UUID_STR_LEN];
this->uuid_.to_str(uuid_buf);
ESP_LOGV(TAG, "Creating descriptor - %s", uuid_buf);
#endif
esp_bt_uuid_t uuid = this->uuid_.get_uuid(); esp_bt_uuid_t uuid = this->uuid_.get_uuid();
esp_err_t err = esp_ble_gatts_add_char_descr(this->characteristic_->get_service()->get_handle(), &uuid, esp_err_t err = esp_ble_gatts_add_char_descr(this->characteristic_->get_service()->get_handle(), &uuid,
this->permissions_, &this->value_, &control); this->permissions_, &this->value_, &control);
@@ -106,7 +106,11 @@ void BLEServer::restart_advertising_() {
} }
BLEService *BLEServer::create_service(ESPBTUUID uuid, bool advertise, uint16_t num_handles) { BLEService *BLEServer::create_service(ESPBTUUID uuid, bool advertise, uint16_t num_handles) {
ESP_LOGV(TAG, "Creating BLE service - %s", uuid.to_string().c_str()); #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
char uuid_buf[esp32_ble::UUID_STR_LEN];
uuid.to_str(uuid_buf);
ESP_LOGV(TAG, "Creating BLE service - %s", uuid_buf);
#endif
// Calculate the inst_id for the service // Calculate the inst_id for the service
uint8_t inst_id = 0; uint8_t inst_id = 0;
for (; inst_id < 0xFF; inst_id++) { for (; inst_id < 0xFF; inst_id++) {
@@ -115,7 +119,9 @@ BLEService *BLEServer::create_service(ESPBTUUID uuid, bool advertise, uint16_t n
} }
} }
if (inst_id == 0xFF) { if (inst_id == 0xFF) {
ESP_LOGW(TAG, "Could not create BLE service %s, too many instances", uuid.to_string().c_str()); char warn_uuid_buf[esp32_ble::UUID_STR_LEN];
uuid.to_str(warn_uuid_buf);
ESP_LOGW(TAG, "Could not create BLE service %s, too many instances", warn_uuid_buf);
return nullptr; return nullptr;
} }
BLEService *service = // NOLINT(cppcoreguidelines-owning-memory) BLEService *service = // NOLINT(cppcoreguidelines-owning-memory)
@@ -128,7 +134,11 @@ BLEService *BLEServer::create_service(ESPBTUUID uuid, bool advertise, uint16_t n
} }
void BLEServer::remove_service(ESPBTUUID uuid, uint8_t inst_id) { void BLEServer::remove_service(ESPBTUUID uuid, uint8_t inst_id) {
ESP_LOGV(TAG, "Removing BLE service - %s %d", uuid.to_string().c_str(), inst_id); #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
char uuid_buf[esp32_ble::UUID_STR_LEN];
uuid.to_str(uuid_buf);
ESP_LOGV(TAG, "Removing BLE service - %s %d", uuid_buf, inst_id);
#endif
for (auto it = this->services_.begin(); it != this->services_.end(); ++it) { for (auto it = this->services_.begin(); it != this->services_.end(); ++it) {
if (it->uuid == uuid && it->inst_id == inst_id) { if (it->uuid == uuid && it->inst_id == inst_id) {
it->service->do_delete(); it->service->do_delete();
@@ -137,7 +147,9 @@ void BLEServer::remove_service(ESPBTUUID uuid, uint8_t inst_id) {
return; return;
} }
} }
ESP_LOGW(TAG, "BLE service %s %d does not exist", uuid.to_string().c_str(), inst_id); char warn_uuid_buf[esp32_ble::UUID_STR_LEN];
uuid.to_str(warn_uuid_buf);
ESP_LOGW(TAG, "BLE service %s %d does not exist", warn_uuid_buf, inst_id);
} }
BLEService *BLEServer::get_service(ESPBTUUID uuid, uint8_t inst_id) { BLEService *BLEServer::get_service(ESPBTUUID uuid, uint8_t inst_id) {
@@ -438,24 +438,31 @@ void ESPBTDevice::parse_scan_rst(const BLEScanResult &scan_result) {
ESP_LOGVV(TAG, " Ad Flag: %u", *this->ad_flag_); ESP_LOGVV(TAG, " Ad Flag: %u", *this->ad_flag_);
} }
for (auto &uuid : this->service_uuids_) { for (auto &uuid : this->service_uuids_) {
ESP_LOGVV(TAG, " Service UUID: %s", uuid.to_string().c_str()); char uuid_buf[esp32_ble::UUID_STR_LEN];
uuid.to_str(uuid_buf);
ESP_LOGVV(TAG, " Service UUID: %s", uuid_buf);
} }
for (auto &data : this->manufacturer_datas_) { for (auto &data : this->manufacturer_datas_) {
auto ibeacon = ESPBLEiBeacon::from_manufacturer_data(data); auto ibeacon = ESPBLEiBeacon::from_manufacturer_data(data);
if (ibeacon.has_value()) { if (ibeacon.has_value()) {
ESP_LOGVV(TAG, " Manufacturer iBeacon:"); ESP_LOGVV(TAG, " Manufacturer iBeacon:");
ESP_LOGVV(TAG, " UUID: %s", ibeacon.value().get_uuid().to_string().c_str()); char uuid_buf[esp32_ble::UUID_STR_LEN];
ibeacon.value().get_uuid().to_str(uuid_buf);
ESP_LOGVV(TAG, " UUID: %s", uuid_buf);
ESP_LOGVV(TAG, " Major: %u", ibeacon.value().get_major()); ESP_LOGVV(TAG, " Major: %u", ibeacon.value().get_major());
ESP_LOGVV(TAG, " Minor: %u", ibeacon.value().get_minor()); ESP_LOGVV(TAG, " Minor: %u", ibeacon.value().get_minor());
ESP_LOGVV(TAG, " TXPower: %d", ibeacon.value().get_signal_power()); ESP_LOGVV(TAG, " TXPower: %d", ibeacon.value().get_signal_power());
} else { } else {
ESP_LOGVV(TAG, " Manufacturer ID: %s, data: %s", data.uuid.to_string().c_str(), char uuid_buf[esp32_ble::UUID_STR_LEN];
format_hex_pretty(data.data).c_str()); data.uuid.to_str(uuid_buf);
ESP_LOGVV(TAG, " Manufacturer ID: %s, data: %s", uuid_buf, format_hex_pretty(data.data).c_str());
} }
} }
for (auto &data : this->service_datas_) { for (auto &data : this->service_datas_) {
ESP_LOGVV(TAG, " Service data:"); ESP_LOGVV(TAG, " Service data:");
ESP_LOGVV(TAG, " UUID: %s", data.uuid.to_string().c_str()); char uuid_buf[esp32_ble::UUID_STR_LEN];
data.uuid.to_str(uuid_buf);
ESP_LOGVV(TAG, " UUID: %s", uuid_buf);
ESP_LOGVV(TAG, " Data: %s", format_hex_pretty(data.data).c_str()); ESP_LOGVV(TAG, " Data: %s", format_hex_pretty(data.data).c_str());
} }