Remove parsed advertisement support from bluetooth_proxy to save memory (#9489)

This commit is contained in:
J. Nick Koston 2025-07-15 15:19:03 -10:00 committed by GitHub
parent bfaf2547e3
commit 30c4b91697
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 113 additions and 31 deletions

View File

@ -85,13 +85,13 @@ async def to_code(config):
await cg.register_component(var, config) await cg.register_component(var, config)
cg.add(var.set_active(config[CONF_ACTIVE])) cg.add(var.set_active(config[CONF_ACTIVE]))
await esp32_ble_tracker.register_ble_device(var, config) await esp32_ble_tracker.register_raw_ble_device(var, config)
for connection_conf in config.get(CONF_CONNECTIONS, []): for connection_conf in config.get(CONF_CONNECTIONS, []):
connection_var = cg.new_Pvariable(connection_conf[CONF_ID]) connection_var = cg.new_Pvariable(connection_conf[CONF_ID])
await cg.register_component(connection_var, connection_conf) await cg.register_component(connection_var, connection_conf)
cg.add(var.register_connection(connection_var)) cg.add(var.register_connection(connection_var))
await esp32_ble_tracker.register_client(connection_var, connection_conf) await esp32_ble_tracker.register_raw_client(connection_var, connection_conf)
if config.get(CONF_CACHE_SERVICES): if config.get(CONF_CACHE_SERVICES):
add_idf_sdkconfig_option("CONFIG_BT_GATTC_CACHE_NVS_FLASH", True) add_idf_sdkconfig_option("CONFIG_BT_GATTC_CACHE_NVS_FLASH", True)

View File

@ -42,15 +42,13 @@ void BluetoothProxy::send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerSta
this->api_connection_->send_message(resp); this->api_connection_->send_message(resp);
} }
#ifdef USE_ESP32_BLE_DEVICE
bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || this->raw_advertisements_) // This method should never be called since bluetooth_proxy always uses raw advertisements
// but we need to provide an implementation to satisfy the virtual method requirement
return false; return false;
ESP_LOGV(TAG, "Proxying packet from %s - %s. RSSI: %d dB", device.get_name().c_str(), device.address_str().c_str(),
device.get_rssi());
this->send_api_packet_(device);
return true;
} }
#endif
// Batch size for BLE advertisements to maximize WiFi efficiency // Batch size for BLE advertisements to maximize WiFi efficiency
// Each advertisement is up to 80 bytes when packaged (including protocol overhead) // Each advertisement is up to 80 bytes when packaged (including protocol overhead)
@ -69,7 +67,7 @@ std::vector<api::BluetoothLERawAdvertisement> batch_buffer;
static std::vector<api::BluetoothLERawAdvertisement> &get_batch_buffer() { return batch_buffer; } static std::vector<api::BluetoothLERawAdvertisement> &get_batch_buffer() { return batch_buffer; }
bool BluetoothProxy::parse_devices(const esp32_ble::BLEScanResult *scan_results, size_t count) { bool BluetoothProxy::parse_devices(const esp32_ble::BLEScanResult *scan_results, size_t count) {
if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || !this->raw_advertisements_) if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr)
return false; return false;
// Get the batch buffer reference // Get the batch buffer reference
@ -116,6 +114,7 @@ void BluetoothProxy::flush_pending_advertisements() {
this->api_connection_->send_message(resp); this->api_connection_->send_message(resp);
} }
#ifdef USE_ESP32_BLE_DEVICE
void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) { void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) {
api::BluetoothLEAdvertisementResponse resp; api::BluetoothLEAdvertisementResponse resp;
resp.address = device.address_uint64(); resp.address = device.address_uint64();
@ -153,14 +152,14 @@ void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &devi
this->api_connection_->send_message(resp); this->api_connection_->send_message(resp);
} }
#endif // USE_ESP32_BLE_DEVICE
void BluetoothProxy::dump_config() { void BluetoothProxy::dump_config() {
ESP_LOGCONFIG(TAG, "Bluetooth Proxy:"); ESP_LOGCONFIG(TAG, "Bluetooth Proxy:");
ESP_LOGCONFIG(TAG, ESP_LOGCONFIG(TAG,
" Active: %s\n" " Active: %s\n"
" Connections: %d\n" " Connections: %d",
" Raw advertisements: %s", YESNO(this->active_), this->connections_.size());
YESNO(this->active_), this->connections_.size(), YESNO(this->raw_advertisements_));
} }
int BluetoothProxy::get_bluetooth_connections_free() { int BluetoothProxy::get_bluetooth_connections_free() {
@ -188,7 +187,6 @@ void BluetoothProxy::loop() {
} }
// Flush any pending BLE advertisements that have been accumulated but not yet sent // Flush any pending BLE advertisements that have been accumulated but not yet sent
if (this->raw_advertisements_) {
static uint32_t last_flush_time = 0; static uint32_t last_flush_time = 0;
uint32_t now = App.get_loop_component_start_time(); uint32_t now = App.get_loop_component_start_time();
@ -197,7 +195,6 @@ void BluetoothProxy::loop() {
this->flush_pending_advertisements(); this->flush_pending_advertisements();
last_flush_time = now; last_flush_time = now;
} }
}
for (auto *connection : this->connections_) { for (auto *connection : this->connections_) {
if (connection->send_service_ == connection->service_count_) { if (connection->send_service_ == connection->service_count_) {
connection->send_service_ = DONE_SENDING_SERVICES; connection->send_service_ = DONE_SENDING_SERVICES;
@ -318,9 +315,7 @@ void BluetoothProxy::loop() {
} }
esp32_ble_tracker::AdvertisementParserType BluetoothProxy::get_advertisement_parser_type() { esp32_ble_tracker::AdvertisementParserType BluetoothProxy::get_advertisement_parser_type() {
if (this->raw_advertisements_)
return esp32_ble_tracker::AdvertisementParserType::RAW_ADVERTISEMENTS; return esp32_ble_tracker::AdvertisementParserType::RAW_ADVERTISEMENTS;
return esp32_ble_tracker::AdvertisementParserType::PARSED_ADVERTISEMENTS;
} }
BluetoothConnection *BluetoothProxy::get_connection_(uint64_t address, bool reserve) { BluetoothConnection *BluetoothProxy::get_connection_(uint64_t address, bool reserve) {
@ -565,7 +560,6 @@ void BluetoothProxy::subscribe_api_connection(api::APIConnection *api_connection
return; return;
} }
this->api_connection_ = api_connection; this->api_connection_ = api_connection;
this->raw_advertisements_ = flags & BluetoothProxySubscriptionFlag::SUBSCRIPTION_RAW_ADVERTISEMENTS;
this->parent_->recalculate_advertisement_parser_types(); this->parent_->recalculate_advertisement_parser_types();
this->send_bluetooth_scanner_state_(this->parent_->get_scanner_state()); this->send_bluetooth_scanner_state_(this->parent_->get_scanner_state());
@ -577,7 +571,6 @@ void BluetoothProxy::unsubscribe_api_connection(api::APIConnection *api_connecti
return; return;
} }
this->api_connection_ = nullptr; this->api_connection_ = nullptr;
this->raw_advertisements_ = false;
this->parent_->recalculate_advertisement_parser_types(); this->parent_->recalculate_advertisement_parser_types();
} }

View File

@ -51,7 +51,9 @@ enum BluetoothProxySubscriptionFlag : uint32_t {
class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Component { class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Component {
public: public:
BluetoothProxy(); BluetoothProxy();
#ifdef USE_ESP32_BLE_DEVICE
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
#endif
bool parse_devices(const esp32_ble::BLEScanResult *scan_results, size_t count) override; bool parse_devices(const esp32_ble::BLEScanResult *scan_results, size_t count) override;
void dump_config() override; void dump_config() override;
void setup() override; void setup() override;
@ -129,7 +131,9 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
} }
protected: protected:
#ifdef USE_ESP32_BLE_DEVICE
void send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device); void send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device);
#endif
void send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerState state); void send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerState state);
BluetoothConnection *get_connection_(uint64_t address, bool reserve); BluetoothConnection *get_connection_(uint64_t address, bool reserve);
@ -143,8 +147,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
// Group 3: 1-byte types grouped together // Group 3: 1-byte types grouped together
bool active_; bool active_;
bool raw_advertisements_{false}; // 1 byte used, 3 bytes padding
// 2 bytes used, 2 bytes padding
}; };
extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)

View File

@ -105,6 +105,7 @@ void BLEClientBase::dump_config() {
} }
} }
#ifdef USE_ESP32_BLE_DEVICE
bool BLEClientBase::parse_device(const espbt::ESPBTDevice &device) { bool BLEClientBase::parse_device(const espbt::ESPBTDevice &device) {
if (!this->auto_connect_) if (!this->auto_connect_)
return false; return false;
@ -122,6 +123,7 @@ bool BLEClientBase::parse_device(const espbt::ESPBTDevice &device) {
this->remote_addr_type_ = device.get_address_type(); this->remote_addr_type_ = device.get_address_type();
return true; return true;
} }
#endif
void BLEClientBase::connect() { void BLEClientBase::connect() {
ESP_LOGI(TAG, "[%d] [%s] 0x%02x Attempting BLE connection", this->connection_index_, this->address_str_.c_str(), ESP_LOGI(TAG, "[%d] [%s] 0x%02x Attempting BLE connection", this->connection_index_, this->address_str_.c_str(),

View File

@ -31,7 +31,9 @@ class BLEClientBase : public espbt::ESPBTClient, public Component {
void dump_config() override; void dump_config() override;
void run_later(std::function<void()> &&f); // NOLINT void run_later(std::function<void()> &&f); // NOLINT
#ifdef USE_ESP32_BLE_DEVICE
bool parse_device(const espbt::ESPBTDevice &device) override; bool parse_device(const espbt::ESPBTDevice &device) override;
#endif
void on_scan_end() override {} void on_scan_end() override {}
bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override; esp_ble_gattc_cb_param_t *param) override;

View File

@ -31,6 +31,8 @@ from esphome.const import (
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
) )
from esphome.core import CORE from esphome.core import CORE
from esphome.enum import StrEnum
from esphome.types import ConfigType
AUTO_LOAD = ["esp32_ble"] AUTO_LOAD = ["esp32_ble"]
DEPENDENCIES = ["esp32"] DEPENDENCIES = ["esp32"]
@ -50,6 +52,25 @@ IDF_MAX_CONNECTIONS = 9
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
# Enum for BLE features
class BLEFeatures(StrEnum):
ESP_BT_DEVICE = "ESP_BT_DEVICE"
# Set to track which features are needed by components
_required_features: set[BLEFeatures] = set()
def register_ble_features(features: set[BLEFeatures]) -> None:
"""Register BLE features that a component needs.
Args:
features: Set of BLEFeatures enum members
"""
_required_features.update(features)
esp32_ble_tracker_ns = cg.esphome_ns.namespace("esp32_ble_tracker") esp32_ble_tracker_ns = cg.esphome_ns.namespace("esp32_ble_tracker")
ESP32BLETracker = esp32_ble_tracker_ns.class_( ESP32BLETracker = esp32_ble_tracker_ns.class_(
"ESP32BLETracker", "ESP32BLETracker",
@ -277,6 +298,15 @@ async def to_code(config):
cg.add(var.set_scan_window(int(params[CONF_WINDOW].total_milliseconds / 0.625))) cg.add(var.set_scan_window(int(params[CONF_WINDOW].total_milliseconds / 0.625)))
cg.add(var.set_scan_active(params[CONF_ACTIVE])) cg.add(var.set_scan_active(params[CONF_ACTIVE]))
cg.add(var.set_scan_continuous(params[CONF_CONTINUOUS])) cg.add(var.set_scan_continuous(params[CONF_CONTINUOUS]))
# Register ESP_BT_DEVICE feature if any of the automation triggers are used
if (
config.get(CONF_ON_BLE_ADVERTISE)
or config.get(CONF_ON_BLE_SERVICE_DATA_ADVERTISE)
or config.get(CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE)
):
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, []):
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:
@ -334,6 +364,11 @@ async def to_code(config):
cg.add_define("USE_OTA_STATE_CALLBACK") # To be notified when an OTA update starts cg.add_define("USE_OTA_STATE_CALLBACK") # To be notified when an OTA update starts
cg.add_define("USE_ESP32_BLE_CLIENT") cg.add_define("USE_ESP32_BLE_CLIENT")
# Add feature-specific defines based on what's needed
if BLEFeatures.ESP_BT_DEVICE in _required_features:
cg.add_define("USE_ESP32_BLE_DEVICE")
if config.get(CONF_SOFTWARE_COEXISTENCE): if config.get(CONF_SOFTWARE_COEXISTENCE):
cg.add_define("USE_ESP32_BLE_SOFTWARE_COEXISTENCE") cg.add_define("USE_ESP32_BLE_SOFTWARE_COEXISTENCE")
@ -382,13 +417,43 @@ async def esp32_ble_tracker_stop_scan_action_to_code(
return var return var
async def register_ble_device(var, config): async def register_ble_device(
var: cg.SafeExpType, config: ConfigType
) -> cg.SafeExpType:
register_ble_features({BLEFeatures.ESP_BT_DEVICE})
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
async def register_client(var, config): async def register_client(var: cg.SafeExpType, config: ConfigType) -> cg.SafeExpType:
register_ble_features({BLEFeatures.ESP_BT_DEVICE})
paren = await cg.get_variable(config[CONF_ESP32_BLE_ID])
cg.add(paren.register_client(var))
return var
async def register_raw_ble_device(
var: cg.SafeExpType, config: ConfigType
) -> cg.SafeExpType:
"""Register a BLE device listener that only needs raw advertisement data.
This does NOT register the ESP_BT_DEVICE feature, meaning ESPBTDevice
will not be compiled in if this is the only registration method used.
"""
paren = await cg.get_variable(config[CONF_ESP32_BLE_ID])
cg.add(paren.register_listener(var))
return var
async def register_raw_client(
var: cg.SafeExpType, config: ConfigType
) -> cg.SafeExpType:
"""Register a BLE client that only needs raw advertisement data.
This does NOT register the ESP_BT_DEVICE feature, meaning ESPBTDevice
will not be compiled in if this is the only registration method used.
"""
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

@ -7,6 +7,7 @@
namespace esphome { namespace esphome {
namespace esp32_ble_tracker { namespace esp32_ble_tracker {
#ifdef USE_ESP32_BLE_DEVICE
class ESPBTAdvertiseTrigger : public Trigger<const ESPBTDevice &>, public ESPBTDeviceListener { class ESPBTAdvertiseTrigger : public Trigger<const ESPBTDevice &>, public ESPBTDeviceListener {
public: public:
explicit ESPBTAdvertiseTrigger(ESP32BLETracker *parent) { parent->register_listener(this); } explicit ESPBTAdvertiseTrigger(ESP32BLETracker *parent) { parent->register_listener(this); }
@ -87,6 +88,7 @@ class BLEEndOfScanTrigger : public Trigger<>, public ESPBTDeviceListener {
bool parse_device(const ESPBTDevice &device) override { return false; } bool parse_device(const ESPBTDevice &device) override { return false; }
void on_scan_end() override { this->trigger(); } void on_scan_end() override { this->trigger(); }
}; };
#endif // USE_ESP32_BLE_DEVICE
template<typename... Ts> class ESP32BLEStartScanAction : public Action<Ts...> { template<typename... Ts> class ESP32BLEStartScanAction : public Action<Ts...> {
public: public:

View File

@ -141,6 +141,7 @@ void ESP32BLETracker::loop() {
} }
if (this->parse_advertisements_) { if (this->parse_advertisements_) {
#ifdef USE_ESP32_BLE_DEVICE
ESPBTDevice device; ESPBTDevice device;
device.parse_scan_rst(scan_result); device.parse_scan_rst(scan_result);
@ -162,6 +163,7 @@ void ESP32BLETracker::loop() {
if (!found && !this->scan_continuous_) { if (!found && !this->scan_continuous_) {
this->print_bt_device_info(device); this->print_bt_device_info(device);
} }
#endif // USE_ESP32_BLE_DEVICE
} }
// Move to next entry in ring buffer // Move to next entry in ring buffer
@ -511,6 +513,7 @@ void ESP32BLETracker::set_scanner_state_(ScannerState state) {
this->scanner_state_callbacks_.call(state); this->scanner_state_callbacks_.call(state);
} }
#ifdef USE_ESP32_BLE_DEVICE
ESPBLEiBeacon::ESPBLEiBeacon(const uint8_t *data) { memcpy(&this->beacon_data_, data, sizeof(beacon_data_)); } ESPBLEiBeacon::ESPBLEiBeacon(const uint8_t *data) { memcpy(&this->beacon_data_, data, sizeof(beacon_data_)); }
optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const ServiceData &data) { optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const ServiceData &data) {
if (!data.uuid.contains(0x4C, 0x00)) if (!data.uuid.contains(0x4C, 0x00))
@ -751,13 +754,16 @@ void ESPBTDevice::parse_adv_(const uint8_t *payload, uint8_t len) {
} }
} }
} }
std::string ESPBTDevice::address_str() const { std::string ESPBTDevice::address_str() const {
char mac[24]; char mac[24];
snprintf(mac, sizeof(mac), "%02X:%02X:%02X:%02X:%02X:%02X", this->address_[0], this->address_[1], this->address_[2], snprintf(mac, sizeof(mac), "%02X:%02X:%02X:%02X:%02X:%02X", this->address_[0], this->address_[1], this->address_[2],
this->address_[3], this->address_[4], this->address_[5]); this->address_[3], this->address_[4], this->address_[5]);
return mac; return mac;
} }
uint64_t ESPBTDevice::address_uint64() const { return esp32_ble::ble_addr_to_uint64(this->address_); } uint64_t ESPBTDevice::address_uint64() const { return esp32_ble::ble_addr_to_uint64(this->address_); }
#endif // USE_ESP32_BLE_DEVICE
void ESP32BLETracker::dump_config() { void ESP32BLETracker::dump_config() {
ESP_LOGCONFIG(TAG, "BLE Tracker:"); ESP_LOGCONFIG(TAG, "BLE Tracker:");
@ -796,6 +802,7 @@ void ESP32BLETracker::dump_config() {
} }
} }
#ifdef USE_ESP32_BLE_DEVICE
void ESP32BLETracker::print_bt_device_info(const ESPBTDevice &device) { void ESP32BLETracker::print_bt_device_info(const ESPBTDevice &device) {
const uint64_t address = device.address_uint64(); const uint64_t address = device.address_uint64();
for (auto &disc : this->already_discovered_) { for (auto &disc : this->already_discovered_) {
@ -866,8 +873,9 @@ bool ESPBTDevice::resolve_irk(const uint8_t *irk) const {
return ecb_ciphertext[15] == (addr64 & 0xff) && ecb_ciphertext[14] == ((addr64 >> 8) & 0xff) && return ecb_ciphertext[15] == (addr64 & 0xff) && ecb_ciphertext[14] == ((addr64 >> 8) & 0xff) &&
ecb_ciphertext[13] == ((addr64 >> 16) & 0xff); ecb_ciphertext[13] == ((addr64 >> 16) & 0xff);
} }
#endif // USE_ESP32_BLE_DEVICE
} // namespace esp32_ble_tracker } // namespace esp32_ble_tracker
} // namespace esphome } // namespace esphome
#endif #endif // USE_ESP32

View File

@ -39,6 +39,7 @@ struct ServiceData {
adv_data_t data; adv_data_t data;
}; };
#ifdef USE_ESP32_BLE_DEVICE
class ESPBLEiBeacon { class ESPBLEiBeacon {
public: public:
ESPBLEiBeacon() { memset(&this->beacon_data_, 0, sizeof(this->beacon_data_)); } ESPBLEiBeacon() { memset(&this->beacon_data_, 0, sizeof(this->beacon_data_)); }
@ -116,13 +117,16 @@ class ESPBTDevice {
std::vector<ServiceData> service_datas_{}; std::vector<ServiceData> service_datas_{};
const BLEScanResult *scan_result_{nullptr}; const BLEScanResult *scan_result_{nullptr};
}; };
#endif // USE_ESP32_BLE_DEVICE
class ESP32BLETracker; class ESP32BLETracker;
class ESPBTDeviceListener { class ESPBTDeviceListener {
public: public:
virtual void on_scan_end() {} virtual void on_scan_end() {}
#ifdef USE_ESP32_BLE_DEVICE
virtual bool parse_device(const ESPBTDevice &device) = 0; virtual bool parse_device(const ESPBTDevice &device) = 0;
#endif
virtual bool parse_devices(const BLEScanResult *scan_results, size_t count) { return false; }; virtual bool parse_devices(const BLEScanResult *scan_results, size_t count) { return false; };
virtual AdvertisementParserType get_advertisement_parser_type() { virtual AdvertisementParserType get_advertisement_parser_type() {
return AdvertisementParserType::PARSED_ADVERTISEMENTS; return AdvertisementParserType::PARSED_ADVERTISEMENTS;
@ -237,7 +241,9 @@ class ESP32BLETracker : public Component,
void register_client(ESPBTClient *client); void register_client(ESPBTClient *client);
void recalculate_advertisement_parser_types(); void recalculate_advertisement_parser_types();
#ifdef USE_ESP32_BLE_DEVICE
void print_bt_device_info(const ESPBTDevice &device); void print_bt_device_info(const ESPBTDevice &device);
#endif
void start_scan(); void start_scan();
void stop_scan(); void stop_scan();

View File

@ -145,6 +145,7 @@
#define USE_CAPTIVE_PORTAL #define USE_CAPTIVE_PORTAL
#define USE_ESP32_BLE #define USE_ESP32_BLE
#define USE_ESP32_BLE_CLIENT #define USE_ESP32_BLE_CLIENT
#define USE_ESP32_BLE_DEVICE
#define USE_ESP32_BLE_SERVER #define USE_ESP32_BLE_SERVER
#define USE_I2C #define USE_I2C
#define USE_IMPROV #define USE_IMPROV