From 0e66c899ce57b304a7697bf4859806e78a07325c Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 2 Nov 2022 23:02:33 +1300 Subject: [PATCH] Allow multiple bluetooth proxy connections (#3971) --- esphome/components/api/api_server.h | 2 +- esphome/components/ble_client/ble_client.cpp | 6 +- esphome/components/ble_client/ble_client.h | 2 +- .../components/bluetooth_proxy/__init__.py | 54 ++- .../bluetooth_proxy/bluetooth_connection.cpp | 281 +++++++++++++ .../bluetooth_proxy/bluetooth_connection.h | 34 ++ .../bluetooth_proxy/bluetooth_proxy.cpp | 379 +++++------------- .../bluetooth_proxy/bluetooth_proxy.h | 28 +- .../esp32_ble_client/ble_characteristic.cpp | 11 +- .../esp32_ble_client/ble_client_base.cpp | 113 +++--- .../esp32_ble_client/ble_client_base.h | 28 +- .../esp32_ble_client/ble_service.cpp | 10 +- .../esp32_ble_tracker/esp32_ble_tracker.h | 2 +- 13 files changed, 601 insertions(+), 349 deletions(-) create mode 100644 esphome/components/bluetooth_proxy/bluetooth_connection.cpp create mode 100644 esphome/components/bluetooth_proxy/bluetooth_connection.h diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index ec8c54a269..1aefa31400 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -75,7 +75,7 @@ class APIServer : public Component, public Controller { void send_homeassistant_service_call(const HomeassistantServiceResponse &call); #ifdef USE_BLUETOOTH_PROXY void send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &call); - void send_bluetooth_device_connection(uint64_t address, bool connected, uint16_t mtu, esp_err_t error = ESP_OK); + void send_bluetooth_device_connection(uint64_t address, bool connected, uint16_t mtu = 0, esp_err_t error = ESP_OK); void send_bluetooth_connections_free(uint8_t free, uint8_t limit); void send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &call); void send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &call); diff --git a/esphome/components/ble_client/ble_client.cpp b/esphome/components/ble_client/ble_client.cpp index a757d6f903..f3a9f01a1a 100644 --- a/esphome/components/ble_client/ble_client.cpp +++ b/esphome/components/ble_client/ble_client.cpp @@ -47,11 +47,12 @@ void BLEClient::set_enabled(bool enabled) { this->enabled = enabled; } -void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t esp_gattc_if, +bool BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t esp_gattc_if, esp_ble_gattc_cb_param_t *param) { bool all_established = this->all_nodes_established_(); - BLEClientBase::gattc_event_handler(event, esp_gattc_if, param); + if (!BLEClientBase::gattc_event_handler(event, esp_gattc_if, param)) + return false; for (auto *node : this->nodes_) node->gattc_event_handler(event, esp_gattc_if, param); @@ -62,6 +63,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es delete svc; // NOLINT(cppcoreguidelines-owning-memory) this->services_.clear(); } + return true; } void BLEClient::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { diff --git a/esphome/components/ble_client/ble_client.h b/esphome/components/ble_client/ble_client.h index 177cab2e3c..54f5f298fc 100644 --- a/esphome/components/ble_client/ble_client.h +++ b/esphome/components/ble_client/ble_client.h @@ -50,7 +50,7 @@ class BLEClient : public BLEClientBase { void dump_config() override; void loop() override; - void 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; void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override; diff --git a/esphome/components/bluetooth_proxy/__init__.py b/esphome/components/bluetooth_proxy/__init__.py index 84841d9bf4..9c566c56a5 100644 --- a/esphome/components/bluetooth_proxy/__init__.py +++ b/esphome/components/bluetooth_proxy/__init__.py @@ -7,19 +7,56 @@ AUTO_LOAD = ["esp32_ble_client", "esp32_ble_tracker"] DEPENDENCIES = ["api", "esp32"] CODEOWNERS = ["@jesserockz"] +CONF_CONNECTIONS = "connections" +MAX_CONNECTIONS = 3 bluetooth_proxy_ns = cg.esphome_ns.namespace("bluetooth_proxy") BluetoothProxy = bluetooth_proxy_ns.class_( - "BluetoothProxy", esp32_ble_client.BLEClientBase + "BluetoothProxy", esp32_ble_tracker.ESPBTDeviceListener, cg.Component +) +BluetoothConnection = bluetooth_proxy_ns.class_( + "BluetoothConnection", esp32_ble_client.BLEClientBase ) -CONFIG_SCHEMA = cv.Schema( +CONNECTION_SCHEMA = esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA.extend( { - cv.GenerateID(): cv.declare_id(BluetoothProxy), - cv.Optional(CONF_ACTIVE, default=False): cv.boolean, + cv.GenerateID(): cv.declare_id(BluetoothConnection), } -).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) +).extend(cv.COMPONENT_SCHEMA) + + +def validate_connections(config): + if CONF_CONNECTIONS in config: + if not config[CONF_ACTIVE]: + raise cv.Invalid( + "Connections can only be used if the proxy is set to active" + ) + else: + if config[CONF_ACTIVE]: + conf = config.copy() + conf[CONF_CONNECTIONS] = [ + CONNECTION_SCHEMA({}) for _ in range(MAX_CONNECTIONS) + ] + return conf + return config + + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(BluetoothProxy), + cv.Optional(CONF_ACTIVE, default=False): cv.boolean, + cv.Optional(CONF_CONNECTIONS): cv.All( + cv.ensure_list(CONNECTION_SCHEMA), + cv.Length(min=1, max=MAX_CONNECTIONS), + ), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA), + validate_connections, +) async def to_code(config): @@ -27,7 +64,12 @@ async def to_code(config): await cg.register_component(var, config) cg.add(var.set_active(config[CONF_ACTIVE])) + await esp32_ble_tracker.register_ble_device(var, config) - await esp32_ble_tracker.register_client(var, config) + for connection_conf in config.get(CONF_CONNECTIONS, []): + connection_var = cg.new_Pvariable(connection_conf[CONF_ID]) + await cg.register_component(connection_var, connection_conf) + cg.add(var.register_connection(connection_var)) + await esp32_ble_tracker.register_client(connection_var, connection_conf) cg.add_define("USE_BLUETOOTH_PROXY") diff --git a/esphome/components/bluetooth_proxy/bluetooth_connection.cpp b/esphome/components/bluetooth_proxy/bluetooth_connection.cpp new file mode 100644 index 0000000000..1911701632 --- /dev/null +++ b/esphome/components/bluetooth_proxy/bluetooth_connection.cpp @@ -0,0 +1,281 @@ +#include "bluetooth_connection.h" + +#include "esphome/components/api/api_server.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +#ifdef USE_ESP32 + +#include "bluetooth_proxy.h" + +namespace esphome { +namespace bluetooth_proxy { + +static const char *const TAG = "bluetooth_proxy.connection"; + +bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t *param) { + if (!BLEClientBase::gattc_event_handler(event, gattc_if, param)) + return false; + + switch (event) { + case ESP_GATTC_DISCONNECT_EVT: { + api::global_api_server->send_bluetooth_device_connection(this->address_, false, 0, param->disconnect.reason); + this->set_address(0); + api::global_api_server->send_bluetooth_connections_free(this->proxy_->get_bluetooth_connections_free(), + this->proxy_->get_bluetooth_connections_limit()); + break; + } + case ESP_GATTC_OPEN_EVT: { + if (param->open.conn_id != this->conn_id_) + break; + if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN) { + api::global_api_server->send_bluetooth_device_connection(this->address_, false, 0, param->open.status); + this->set_address(0); + api::global_api_server->send_bluetooth_connections_free(this->proxy_->get_bluetooth_connections_free(), + this->proxy_->get_bluetooth_connections_limit()); + } + break; + } + case ESP_GATTC_SEARCH_CMPL_EVT: { + if (param->search_cmpl.conn_id != this->conn_id_) + break; + api::global_api_server->send_bluetooth_device_connection(this->address_, true, this->mtu_); + api::global_api_server->send_bluetooth_connections_free(this->proxy_->get_bluetooth_connections_free(), + this->proxy_->get_bluetooth_connections_limit()); + break; + } + case ESP_GATTC_READ_DESCR_EVT: + case ESP_GATTC_READ_CHAR_EVT: { + if (param->read.conn_id != this->conn_id_) + break; + if (param->read.status != ESP_GATT_OK) { + ESP_LOGW(TAG, "[%d] [%s] Error reading char/descriptor at handle 0x%2X, status=%d", this->connection_index_, + this->address_str_.c_str(), param->read.handle, param->read.status); + api::global_api_server->send_bluetooth_gatt_error(this->address_, param->read.handle, param->read.status); + break; + } + api::BluetoothGATTReadResponse resp; + resp.address = this->address_; + resp.handle = param->read.handle; + resp.data.reserve(param->read.value_len); + for (uint16_t i = 0; i < param->read.value_len; i++) { + resp.data.push_back(param->read.value[i]); + } + api::global_api_server->send_bluetooth_gatt_read_response(resp); + break; + } + case ESP_GATTC_WRITE_CHAR_EVT: + case ESP_GATTC_WRITE_DESCR_EVT: { + if (param->write.conn_id != this->conn_id_) + break; + if (param->write.status != ESP_GATT_OK) { + ESP_LOGW(TAG, "[%d] [%s] Error writing char/descriptor at handle 0x%2X, status=%d", this->connection_index_, + this->address_str_.c_str(), param->write.handle, param->write.status); + api::global_api_server->send_bluetooth_gatt_error(this->address_, param->write.handle, param->write.status); + break; + } + api::BluetoothGATTWriteResponse resp; + resp.address = this->address_; + resp.handle = param->write.handle; + api::global_api_server->send_bluetooth_gatt_write_response(resp); + break; + } + case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: { + if (this->get_characteristic(param->unreg_for_notify.handle) == nullptr) // No conn_id in this event + break; + if (param->unreg_for_notify.status != ESP_GATT_OK) { + ESP_LOGW(TAG, "[%d] [%s] Error unregistering notifications for handle 0x%2X, status=%d", + this->connection_index_, this->address_str_.c_str(), param->unreg_for_notify.handle, + param->unreg_for_notify.status); + api::global_api_server->send_bluetooth_gatt_error(this->address_, param->unreg_for_notify.handle, + param->unreg_for_notify.status); + break; + } + api::BluetoothGATTNotifyResponse resp; + resp.address = this->address_; + resp.handle = param->unreg_for_notify.handle; + api::global_api_server->send_bluetooth_gatt_notify_response(resp); + break; + } + case ESP_GATTC_REG_FOR_NOTIFY_EVT: { + if (this->get_characteristic(param->reg_for_notify.handle) == nullptr) // No conn_id in this event + break; + if (param->reg_for_notify.status != ESP_GATT_OK) { + ESP_LOGW(TAG, "[%d] [%s] Error registering notifications for handle 0x%2X, status=%d", this->connection_index_, + this->address_str_.c_str(), param->reg_for_notify.handle, param->reg_for_notify.status); + api::global_api_server->send_bluetooth_gatt_error(this->address_, param->reg_for_notify.handle, + param->reg_for_notify.status); + break; + } + api::BluetoothGATTNotifyResponse resp; + resp.address = this->address_; + resp.handle = param->reg_for_notify.handle; + api::global_api_server->send_bluetooth_gatt_notify_response(resp); + break; + } + case ESP_GATTC_NOTIFY_EVT: { + if (param->notify.conn_id != this->conn_id_) + break; + ESP_LOGV(TAG, "[%d] [%s] ESP_GATTC_NOTIFY_EVT: handle=0x%2X", this->connection_index_, this->address_str_.c_str(), + param->notify.handle); + api::BluetoothGATTNotifyDataResponse resp; + resp.address = this->address_; + resp.handle = param->notify.handle; + resp.data.reserve(param->notify.value_len); + for (uint16_t i = 0; i < param->notify.value_len; i++) { + resp.data.push_back(param->notify.value[i]); + } + api::global_api_server->send_bluetooth_gatt_notify_data_response(resp); + break; + } + default: + break; + } + return true; +} + +esp_err_t BluetoothConnection::read_characteristic(uint16_t handle) { + if (!this->connected()) { + ESP_LOGW(TAG, "[%d] [%s] Cannot read GATT characteristic, not connected.", this->connection_index_, + this->address_str_.c_str()); + return ESP_GATT_NOT_CONNECTED; + } + auto *characteristic = this->get_characteristic(handle); + if (characteristic == nullptr) { + ESP_LOGW(TAG, "[%d] [%s] Cannot read GATT characteristic, not found.", this->connection_index_, + this->address_str_.c_str()); + return ESP_GATT_INVALID_HANDLE; + } + + ESP_LOGV(TAG, "[%d] [%s] Reading GATT characteristic %s", this->connection_index_, this->address_str_.c_str(), + characteristic->uuid.to_string().c_str()); + + esp_err_t err = + esp_ble_gattc_read_char(this->gattc_if_, this->conn_id_, characteristic->handle, ESP_GATT_AUTH_REQ_NONE); + if (err != ERR_OK) { + ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_read_char error, err=%d", this->connection_index_, + this->address_str_.c_str(), err); + return err; + } + return ESP_OK; +} + +esp_err_t BluetoothConnection::write_characteristic(uint16_t handle, const std::string &data, bool response) { + if (!this->connected()) { + ESP_LOGW(TAG, "[%d] [%s] Cannot write GATT characteristic, not connected.", this->connection_index_, + this->address_str_.c_str()); + return ESP_GATT_NOT_CONNECTED; + } + auto *characteristic = this->get_characteristic(handle); + if (characteristic == nullptr) { + ESP_LOGW(TAG, "[%d] [%s] Cannot write GATT characteristic, not found.", this->connection_index_, + this->address_str_.c_str()); + return ESP_GATT_INVALID_HANDLE; + } + + ESP_LOGV(TAG, "[%d] [%s] Writing GATT characteristic %s", this->connection_index_, this->address_str_.c_str(), + characteristic->uuid.to_string().c_str()); + + auto err = characteristic->write_value((uint8_t *) data.data(), data.size(), + response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP); + if (err != ERR_OK) { + ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_write_char error, err=%d", this->connection_index_, + this->address_str_.c_str(), err); + return err; + } + return ESP_OK; +} + +esp_err_t BluetoothConnection::read_descriptor(uint16_t handle) { + if (!this->connected()) { + ESP_LOGW(TAG, "[%d] [%s] Cannot read GATT descriptor, not connected.", this->connection_index_, + this->address_str_.c_str()); + return ESP_GATT_NOT_CONNECTED; + } + auto *descriptor = this->get_descriptor(handle); + if (descriptor == nullptr) { + ESP_LOGW(TAG, "[%d] [%s] Cannot read GATT descriptor, not found.", this->connection_index_, + this->address_str_.c_str()); + return ESP_GATT_INVALID_HANDLE; + } + + ESP_LOGV(TAG, "[%d] [%s] Reading GATT descriptor %s", this->connection_index_, this->address_str_.c_str(), + descriptor->uuid.to_string().c_str()); + + esp_err_t err = + esp_ble_gattc_read_char_descr(this->gattc_if_, this->conn_id_, descriptor->handle, ESP_GATT_AUTH_REQ_NONE); + if (err != ERR_OK) { + ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_read_char_descr error, err=%d", this->connection_index_, + this->address_str_.c_str(), err); + return err; + } + return ESP_OK; +} + +esp_err_t BluetoothConnection::write_descriptor(uint16_t handle, const std::string &data) { + if (!this->connected()) { + ESP_LOGW(TAG, "[%d] [%s] Cannot write GATT descriptor, not connected.", this->connection_index_, + this->address_str_.c_str()); + return ESP_GATT_NOT_CONNECTED; + } + auto *descriptor = this->get_descriptor(handle); + if (descriptor == nullptr) { + ESP_LOGW(TAG, "[%d] [%s] Cannot write GATT descriptor, not found.", this->connection_index_, + this->address_str_.c_str()); + return ESP_GATT_INVALID_HANDLE; + } + + ESP_LOGV(TAG, "[%d] [%s] Writing GATT descriptor %s", this->connection_index_, this->address_str_.c_str(), + descriptor->uuid.to_string().c_str()); + + auto err = + esp_ble_gattc_write_char_descr(this->gattc_if_, this->conn_id_, descriptor->handle, data.size(), + (uint8_t *) data.data(), ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); + if (err != ERR_OK) { + ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_write_char_descr error, err=%d", this->connection_index_, + this->address_str_.c_str(), err); + return err; + } + return ESP_OK; +} + +esp_err_t BluetoothConnection::notify_characteristic(uint16_t handle, bool enable) { + if (!this->connected()) { + ESP_LOGW(TAG, "[%d] [%s] Cannot notify GATT characteristic, not connected.", this->connection_index_, + this->address_str_.c_str()); + return ESP_GATT_NOT_CONNECTED; + } + auto *characteristic = this->get_characteristic(handle); + if (characteristic == nullptr) { + ESP_LOGW(TAG, "[%d] [%s] Cannot notify GATT characteristic, not found.", this->connection_index_, + this->address_str_.c_str()); + return ESP_GATT_INVALID_HANDLE; + } + + if (enable) { + ESP_LOGV(TAG, "[%d] [%s] Registering for GATT characteristic notifications %s", this->connection_index_, + this->address_str_.c_str(), characteristic->uuid.to_string().c_str()); + esp_err_t err = esp_ble_gattc_register_for_notify(this->gattc_if_, this->remote_bda_, characteristic->handle); + if (err != ESP_OK) { + ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_register_for_notify failed, err=%d", this->connection_index_, + this->address_str_.c_str(), err); + return err; + } + } else { + ESP_LOGV(TAG, "[%d] [%s] Unregistering for GATT characteristic notifications %s", this->connection_index_, + this->address_str_.c_str(), characteristic->uuid.to_string().c_str()); + esp_err_t err = esp_ble_gattc_unregister_for_notify(this->gattc_if_, this->remote_bda_, characteristic->handle); + if (err != ESP_OK) { + ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_unregister_for_notify failed, err=%d", this->connection_index_, + this->address_str_.c_str(), err); + return err; + } + } + return ESP_OK; +} + +} // namespace bluetooth_proxy +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/bluetooth_proxy/bluetooth_connection.h b/esphome/components/bluetooth_proxy/bluetooth_connection.h new file mode 100644 index 0000000000..1f759c3485 --- /dev/null +++ b/esphome/components/bluetooth_proxy/bluetooth_connection.h @@ -0,0 +1,34 @@ +#pragma once + +#ifdef USE_ESP32 + +#include "esphome/components/esp32_ble_client/ble_client_base.h" + +namespace esphome { +namespace bluetooth_proxy { + +class BluetoothProxy; + +class BluetoothConnection : public esp32_ble_client::BLEClientBase { + public: + 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_err_t read_characteristic(uint16_t handle); + esp_err_t write_characteristic(uint16_t handle, const std::string &data, bool response); + esp_err_t read_descriptor(uint16_t handle); + esp_err_t write_descriptor(uint16_t handle, const std::string &data); + + esp_err_t notify_characteristic(uint16_t handle, bool enable); + + protected: + friend class BluetoothProxy; + + int16_t send_service_{-1}; + BluetoothProxy *proxy_; +}; + +} // namespace bluetooth_proxy +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp index 5dfdc06070..7798bb1e85 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -11,13 +11,7 @@ namespace bluetooth_proxy { static const char *const TAG = "bluetooth_proxy"; -static const esp_err_t ESP_GATT_NOT_CONNECTED = -1; -static const esp_err_t ESP_GATT_WRONG_ADDRESS = -2; - -BluetoothProxy::BluetoothProxy() { - global_bluetooth_proxy = this; - this->address_ = 0; -} +BluetoothProxy::BluetoothProxy() { global_bluetooth_proxy = this; } bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { if (!api::global_api_server->is_connected()) @@ -26,10 +20,6 @@ bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) device.get_rssi()); this->send_api_packet_(device); - if (this->address_ == 0) - return true; - - BLEClientBase::parse_device(device); return true; } @@ -57,170 +47,101 @@ void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &devi api::global_api_server->send_bluetooth_le_advertisement(resp); } -void BluetoothProxy::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t *param) { - BLEClientBase::gattc_event_handler(event, gattc_if, param); - switch (event) { - case ESP_GATTC_DISCONNECT_EVT: { - api::global_api_server->send_bluetooth_device_connection(this->address_, false, this->mtu_, - param->disconnect.reason); - api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(), - this->get_bluetooth_connections_limit()); - this->address_ = 0; - } - case ESP_GATTC_OPEN_EVT: { - if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN) { - api::global_api_server->send_bluetooth_device_connection(this->address_, false, this->mtu_, param->open.status); - break; +void BluetoothProxy::dump_config() { + ESP_LOGCONFIG(TAG, "Bluetooth Proxy:"); + ESP_LOGCONFIG(TAG, " Active: %s", YESNO(this->active_)); +} + +void BluetoothProxy::loop() { + if (!api::global_api_server->is_connected()) { + for (auto *connection : this->connections_) { + if (connection->get_address() != 0) { + connection->disconnect(); } - break; } - case ESP_GATTC_SEARCH_CMPL_EVT: { - api::global_api_server->send_bluetooth_device_connection(this->address_, true, this->mtu_); - api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(), - this->get_bluetooth_connections_limit()); - break; - } - case ESP_GATTC_READ_DESCR_EVT: - case ESP_GATTC_READ_CHAR_EVT: { - if (param->read.conn_id != this->conn_id_) - break; - if (param->read.status != ESP_GATT_OK) { - ESP_LOGW(TAG, "Error reading char/descriptor at handle 0x%2X, status=%d", param->read.handle, - param->read.status); - api::global_api_server->send_bluetooth_gatt_error(this->address_, param->read.handle, param->read.status); - break; + return; + } + for (auto *connection : this->connections_) { + if (connection->send_service_ == connection->services_.size()) { + connection->send_service_ = -1; + api::global_api_server->send_bluetooth_gatt_services_done(connection->get_address()); + } else if (connection->send_service_ >= 0) { + auto &service = connection->services_[connection->send_service_]; + api::BluetoothGATTGetServicesResponse resp; + resp.address = connection->get_address(); + api::BluetoothGATTService service_resp; + service_resp.uuid = {service->uuid.get_128bit_high(), service->uuid.get_128bit_low()}; + service_resp.handle = service->start_handle; + for (auto &characteristic : service->characteristics) { + api::BluetoothGATTCharacteristic characteristic_resp; + characteristic_resp.uuid = {characteristic->uuid.get_128bit_high(), characteristic->uuid.get_128bit_low()}; + characteristic_resp.handle = characteristic->handle; + characteristic_resp.properties = characteristic->properties; + for (auto &descriptor : characteristic->descriptors) { + api::BluetoothGATTDescriptor descriptor_resp; + descriptor_resp.uuid = {descriptor->uuid.get_128bit_high(), descriptor->uuid.get_128bit_low()}; + descriptor_resp.handle = descriptor->handle; + characteristic_resp.descriptors.push_back(std::move(descriptor_resp)); + } + service_resp.characteristics.push_back(std::move(characteristic_resp)); } - api::BluetoothGATTReadResponse resp; - resp.address = this->address_; - resp.handle = param->read.handle; - resp.data.reserve(param->read.value_len); - for (uint16_t i = 0; i < param->read.value_len; i++) { - resp.data.push_back(param->read.value[i]); - } - api::global_api_server->send_bluetooth_gatt_read_response(resp); - break; + resp.services.push_back(std::move(service_resp)); + api::global_api_server->send_bluetooth_gatt_services(resp); + connection->send_service_++; } - case ESP_GATTC_WRITE_CHAR_EVT: - case ESP_GATTC_WRITE_DESCR_EVT: { - if (param->write.conn_id != this->conn_id_) - break; - if (param->write.status != ESP_GATT_OK) { - ESP_LOGW(TAG, "Error writing char/descriptor at handle 0x%2X, status=%d", param->write.handle, - param->write.status); - api::global_api_server->send_bluetooth_gatt_error(this->address_, param->write.handle, param->write.status); - break; - } - api::BluetoothGATTWriteResponse resp; - resp.address = this->address_; - resp.handle = param->write.handle; - api::global_api_server->send_bluetooth_gatt_write_response(resp); - break; - } - case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: { - if (param->unreg_for_notify.status != ESP_GATT_OK) { - ESP_LOGW(TAG, "Error unregistering notifications for handle 0x%2X, status=%d", param->unreg_for_notify.handle, - param->unreg_for_notify.status); - api::global_api_server->send_bluetooth_gatt_error(this->address_, param->unreg_for_notify.handle, - param->unreg_for_notify.status); - break; - } - api::BluetoothGATTNotifyResponse resp; - resp.address = this->address_; - resp.handle = param->unreg_for_notify.handle; - api::global_api_server->send_bluetooth_gatt_notify_response(resp); - break; - } - case ESP_GATTC_REG_FOR_NOTIFY_EVT: { - if (param->reg_for_notify.status != ESP_GATT_OK) { - ESP_LOGW(TAG, "Error registering notifications for handle 0x%2X, status=%d", param->reg_for_notify.handle, - param->reg_for_notify.status); - api::global_api_server->send_bluetooth_gatt_error(this->address_, param->reg_for_notify.handle, - param->reg_for_notify.status); - break; - } - api::BluetoothGATTNotifyResponse resp; - resp.address = this->address_; - resp.handle = param->reg_for_notify.handle; - api::global_api_server->send_bluetooth_gatt_notify_response(resp); - break; - } - case ESP_GATTC_NOTIFY_EVT: { - if (param->notify.conn_id != this->conn_id_) - break; - ESP_LOGV(TAG, "ESP_GATTC_NOTIFY_EVT: handle=0x%2X", param->notify.handle); - api::BluetoothGATTNotifyDataResponse resp; - resp.address = this->address_; - resp.handle = param->notify.handle; - resp.data.reserve(param->notify.value_len); - for (uint16_t i = 0; i < param->notify.value_len; i++) { - resp.data.push_back(param->notify.value[i]); - } - api::global_api_server->send_bluetooth_gatt_notify_data_response(resp); - break; - } - default: - break; } } -void BluetoothProxy::dump_config() { ESP_LOGCONFIG(TAG, "Bluetooth Proxy:"); } +BluetoothConnection *BluetoothProxy::get_connection_(uint64_t address, bool reserve) { + for (auto *connection : this->connections_) { + if (connection->get_address() == address) + return connection; + } -void BluetoothProxy::loop() { - BLEClientBase::loop(); - if (this->state_ != espbt::ClientState::IDLE && !api::global_api_server->is_connected()) { - ESP_LOGI(TAG, "[%s] Disconnecting.", this->address_str().c_str()); - auto err = esp_ble_gattc_close(this->gattc_if_, this->conn_id_); - if (err != ERR_OK) { - ESP_LOGW(TAG, "esp_ble_gattc_close error, address=%s err=%d", this->address_str().c_str(), err); + if (!reserve) + return nullptr; + + for (auto *connection : this->connections_) { + if (connection->get_address() == 0) { + connection->set_address(address); + return connection; } } - if (this->send_service_ == this->services_.size()) { - this->send_service_ = -1; - api::global_api_server->send_bluetooth_gatt_services_done(this->address_); - } else if (this->send_service_ >= 0) { - auto &service = this->services_[this->send_service_]; - api::BluetoothGATTGetServicesResponse resp; - resp.address = this->address_; - api::BluetoothGATTService service_resp; - service_resp.uuid = {service->uuid.get_128bit_high(), service->uuid.get_128bit_low()}; - service_resp.handle = service->start_handle; - for (auto &characteristic : service->characteristics) { - api::BluetoothGATTCharacteristic characteristic_resp; - characteristic_resp.uuid = {characteristic->uuid.get_128bit_high(), characteristic->uuid.get_128bit_low()}; - characteristic_resp.handle = characteristic->handle; - characteristic_resp.properties = characteristic->properties; - for (auto &descriptor : characteristic->descriptors) { - api::BluetoothGATTDescriptor descriptor_resp; - descriptor_resp.uuid = {descriptor->uuid.get_128bit_high(), descriptor->uuid.get_128bit_low()}; - descriptor_resp.handle = descriptor->handle; - characteristic_resp.descriptors.push_back(std::move(descriptor_resp)); - } - service_resp.characteristics.push_back(std::move(characteristic_resp)); - } - resp.services.push_back(std::move(service_resp)); - api::global_api_server->send_bluetooth_gatt_services(resp); - this->send_service_++; - } + return nullptr; } void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest &msg) { switch (msg.request_type) { case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT: { - this->address_ = msg.address; - this->set_state(espbt::ClientState::SEARCHING); + auto *connection = this->get_connection_(msg.address, true); + if (connection == nullptr) { + ESP_LOGW(TAG, "No free connections available"); + api::global_api_server->send_bluetooth_device_connection(msg.address, false); + return; + } + ESP_LOGV(TAG, "[%d] [%s] Searching to connect", connection->get_connection_index(), + connection->address_str().c_str()); + connection->set_state(espbt::ClientState::SEARCHING); api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(), this->get_bluetooth_connections_limit()); break; } case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT: { - if (this->state() != espbt::ClientState::IDLE) { - ESP_LOGI(TAG, "[%s] Disconnecting.", this->address_str().c_str()); - auto err = esp_ble_gattc_close(this->gattc_if_, this->conn_id_); - if (err != ERR_OK) { - ESP_LOGW(TAG, "esp_ble_gattc_close error, address=%s err=%d", this->address_str().c_str(), err); - } + auto *connection = this->get_connection_(msg.address, false); + if (connection == nullptr) { + api::global_api_server->send_bluetooth_device_connection(msg.address, false); + api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(), + this->get_bluetooth_connections_limit()); + return; + } + if (connection->state() != espbt::ClientState::IDLE) { + connection->disconnect(); + } else { + connection->set_address(0); + api::global_api_server->send_bluetooth_device_connection(msg.address, false); + api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(), + this->get_bluetooth_connections_limit()); } break; } @@ -231,170 +152,88 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest } void BluetoothProxy::bluetooth_gatt_read(const api::BluetoothGATTReadRequest &msg) { - if (this->state_ != espbt::ClientState::ESTABLISHED) { - ESP_LOGW(TAG, "Cannot read GATT characteristic, not connected."); + auto *connection = this->get_connection_(msg.address, false); + if (connection == nullptr) { + ESP_LOGW(TAG, "Cannot read GATT characteristic, not connected"); api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED); return; } - if (this->address_ != msg.address) { - ESP_LOGW(TAG, "Address mismatch for read GATT characteristic request"); - api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_WRONG_ADDRESS); - return; - } - auto *characteristic = this->get_characteristic(msg.handle); - if (characteristic == nullptr) { - ESP_LOGW(TAG, "Cannot read GATT characteristic, not found."); - api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_INVALID_HANDLE); - return; - } - - ESP_LOGV(TAG, "Reading GATT characteristic %s", characteristic->uuid.to_string().c_str()); - - esp_err_t err = - esp_ble_gattc_read_char(this->gattc_if_, this->conn_id_, characteristic->handle, ESP_GATT_AUTH_REQ_NONE); - if (err != ERR_OK) { - ESP_LOGW(TAG, "esp_ble_gattc_read_char error, err=%d", err); + auto err = connection->read_characteristic(msg.handle); + if (err != ESP_OK) { api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err); } } void BluetoothProxy::bluetooth_gatt_write(const api::BluetoothGATTWriteRequest &msg) { - if (this->state_ != espbt::ClientState::ESTABLISHED) { - ESP_LOGW(TAG, "Cannot write GATT characteristic, not connected."); + auto *connection = this->get_connection_(msg.address, false); + if (connection == nullptr) { + ESP_LOGW(TAG, "Cannot write GATT characteristic, not connected"); api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED); return; } - if (this->address_ != msg.address) { - ESP_LOGW(TAG, "Address mismatch for write GATT characteristic request"); - api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_WRONG_ADDRESS); - return; - } - auto *characteristic = this->get_characteristic(msg.handle); - if (characteristic == nullptr) { - ESP_LOGW(TAG, "Cannot write GATT characteristic, not found."); - api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_INVALID_HANDLE); - return; - } - - ESP_LOGV(TAG, "Writing GATT characteristic %s", characteristic->uuid.to_string().c_str()); - auto err = characteristic->write_value((uint8_t *) msg.data.data(), msg.data.size(), - msg.response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP); - if (err != ERR_OK) { + auto err = connection->write_characteristic(msg.handle, msg.data, msg.response); + if (err != ESP_OK) { api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err); } } void BluetoothProxy::bluetooth_gatt_read_descriptor(const api::BluetoothGATTReadDescriptorRequest &msg) { - if (this->state_ != espbt::ClientState::ESTABLISHED) { - ESP_LOGW(TAG, "Cannot read GATT characteristic descriptor, not connected."); + auto *connection = this->get_connection_(msg.address, false); + if (connection == nullptr) { + ESP_LOGW(TAG, "Cannot read GATT descriptor, not connected"); api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED); return; } - if (this->address_ != msg.address) { - ESP_LOGW(TAG, "Address mismatch for read GATT characteristic descriptor request"); - api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_WRONG_ADDRESS); - return; - } - auto *descriptor = this->get_descriptor(msg.handle); - if (descriptor == nullptr) { - ESP_LOGW(TAG, "Cannot read GATT characteristic descriptor, not found."); - api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_INVALID_HANDLE); - return; - } - - ESP_LOGV(TAG, "Reading GATT characteristic descriptor %s -> %s", descriptor->characteristic->uuid.to_string().c_str(), - descriptor->uuid.to_string().c_str()); - - esp_err_t err = - esp_ble_gattc_read_char_descr(this->gattc_if_, this->conn_id_, descriptor->handle, ESP_GATT_AUTH_REQ_NONE); - if (err != ERR_OK) { - ESP_LOGW(TAG, "esp_ble_gattc_read_char error, err=%d", err); + auto err = connection->read_descriptor(msg.handle); + if (err != ESP_OK) { api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err); } } void BluetoothProxy::bluetooth_gatt_write_descriptor(const api::BluetoothGATTWriteDescriptorRequest &msg) { - if (this->state_ != espbt::ClientState::ESTABLISHED) { - ESP_LOGW(TAG, "Cannot write GATT characteristic descriptor, not connected."); + auto *connection = this->get_connection_(msg.address, false); + if (connection == nullptr) { + ESP_LOGW(TAG, "Cannot write GATT descriptor, not connected"); api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED); return; } - if (this->address_ != msg.address) { - ESP_LOGW(TAG, "Address mismatch for write GATT characteristic descriptor request"); - api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_WRONG_ADDRESS); - return; - } - auto *descriptor = this->get_descriptor(msg.handle); - if (descriptor == nullptr) { - ESP_LOGW(TAG, "Cannot write GATT characteristic descriptor, not found."); - api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_INVALID_HANDLE); - return; - } - - ESP_LOGV(TAG, "Writing GATT characteristic descriptor %s -> %s", descriptor->characteristic->uuid.to_string().c_str(), - descriptor->uuid.to_string().c_str()); - - esp_err_t err = - esp_ble_gattc_write_char_descr(this->gattc_if_, this->conn_id_, descriptor->handle, msg.data.size(), - (uint8_t *) msg.data.data(), ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); - if (err != ERR_OK) { - ESP_LOGW(TAG, "esp_ble_gattc_write_char_descr error, err=%d", err); + auto err = connection->write_descriptor(msg.handle, msg.data); + if (err != ESP_OK) { api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err); } } void BluetoothProxy::bluetooth_gatt_send_services(const api::BluetoothGATTGetServicesRequest &msg) { - if (this->state_ != espbt::ClientState::ESTABLISHED) { - ESP_LOGW(TAG, "Cannot get GATT services, not connected."); + auto *connection = this->get_connection_(msg.address, false); + if (connection == nullptr || !connection->connected()) { + ESP_LOGW(TAG, "Cannot get GATT services, not connected"); api::global_api_server->send_bluetooth_gatt_error(msg.address, 0, ESP_GATT_NOT_CONNECTED); return; } - if (this->address_ != msg.address) { - ESP_LOGW(TAG, "Address mismatch for service list request"); - api::global_api_server->send_bluetooth_gatt_error(msg.address, 0, ESP_GATT_WRONG_ADDRESS); + if (connection->services_.empty()) { + ESP_LOGW(TAG, "[%d] [%s] No GATT services found", connection->connection_index_, connection->address_str().c_str()); + api::global_api_server->send_bluetooth_gatt_services_done(msg.address); return; } - this->send_service_ = 0; + if (connection->send_service_ == -1) // Don't start sending services again if we're already sending them + connection->send_service_ = 0; } void BluetoothProxy::bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest &msg) { - if (this->state_ != espbt::ClientState::ESTABLISHED) { - ESP_LOGW(TAG, "Cannot configure notify, not connected."); + auto *connection = this->get_connection_(msg.address, false); + if (connection == nullptr) { + ESP_LOGW(TAG, "Cannot notify GATT characteristic, not connected"); api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED); return; } - if (this->address_ != msg.address) { - ESP_LOGW(TAG, "Address mismatch for notify"); - api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_WRONG_ADDRESS); - return; - } - - auto *characteristic = this->get_characteristic(msg.handle); - - if (characteristic == nullptr) { - ESP_LOGW(TAG, "Cannot notify GATT characteristic, not found."); - api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_INVALID_HANDLE); - return; - } - - esp_err_t err; - if (msg.enable) { - err = esp_ble_gattc_register_for_notify(this->gattc_if_, this->remote_bda_, characteristic->handle); - if (err != ESP_OK) { - ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, err=%d", err); - api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err); - } - } else { - err = esp_ble_gattc_unregister_for_notify(this->gattc_if_, this->remote_bda_, characteristic->handle); - if (err != ESP_OK) { - ESP_LOGW(TAG, "esp_ble_gattc_unregister_for_notify failed, err=%d", err); - api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err); - } + auto err = connection->notify_characteristic(msg.handle, msg.enable); + if (err != ESP_OK) { + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err); } } diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.h b/esphome/components/bluetooth_proxy/bluetooth_proxy.h index 9529b99f73..129b042277 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.h +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.h @@ -11,22 +11,26 @@ #include "esphome/core/component.h" #include "esphome/core/defines.h" -#include +#include "bluetooth_connection.h" namespace esphome { namespace bluetooth_proxy { +static const esp_err_t ESP_GATT_NOT_CONNECTED = -1; + using namespace esp32_ble_client; -class BluetoothProxy : public BLEClientBase { +class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Component { public: BluetoothProxy(); bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; void dump_config() override; void loop() override; - 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 register_connection(BluetoothConnection *connection) { + this->connections_.push_back(connection); + connection->proxy_ = this; + } void bluetooth_device_request(const api::BluetoothDeviceRequest &msg); void bluetooth_gatt_read(const api::BluetoothGATTReadRequest &msg); @@ -36,8 +40,16 @@ class BluetoothProxy : public BLEClientBase { void bluetooth_gatt_send_services(const api::BluetoothGATTGetServicesRequest &msg); void bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest &msg); - int get_bluetooth_connections_free() { return this->state_ == espbt::ClientState::IDLE ? 1 : 0; } - int get_bluetooth_connections_limit() { return 1; } + int get_bluetooth_connections_free() { + int free = 0; + for (auto *connection : this->connections_) { + if (connection->address_ == 0) { + free++; + } + } + return free; + } + int get_bluetooth_connections_limit() { return this->connections_.size(); } void set_active(bool active) { this->active_ = active; } bool has_active() { return this->active_; } @@ -45,8 +57,12 @@ class BluetoothProxy : public BLEClientBase { protected: void send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device); + BluetoothConnection *get_connection_(uint64_t address, bool reserve); + int16_t send_service_{-1}; bool active_; + + std::vector connections_{}; }; extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/esphome/components/esp32_ble_client/ble_characteristic.cpp b/esphome/components/esp32_ble_client/ble_characteristic.cpp index 2692498e98..fbc9ba16dc 100644 --- a/esphome/components/esp32_ble_client/ble_characteristic.cpp +++ b/esphome/components/esp32_ble_client/ble_characteristic.cpp @@ -9,7 +9,7 @@ namespace esphome { namespace esp32_ble_client { -static const char *const TAG = "esp32_ble_client.characteristic"; +static const char *const TAG = "esp32_ble_client"; BLECharacteristic::~BLECharacteristic() { for (auto &desc : this->descriptors) @@ -29,7 +29,8 @@ void BLECharacteristic::parse_descriptors() { break; } if (status != ESP_GATT_OK) { - ESP_LOGW(TAG, "esp_ble_gattc_get_all_descr error, status=%d", status); + ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_get_all_descr error, status=%d", + this->service->client->get_connection_index(), this->service->client->address_str().c_str(), status); break; } if (count == 0) { @@ -41,7 +42,8 @@ void BLECharacteristic::parse_descriptors() { desc->handle = result.handle; desc->characteristic = this; this->descriptors.push_back(desc); - ESP_LOGV(TAG, " descriptor %s, handle 0x%x", desc->uuid.to_string().c_str(), desc->handle); + ESP_LOGV(TAG, "[%d] [%s] descriptor %s, handle 0x%x", this->service->client->get_connection_index(), + this->service->client->address_str().c_str(), desc->uuid.to_string().c_str(), desc->handle); offset++; } } @@ -69,7 +71,8 @@ esp_err_t BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size, auto status = esp_ble_gattc_write_char(client->get_gattc_if(), client->get_conn_id(), this->handle, new_val_size, new_val, write_type, ESP_GATT_AUTH_REQ_NONE); if (status) { - ESP_LOGW(TAG, "Error sending write value to BLE gattc server, status=%d", status); + ESP_LOGW(TAG, "[%d] [%s] Error sending write value to BLE gattc server, status=%d", + this->service->client->get_connection_index(), this->service->client->address_str().c_str(), status); } return status; } diff --git a/esphome/components/esp32_ble_client/ble_client_base.cpp b/esphome/components/esp32_ble_client/ble_client_base.cpp index 68d9d873cd..3579d93c5a 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.cpp +++ b/esphome/components/esp32_ble_client/ble_client_base.cpp @@ -11,6 +11,9 @@ namespace esp32_ble_client { static const char *const TAG = "esp32_ble_client"; void BLEClientBase::setup() { + static uint8_t connection_index = 0; + this->connection_index_ = connection_index++; + auto ret = esp_ble_gattc_app_register(this->app_id); if (ret) { ESP_LOGE(TAG, "gattc app register failed. app_id=%d code=%d", this->app_id, ret); @@ -33,7 +36,7 @@ bool BLEClientBase::parse_device(const espbt::ESPBTDevice &device) { if (this->state_ != espbt::ClientState::IDLE && this->state_ != espbt::ClientState::SEARCHING) return false; - ESP_LOGD(TAG, "Found device at MAC address [%s]", device.address_str().c_str()); + ESP_LOGD(TAG, "[%d] [%s] Found device", this->connection_index_, this->address_str_.c_str()); this->set_state(espbt::ClientState::DISCOVERED); auto addr = device.address_uint64(); @@ -47,80 +50,88 @@ bool BLEClientBase::parse_device(const espbt::ESPBTDevice &device) { return true; } -std::string BLEClientBase::address_str() const { - return str_snprintf("%02x:%02x:%02x:%02x:%02x:%02x", 17, (uint8_t)(this->address_ >> 40) & 0xff, - (uint8_t)(this->address_ >> 32) & 0xff, (uint8_t)(this->address_ >> 24) & 0xff, - (uint8_t)(this->address_ >> 16) & 0xff, (uint8_t)(this->address_ >> 8) & 0xff, - (uint8_t)(this->address_ >> 0) & 0xff); -} - void BLEClientBase::connect() { - ESP_LOGI(TAG, "Attempting BLE connection to %s", this->address_str().c_str()); + ESP_LOGI(TAG, "[%d] [%s] Attempting BLE connection", this->connection_index_, this->address_str_.c_str()); auto ret = esp_ble_gattc_open(this->gattc_if_, this->remote_bda_, this->remote_addr_type_, true); if (ret) { - ESP_LOGW(TAG, "esp_ble_gattc_open error, address=%s status=%d", this->address_str().c_str(), ret); + ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_open error, status=%d", this->connection_index_, this->address_str_.c_str(), + ret); this->set_state(espbt::ClientState::IDLE); } else { this->set_state(espbt::ClientState::CONNECTING); } } -void BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t esp_gattc_if, +void BLEClientBase::disconnect() { + ESP_LOGI(TAG, "[%d] [%s] Disconnecting.", this->connection_index_, this->address_str_.c_str()); + auto err = esp_ble_gattc_close(this->gattc_if_, this->conn_id_); + if (err != ESP_OK) { + ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_close error, err=%d", this->connection_index_, this->address_str_.c_str(), + err); + } + + if (this->state_ == espbt::ClientState::SEARCHING) { + this->set_address(0); + this->set_state(espbt::ClientState::IDLE); + } +} + +bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t esp_gattc_if, esp_ble_gattc_cb_param_t *param) { if (event == ESP_GATTC_REG_EVT && this->app_id != param->reg.app_id) - return; + return false; if (event != ESP_GATTC_REG_EVT && esp_gattc_if != ESP_GATT_IF_NONE && esp_gattc_if != this->gattc_if_) - return; + return false; + + ESP_LOGV(TAG, "[%d] [%s] gattc_event_handler: event=%d gattc_if=%d", this->connection_index_, + this->address_str_.c_str(), event, esp_gattc_if); switch (event) { case ESP_GATTC_REG_EVT: { if (param->reg.status == ESP_GATT_OK) { - ESP_LOGV(TAG, "gattc registered app id %d", this->app_id); + ESP_LOGV(TAG, "[%d] [%s] gattc registered app id %d", this->connection_index_, this->address_str_.c_str(), + this->app_id); this->gattc_if_ = esp_gattc_if; } else { - ESP_LOGE(TAG, "gattc app registration failed id=%d code=%d", param->reg.app_id, param->reg.status); + ESP_LOGE(TAG, "[%d] [%s] gattc app registration failed id=%d code=%d", this->connection_index_, + this->address_str_.c_str(), param->reg.app_id, param->reg.status); } break; } case ESP_GATTC_OPEN_EVT: { - ESP_LOGV(TAG, "[%s] ESP_GATTC_OPEN_EVT", this->address_str().c_str()); + ESP_LOGV(TAG, "[%d] [%s] ESP_GATTC_OPEN_EVT", this->connection_index_, this->address_str_.c_str()); this->conn_id_ = param->open.conn_id; if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN) { - ESP_LOGW(TAG, "connect to %s failed, status=%d", this->address_str().c_str(), param->open.status); + ESP_LOGW(TAG, "[%d] [%s] Connection failed, status=%d", this->connection_index_, this->address_str_.c_str(), + param->open.status); this->set_state(espbt::ClientState::IDLE); break; } - break; - } - case ESP_GATTC_CONNECT_EVT: { - ESP_LOGV(TAG, "[%s] ESP_GATTC_CONNECT_EVT", this->address_str().c_str()); - if (this->conn_id_ != param->connect.conn_id) { - ESP_LOGD(TAG, "[%s] Unexpected conn_id in CONNECT_EVT: param conn=%d, open conn=%d", - this->address_str().c_str(), param->connect.conn_id, this->conn_id_); - } - auto ret = esp_ble_gattc_send_mtu_req(this->gattc_if_, param->connect.conn_id); + auto ret = esp_ble_gattc_send_mtu_req(this->gattc_if_, param->open.conn_id); if (ret) { - ESP_LOGW(TAG, "esp_ble_gattc_send_mtu_req failed, status=%x", ret); + ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_send_mtu_req failed, status=%x", this->connection_index_, + this->address_str_.c_str(), ret); } break; } case ESP_GATTC_CFG_MTU_EVT: { if (param->cfg_mtu.status != ESP_GATT_OK) { - ESP_LOGW(TAG, "cfg_mtu to %s failed, mtu %d, status %d", this->address_str().c_str(), param->cfg_mtu.mtu, - param->cfg_mtu.status); + ESP_LOGW(TAG, "[%d] [%s] cfg_mtu failed, mtu %d, status %d", this->connection_index_, + this->address_str_.c_str(), param->cfg_mtu.mtu, param->cfg_mtu.status); this->set_state(espbt::ClientState::IDLE); break; } - ESP_LOGV(TAG, "cfg_mtu status %d, mtu %d", param->cfg_mtu.status, param->cfg_mtu.mtu); + ESP_LOGV(TAG, "[%d] [%s] cfg_mtu status %d, mtu %d", this->connection_index_, this->address_str_.c_str(), + param->cfg_mtu.status, param->cfg_mtu.mtu); this->mtu_ = param->cfg_mtu.mtu; esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, nullptr); break; } case ESP_GATTC_DISCONNECT_EVT: { - if (memcmp(param->disconnect.remote_bda, this->remote_bda_, 6) != 0) { - return; - } - ESP_LOGV(TAG, "[%s] ESP_GATTC_DISCONNECT_EVT, reason %d", this->address_str().c_str(), param->disconnect.reason); + if (memcmp(param->disconnect.remote_bda, this->remote_bda_, 6) != 0) + return false; + ESP_LOGV(TAG, "[%d] [%s] ESP_GATTC_DISCONNECT_EVT, reason %d", this->connection_index_, + this->address_str_.c_str(), param->disconnect.reason); for (auto &svc : this->services_) delete svc; // NOLINT(cppcoreguidelines-owning-memory) this->services_.clear(); @@ -137,10 +148,12 @@ void BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ break; } case ESP_GATTC_SEARCH_CMPL_EVT: { - ESP_LOGV(TAG, "[%s] ESP_GATTC_SEARCH_CMPL_EVT", this->address_str().c_str()); + ESP_LOGV(TAG, "[%d] [%s] ESP_GATTC_SEARCH_CMPL_EVT", this->connection_index_, this->address_str_.c_str()); for (auto &svc : this->services_) { - ESP_LOGI(TAG, "Service UUID: %s", svc->uuid.to_string().c_str()); - ESP_LOGI(TAG, " start_handle: 0x%x end_handle: 0x%x", svc->start_handle, svc->end_handle); + ESP_LOGI(TAG, "[%d] [%s] Service UUID: %s", this->connection_index_, this->address_str_.c_str(), + svc->uuid.to_string().c_str()); + ESP_LOGI(TAG, "[%d] [%s] start_handle: 0x%x end_handle: 0x%x", this->connection_index_, + this->address_str_.c_str(), svc->start_handle, svc->end_handle); svc->parse_characteristics(); } this->set_state(espbt::ClientState::CONNECTED); @@ -149,14 +162,10 @@ void BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ } case ESP_GATTC_REG_FOR_NOTIFY_EVT: { auto *descr = this->get_config_descriptor(param->reg_for_notify.handle); - if (descr == nullptr) { - ESP_LOGW(TAG, "No descriptor found for notify of handle 0x%x", param->reg_for_notify.handle); - break; - } if (descr->uuid.get_uuid().len != ESP_UUID_LEN_16 || descr->uuid.get_uuid().uuid.uuid16 != ESP_GATT_UUID_CHAR_CLIENT_CONFIG) { - ESP_LOGW(TAG, "Handle 0x%x (uuid %s) is not a client config char uuid", param->reg_for_notify.handle, - descr->uuid.to_string().c_str()); + ESP_LOGW(TAG, "[%d] [%s] Handle 0x%x (uuid %s) is not a client config char uuid", this->connection_index_, + this->address_str_.c_str(), param->reg_for_notify.handle, descr->uuid.to_string().c_str()); break; } uint16_t notify_en = 1; @@ -164,7 +173,8 @@ void BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ esp_ble_gattc_write_char_descr(this->gattc_if_, this->conn_id_, descr->handle, sizeof(notify_en), (uint8_t *) ¬ify_en, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE); if (status) { - ESP_LOGW(TAG, "esp_ble_gattc_write_char_descr error, status=%d", status); + ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_write_char_descr error, status=%d", this->connection_index_, + this->address_str_.c_str(), status); } break; } @@ -172,24 +182,28 @@ void BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ default: break; } + return true; } void BLEClientBase::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { switch (event) { // This event is sent by the server when it requests security case ESP_GAP_BLE_SEC_REQ_EVT: - ESP_LOGV(TAG, "ESP_GAP_BLE_SEC_REQ_EVT %x", event); + ESP_LOGV(TAG, "[%d] [%s] ESP_GAP_BLE_SEC_REQ_EVT %x", this->connection_index_, this->address_str_.c_str(), event); esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); break; // This event is sent once authentication has completed case ESP_GAP_BLE_AUTH_CMPL_EVT: esp_bd_addr_t bd_addr; memcpy(bd_addr, param->ble_security.auth_cmpl.bd_addr, sizeof(esp_bd_addr_t)); - ESP_LOGI(TAG, "auth complete. remote BD_ADDR: %s", format_hex(bd_addr, 6).c_str()); + ESP_LOGI(TAG, "[%d] [%s] auth complete. remote BD_ADDR: %s", this->connection_index_, this->address_str_.c_str(), + format_hex(bd_addr, 6).c_str()); if (!param->ble_security.auth_cmpl.success) { - ESP_LOGE(TAG, "auth fail reason = 0x%x", param->ble_security.auth_cmpl.fail_reason); + ESP_LOGE(TAG, "[%d] [%s] auth fail reason = 0x%x", this->connection_index_, this->address_str_.c_str(), + param->ble_security.auth_cmpl.fail_reason); } else { - ESP_LOGV(TAG, "auth success. address type = %d auth mode = %d", param->ble_security.auth_cmpl.addr_type, + ESP_LOGV(TAG, "[%d] [%s] auth success. address type = %d auth mode = %d", this->connection_index_, + this->address_str_.c_str(), param->ble_security.auth_cmpl.addr_type, param->ble_security.auth_cmpl.auth_mode); } break; @@ -245,7 +259,8 @@ float BLEClientBase::parse_char_value(uint8_t *value, uint16_t length) { (int32_t)(value[4])); } } - ESP_LOGW(TAG, "Cannot parse characteristic value of type 0x%x length %d", value[0], length); + ESP_LOGW(TAG, "[%d] [%s] Cannot parse characteristic value of type 0x%x length %d", this->connection_index_, + this->address_str_.c_str(), value[0], length); return NAN; } diff --git a/esphome/components/esp32_ble_client/ble_client_base.h b/esphome/components/esp32_ble_client/ble_client_base.h index eba70fa571..90cd8dbddc 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.h +++ b/esphome/components/esp32_ble_client/ble_client_base.h @@ -28,13 +28,27 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { bool parse_device(const espbt::ESPBTDevice &device) override; void on_scan_end() override {} - void 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; void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override; void connect() override; + void disconnect(); - void set_address(uint64_t address) { this->address_ = address; } - std::string address_str() const; + bool connected() { return this->state_ == espbt::ClientState::ESTABLISHED; } + + void set_address(uint64_t address) { + this->address_ = address; + if (address == 0) { + memset(this->remote_bda_, 0, sizeof(this->remote_bda_)); + this->address_str_ = ""; + } else { + this->address_str_ = str_snprintf("%02x:%02x:%02x:%02x:%02x:%02x", 17, (uint8_t)(this->address_ >> 40) & 0xff, + (uint8_t)(this->address_ >> 32) & 0xff, (uint8_t)(this->address_ >> 24) & 0xff, + (uint8_t)(this->address_ >> 16) & 0xff, (uint8_t)(this->address_ >> 8) & 0xff, + (uint8_t)(this->address_ >> 0) & 0xff); + } + } + std::string address_str() const { return this->address_str_; } BLEService *get_service(espbt::ESPBTUUID uuid); BLEService *get_service(uint16_t uuid); @@ -55,12 +69,16 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { uint16_t get_conn_id() const { return this->conn_id_; } uint64_t get_address() const { return this->address_; } + uint8_t get_connection_index() const { return this->connection_index_; } + protected: int gattc_if_; esp_bd_addr_t remote_bda_; esp_ble_addr_type_t remote_addr_type_; - uint16_t conn_id_; - uint64_t address_; + uint16_t conn_id_{0xFFFF}; + uint64_t address_{0}; + std::string address_str_{}; + uint8_t connection_index_; uint16_t mtu_{23}; std::vector services_; diff --git a/esphome/components/esp32_ble_client/ble_service.cpp b/esphome/components/esp32_ble_client/ble_service.cpp index 1d81eaa556..2f8a55f928 100644 --- a/esphome/components/esp32_ble_client/ble_service.cpp +++ b/esphome/components/esp32_ble_client/ble_service.cpp @@ -8,7 +8,7 @@ namespace esphome { namespace esp32_ble_client { -static const char *const TAG = "esp32_ble_client.service"; +static const char *const TAG = "esp32_ble_client"; BLECharacteristic *BLEService::get_characteristic(espbt::ESPBTUUID uuid) { for (auto &chr : this->characteristics) { @@ -40,7 +40,8 @@ void BLEService::parse_characteristics() { break; } if (status != ESP_GATT_OK) { - ESP_LOGW(TAG, "esp_ble_gattc_get_all_char error, status=%d", status); + ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_get_all_char error, status=%d", this->client->get_connection_index(), + this->client->address_str().c_str(), status); break; } if (count == 0) { @@ -53,8 +54,9 @@ void BLEService::parse_characteristics() { characteristic->handle = result.char_handle; characteristic->service = this; this->characteristics.push_back(characteristic); - ESP_LOGI(TAG, " characteristic %s, handle 0x%x, properties 0x%x", characteristic->uuid.to_string().c_str(), - characteristic->handle, characteristic->properties); + ESP_LOGI(TAG, "[%d] [%s] characteristic %s, handle 0x%x, properties 0x%x", this->client->get_connection_index(), + this->client->address_str().c_str(), characteristic->uuid.to_string().c_str(), characteristic->handle, + characteristic->properties); characteristic->parse_descriptors(); offset++; } diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index dd08f07fd7..4fbf7b12e1 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -159,7 +159,7 @@ enum class ClientState { class ESPBTClient : public ESPBTDeviceListener { public: - virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + virtual bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) = 0; virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) = 0; virtual void connect() = 0;