diff --git a/esphome/components/ble_client/ble_client.cpp b/esphome/components/ble_client/ble_client.cpp index 5fba5b23a1..3ccc9ede42 100644 --- a/esphome/components/ble_client/ble_client.cpp +++ b/esphome/components/ble_client/ble_client.cpp @@ -238,35 +238,32 @@ bool BLEClient::handle_gatt_search_cmpl_(esp_gatt_status_t status) { // Stack-owned; nodes copy their handles during on_connected(). bluetooth_connection::BluedroidServiceTable table; if (!table.build(this->gattc_if_, this->conn_id_, service_total, this->connection_index_)) { - if (this->nodes_.size() > this->gatt_nodes_.size()) { - // Only the table build failed; legacy nodes read the base's services_ - // and keep the link. Gatt nodes catch the next connection (promoted so - // the release condition still fires). - ESP_LOGW(TAG, "[%s] Service table build failed; gatt nodes skip this connection", this->address_str()); - for (auto *node : this->gatt_nodes_) - node->node_state = espbt::ClientState::ESTABLISHED; - return true; - } - ESP_LOGW(TAG, "[%s] Service table build failed; treating as failed discovery", this->address_str()); - this->gatt_backoff_.register_failure(this->address_str()); - this->disconnect(); - return false; - } - this->gatt_connected_ = true; - auto view = table.view(); - for (auto *node : this->gatt_nodes_) { - node->on_connected(view); - if (this->state() != espbt::ClientState::ESTABLISHED) { - // The node tore the link down; remaining nodes get on_disconnected - // with no preceding on_connected, so leave a trace of why. - ESP_LOGW(TAG, "[%s] A node aborted the connection during setup", this->address_str()); + if (!this->has_legacy_nodes_()) { + ESP_LOGW(TAG, "[%s] Service table build failed; treating as failed discovery", this->address_str()); + this->gatt_backoff_.register_failure(this->address_str()); + this->disconnect(); return false; } + // Only the table build failed; legacy nodes read the base's services_ + // and keep the link. Gatt nodes catch the next connection. + ESP_LOGW(TAG, "[%s] Service table build failed; gatt nodes skip this connection", this->address_str()); + } else { + this->gatt_connected_ = true; + auto view = table.view(); + for (auto *node : this->gatt_nodes_) { + node->on_connected(view); + if (this->state() != espbt::ClientState::ESTABLISHED) { + // The node tore the link down; remaining nodes get on_disconnected + // with no preceding on_connected, so leave a trace of why. + ESP_LOGW(TAG, "[%s] A node aborted the connection during setup", this->address_str()); + return false; + } + } + this->gatt_backoff_.reset(); } // Promote so the legacy release condition can fire. for (auto *node : this->gatt_nodes_) node->node_state = espbt::ClientState::ESTABLISHED; - this->gatt_backoff_.reset(); return true; } @@ -338,7 +335,7 @@ int BLEClient::notify_characteristic(uint16_t handle, bool enable) { esp_err_t err = this->register_for_notify(handle); if (err == ESP_OK) this->pending_gatt_regs_[this->pending_gatt_reg_count_++] = handle; - return this->check_and_log_error_("register_for_notify", err); + return this->check_and_log_error_("esp_ble_gattc_register_for_notify", err); } return this->check_and_log_error_("esp_ble_gattc_unregister_for_notify", esp_ble_gattc_unregister_for_notify(this->gattc_if_, this->remote_bda_, handle)); diff --git a/esphome/components/ble_client/ble_client.h b/esphome/components/ble_client/ble_client.h index bb4a24a19b..94d53fd525 100644 --- a/esphome/components/ble_client/ble_client.h +++ b/esphome/components/ble_client/ble_client.h @@ -85,8 +85,10 @@ class BLEClient final : public BLEClientBase { static constexpr uint8_t MAX_PENDING_NOTIFY_REGS = 4; // Nodes on the neutral surface; fed the translated callbacks and - // auto-established after the on_connected fan-out. + // auto-established after the on_connected fan-out. Every gatt node is + // also in nodes_ (registration pushes into both). StaticVector gatt_nodes_; + bool has_legacy_nodes_() const { return this->nodes_.size() > this->gatt_nodes_.size(); } // Reconnect backoff after materializer failures. ConnectBackoff gatt_backoff_; // Bridge-initiated notify registrations awaiting REG_FOR_NOTIFY_EVT. diff --git a/esphome/components/bluetooth_connection/gatt_service_table_bluedroid.cpp b/esphome/components/bluetooth_connection/gatt_service_table_bluedroid.cpp index 4acc08fbaf..6bbb5cb2d7 100644 --- a/esphome/components/bluetooth_connection/gatt_service_table_bluedroid.cpp +++ b/esphome/components/bluetooth_connection/gatt_service_table_bluedroid.cpp @@ -117,7 +117,7 @@ bool BluedroidServiceTable::build(esp_gatt_if_t gattc_if, uint16_t conn_id, uint // The arrays share one block; carving stays aligned because each struct's // strictest member is the UUID and array sizes are multiples of it. - static_assert(alignof(ble_device_base::GattService) == alignof(ble_device_base::GattCharacteristic) && + static_assert(alignof(ble_device_base::GattService) >= alignof(ble_device_base::GattCharacteristic) && alignof(ble_device_base::GattCharacteristic) >= alignof(ble_device_base::GattDescriptor)); size_t svc_bytes = this->service_total_ * sizeof(ble_device_base::GattService); size_t char_bytes = char_total * sizeof(ble_device_base::GattCharacteristic); diff --git a/tests/components/bluetooth_proxy/test-passive.esp32-c6-idf.yaml b/tests/components/bluetooth_proxy/test-passive.esp32-c6-idf.yaml index fd11e4e06e..96f799b592 100644 --- a/tests/components/bluetooth_proxy/test-passive.esp32-c6-idf.yaml +++ b/tests/components/bluetooth_proxy/test-passive.esp32-c6-idf.yaml @@ -1,5 +1,6 @@ # Advertisement-only proxy on esp32 by explicit choice: no GATT backend is -# compiled (USE_BLE_GATT_CLIENT unset), which pins the USE_BLUETOOTH_PROXY_CONNECTIONS gating and the +# compiled (USE_BLE_GATT_CLIENT unset), which pins the +# USE_BLUETOOTH_PROXY_CONNECTIONS gating and the # address-scoped maintenance path that a connections build never exercises. # Under batch grouping the active default build is what runs; the standalone # compile of this fixture is what exercises the passive gating.