mirror of
https://github.com/esphome/esphome.git
synced 2026-09-30 00:10:22 +00:00
Merge branch 'neutral-ble-client' into radon-eye-single-node
This commit is contained in:
@@ -338,13 +338,13 @@ async def register_ble_node(var, config):
|
||||
|
||||
|
||||
def _request_gatt_node_build() -> None:
|
||||
"""Node storage plus, on esp32, the bridge and shared-materializer
|
||||
defines one neutral node needs compiled in."""
|
||||
"""Node storage and the one define meaning "the neutral node surface is
|
||||
compiled in", plus the esp32 bridge/materializer defines."""
|
||||
_request_node_slot()
|
||||
cg.add_define("USE_BLE_CLIENT_GATT_NODES")
|
||||
if CORE.is_esp32:
|
||||
# Deliberately not ble_device_base.request_gatt_client(): that would
|
||||
# claim a phantom backend slot on combined proxy builds.
|
||||
cg.add_define("USE_BLE_CLIENT_GATT_NODES")
|
||||
cg.add_define("USE_BLE_GATT_CLIENT")
|
||||
cg.add_define("USE_BLE_GATT_SERVICE_TABLE")
|
||||
|
||||
@@ -538,7 +538,9 @@ _request_node_slot = cg.slot_counter("ESPHOME_BLE_CLIENT_MAX_NODES")
|
||||
|
||||
|
||||
async def _to_code_gatt(config: ConfigType) -> cg.MockObj:
|
||||
_request_node_slot()
|
||||
# The engine always carries the node surface (the client itself owns the
|
||||
# baseline slot).
|
||||
_request_gatt_node_build()
|
||||
backend = await bluetooth_connection.new_gatt_backend(config)
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
@@ -2,13 +2,8 @@
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/ble_client/ble_client.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::ble_client {
|
||||
|
||||
|
||||
@@ -12,12 +12,9 @@
|
||||
#if defined(USE_BLE_GATT_CLIENT) && !defined(USE_ESP32)
|
||||
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "ble_client_gatt.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::ble_client {
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#ifdef USE_BLE_CLIENT_GATT_NODES
|
||||
#include "esphome/components/bluetooth_connection/bluetooth_connection.h"
|
||||
#include "esphome/components/bluetooth_connection/gatt_service_table_bluedroid.h"
|
||||
#endif
|
||||
|
||||
namespace esphome::ble_client {
|
||||
@@ -60,7 +61,8 @@ bool BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
|
||||
int err = param->reg_for_notify.status == ESP_GATT_OK ? 0 : param->reg_for_notify.status;
|
||||
for (auto *node : this->gatt_nodes_)
|
||||
node->on_notify_state(param->reg_for_notify.handle, true, err);
|
||||
// A retiring last registration must still release the cache.
|
||||
// A retiring last registration must still release the cache (the counter
|
||||
// was cleared above, before the node dispatch).
|
||||
this->maybe_release_services_();
|
||||
return true;
|
||||
}
|
||||
@@ -68,18 +70,20 @@ bool BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
|
||||
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);
|
||||
#ifdef USE_BLE_CLIENT_GATT_NODES
|
||||
// Before the legacy fan-out so gatt nodes resolve their handles before any
|
||||
// legacy trigger (e.g. on_connect at SEARCH_CMPL) fires - the neutral
|
||||
// engine's order.
|
||||
this->dispatch_gatt_event_(event, param);
|
||||
#endif
|
||||
for (auto *node : this->nodes_)
|
||||
node->gattc_event_handler(event, esp_gattc_if, param);
|
||||
this->maybe_release_services_();
|
||||
return true;
|
||||
}
|
||||
|
||||
void BLEClient::maybe_release_services_() {
|
||||
// The release frees the GATT cache that BLEClientBase's CCCD lookup still needs.
|
||||
// The last REG_FOR_NOTIFY event clears the counter before node dispatch, so the release still runs here.
|
||||
if (!this->services_.empty() && !this->notify_registration_pending() && this->all_nodes_established_()) {
|
||||
this->release_services();
|
||||
ESP_LOGD(TAG, "All clients established, services released");
|
||||
@@ -102,14 +106,10 @@ void BLEClient::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_p
|
||||
|
||||
void BLEClient::set_state(espbt::ClientState state) {
|
||||
BLEClientBase::set_state(state);
|
||||
// ESTABLISHED never flows through here (the base sets it internally); gatt
|
||||
// nodes (members of nodes_ too) are promoted after the on_connected fan-out.
|
||||
for (auto &node : nodes_)
|
||||
node->node_state = state;
|
||||
#ifdef USE_BLE_CLIENT_GATT_NODES
|
||||
// ESTABLISHED never flows through here (the base sets it internally);
|
||||
// gatt nodes are promoted explicitly after the on_connected fan-out.
|
||||
for (auto *node : this->gatt_nodes_)
|
||||
node->node_state = state;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool BLEClient::all_nodes_established_() {
|
||||
@@ -119,19 +119,12 @@ bool BLEClient::all_nodes_established_() {
|
||||
if (node->node_state != espbt::ClientState::ESTABLISHED)
|
||||
return false;
|
||||
}
|
||||
#ifdef USE_BLE_CLIENT_GATT_NODES
|
||||
for (auto *node : this->gatt_nodes_) {
|
||||
if (node->node_state != espbt::ClientState::ESTABLISHED)
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef USE_BLE_CLIENT_GATT_NODES
|
||||
|
||||
void BLEClient::register_gatt_node(BLEClientNode *node) {
|
||||
node->set_ble_client_parent(this);
|
||||
if (this->gatt_nodes_.size() == ESPHOME_BLE_CLIENT_MAX_NODES) {
|
||||
// push_back past capacity is a silent no-op; an undersized slot count
|
||||
// must be loud at boot, not an unresolvable node at runtime.
|
||||
@@ -139,22 +132,21 @@ void BLEClient::register_gatt_node(BLEClientNode *node) {
|
||||
return;
|
||||
}
|
||||
this->gatt_nodes_.push_back(node);
|
||||
// Membership in nodes_ covers the shared state bookkeeping (node_state,
|
||||
// release condition); gatt_nodes_ is the neutral fan-out subset.
|
||||
this->register_ble_node(node);
|
||||
}
|
||||
|
||||
bool BLEClient::take_pending_gatt_reg_(uint16_t handle) {
|
||||
bool found = false;
|
||||
// Erase every match: a stale duplicate must never swallow a later legacy
|
||||
// registration of the same handle.
|
||||
auto ®s = this->pending_gatt_regs_;
|
||||
for (auto it = regs.begin(); it != regs.end();) {
|
||||
if (*it == handle) {
|
||||
it = regs.erase(it);
|
||||
found = true;
|
||||
} else {
|
||||
++it;
|
||||
// No duplicates possible: notify_characteristic() refuses a re-push while
|
||||
// one is pending, so a single swap-with-last removal suffices.
|
||||
for (uint8_t i = 0; i < this->pending_gatt_reg_count_; i++) {
|
||||
if (this->pending_gatt_regs_[i] == handle) {
|
||||
this->pending_gatt_regs_[i] = this->pending_gatt_regs_[--this->pending_gatt_reg_count_];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
return false;
|
||||
}
|
||||
|
||||
void BLEClient::dispatch_gatt_event_(esp_gattc_cb_event_t event, esp_ble_gattc_cb_param_t *param) {
|
||||
@@ -199,18 +191,14 @@ void BLEClient::dispatch_gatt_event_(esp_gattc_cb_event_t event, esp_ble_gattc_c
|
||||
void BLEClient::handle_gatt_search_cmpl_(esp_gatt_status_t status) {
|
||||
// The base establishes without reading the search status; the neutral
|
||||
// contract treats a failed or empty discovery as a failed connection.
|
||||
uint16_t primary = 0;
|
||||
uint16_t secondary = 0;
|
||||
bool counted = status == ESP_GATT_OK &&
|
||||
esp_ble_gattc_get_attr_count(this->gattc_if_, this->conn_id_, ESP_GATT_DB_PRIMARY_SERVICE, 0x0001,
|
||||
0xFFFF, 0, &primary) == ESP_GATT_OK &&
|
||||
esp_ble_gattc_get_attr_count(this->gattc_if_, this->conn_id_, ESP_GATT_DB_SECONDARY_SERVICE, 0x0001,
|
||||
0xFFFF, 0, &secondary) == ESP_GATT_OK;
|
||||
uint16_t service_total = 0;
|
||||
bool counted = status == ESP_GATT_OK && bluetooth_connection::BluedroidServiceTable::count_services(
|
||||
this->gattc_if_, this->conn_id_, &service_total);
|
||||
// Stack-owned: freed on scope exit; nodes copy their handles during
|
||||
// on_connected() per the borrowed-table contract.
|
||||
bluetooth_connection::BluedroidServiceTable table;
|
||||
if (!counted || primary + secondary == 0 ||
|
||||
!table.build(this->gattc_if_, this->conn_id_, primary + secondary, this->connection_index_)) {
|
||||
if (!counted || service_total == 0 ||
|
||||
!table.build(this->gattc_if_, this->conn_id_, service_total, this->connection_index_)) {
|
||||
ESP_LOGW(TAG, "[%s] Service table is empty; treating as failed discovery", this->address_str());
|
||||
this->disconnect();
|
||||
return;
|
||||
@@ -233,7 +221,7 @@ void BLEClient::handle_gatt_search_cmpl_(esp_gatt_status_t status) {
|
||||
}
|
||||
|
||||
void BLEClient::on_disconnect_complete(esp_err_t reason) {
|
||||
this->pending_gatt_regs_.clear();
|
||||
this->pending_gatt_reg_count_ = 0;
|
||||
if (!this->gatt_connected_)
|
||||
return; // Never-established links report nothing (neutral parity).
|
||||
this->gatt_connected_ = false;
|
||||
@@ -241,45 +229,68 @@ void BLEClient::on_disconnect_complete(esp_err_t reason) {
|
||||
node->on_disconnected();
|
||||
}
|
||||
|
||||
int BLEClient::check_gatt_op_(const char *operation, esp_err_t err) {
|
||||
if (err != ESP_OK)
|
||||
this->log_gattc_warning_(operation, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
int BLEClient::write_characteristic(uint16_t handle, const uint8_t *data, uint16_t len, bool response) {
|
||||
if (this->conn_id_ == UNSET_CONN_ID)
|
||||
return ble_device_base::GATT_ERR_NOT_CONNECTED;
|
||||
return esp_ble_gattc_write_char(this->gattc_if_, this->conn_id_, handle, len, const_cast<uint8_t *>(data),
|
||||
response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP,
|
||||
ESP_GATT_AUTH_REQ_NONE);
|
||||
return this->check_gatt_op_(
|
||||
"esp_ble_gattc_write_char",
|
||||
esp_ble_gattc_write_char(this->gattc_if_, this->conn_id_, handle, len, const_cast<uint8_t *>(data),
|
||||
response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP,
|
||||
ESP_GATT_AUTH_REQ_NONE));
|
||||
}
|
||||
|
||||
int BLEClient::read_characteristic(uint16_t handle) {
|
||||
if (this->conn_id_ == UNSET_CONN_ID)
|
||||
return ble_device_base::GATT_ERR_NOT_CONNECTED;
|
||||
return esp_ble_gattc_read_char(this->gattc_if_, this->conn_id_, handle, ESP_GATT_AUTH_REQ_NONE);
|
||||
return this->check_gatt_op_("esp_ble_gattc_read_char",
|
||||
esp_ble_gattc_read_char(this->gattc_if_, this->conn_id_, handle, ESP_GATT_AUTH_REQ_NONE));
|
||||
}
|
||||
|
||||
int BLEClient::read_descriptor(uint16_t handle) {
|
||||
if (this->conn_id_ == UNSET_CONN_ID)
|
||||
return ble_device_base::GATT_ERR_NOT_CONNECTED;
|
||||
return esp_ble_gattc_read_char_descr(this->gattc_if_, this->conn_id_, handle, ESP_GATT_AUTH_REQ_NONE);
|
||||
return this->check_gatt_op_(
|
||||
"esp_ble_gattc_read_char_descr",
|
||||
esp_ble_gattc_read_char_descr(this->gattc_if_, this->conn_id_, handle, ESP_GATT_AUTH_REQ_NONE));
|
||||
}
|
||||
|
||||
int BLEClient::write_descriptor(uint16_t handle, const uint8_t *data, uint16_t len) {
|
||||
if (this->conn_id_ == UNSET_CONN_ID)
|
||||
return ble_device_base::GATT_ERR_NOT_CONNECTED;
|
||||
return esp_ble_gattc_write_char_descr(this->gattc_if_, this->conn_id_, handle, len, const_cast<uint8_t *>(data),
|
||||
ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||
return this->check_gatt_op_(
|
||||
"esp_ble_gattc_write_char_descr",
|
||||
esp_ble_gattc_write_char_descr(this->gattc_if_, this->conn_id_, handle, len, const_cast<uint8_t *>(data),
|
||||
ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE));
|
||||
}
|
||||
|
||||
int BLEClient::notify_characteristic(uint16_t handle, bool enable) {
|
||||
if (this->conn_id_ == UNSET_CONN_ID)
|
||||
return ble_device_base::GATT_ERR_NOT_CONNECTED;
|
||||
if (enable) {
|
||||
for (uint8_t i = 0; i < this->pending_gatt_reg_count_; i++) {
|
||||
if (this->pending_gatt_regs_[i] == handle) {
|
||||
ESP_LOGW(TAG, "[%s] Notify registration already pending for handle 0x%04x", this->address_str(), handle);
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
if (this->pending_gatt_reg_count_ == ESPHOME_BLE_CLIENT_MAX_NODES) {
|
||||
// Registering without a pending entry would let the base's automatic
|
||||
// CCCD write through; refuse loudly instead.
|
||||
ESP_LOGE(TAG, "[%s] Too many pending notify registrations", this->address_str());
|
||||
return ble_device_base::GATT_ERR_NO_MEMORY;
|
||||
}
|
||||
// Reuses the base helper so its pending count holds the service-release
|
||||
// until the registration completes; the completion is intercepted before
|
||||
// the base's automatic CCCD write.
|
||||
if (this->take_pending_gatt_reg_(handle))
|
||||
ESP_LOGW(TAG, "[%s] Duplicate notify registration for handle 0x%04x", this->address_str(), handle);
|
||||
esp_err_t err = this->register_for_notify(handle);
|
||||
if (err == ESP_OK)
|
||||
this->pending_gatt_regs_.push_back(handle);
|
||||
this->pending_gatt_regs_[this->pending_gatt_reg_count_++] = handle;
|
||||
return err;
|
||||
}
|
||||
return esp_ble_gattc_unregister_for_notify(this->gattc_if_, this->remote_bda_, handle);
|
||||
|
||||
@@ -10,10 +10,6 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#ifdef USE_BLE_CLIENT_GATT_NODES
|
||||
#include "esphome/components/bluetooth_connection/gatt_service_table_bluedroid.h"
|
||||
#endif
|
||||
|
||||
#include <esp_bt_defs.h>
|
||||
#include <esp_gap_ble_api.h>
|
||||
#include <esp_gatt_common_api.h>
|
||||
@@ -65,6 +61,7 @@ class BLEClient final : public BLEClientBase {
|
||||
/// Local registration only; per the neutral contract the CCCD write is the
|
||||
/// node's job (the legacy auto-CCCD is suppressed for these handles).
|
||||
int notify_characteristic(uint16_t handle, bool enable);
|
||||
// pair() comes from BLEClientBase, matching the neutral engine's.
|
||||
int unpair();
|
||||
#endif
|
||||
|
||||
@@ -72,6 +69,7 @@ class BLEClient final : public BLEClientBase {
|
||||
bool all_nodes_established_();
|
||||
void maybe_release_services_();
|
||||
#ifdef USE_BLE_CLIENT_GATT_NODES
|
||||
int check_gatt_op_(const char *operation, esp_err_t err);
|
||||
void dispatch_gatt_event_(esp_gattc_cb_event_t event, esp_ble_gattc_cb_param_t *param);
|
||||
void handle_gatt_search_cmpl_(esp_gatt_status_t status);
|
||||
bool take_pending_gatt_reg_(uint16_t handle);
|
||||
@@ -83,8 +81,10 @@ class BLEClient final : public BLEClientBase {
|
||||
// Nodes on the neutral surface; fed the translated callbacks and
|
||||
// auto-established after the on_connected fan-out.
|
||||
StaticVector<BLEClientNode *, ESPHOME_BLE_CLIENT_MAX_NODES> gatt_nodes_;
|
||||
// Bridge-initiated notify registrations awaiting REG_FOR_NOTIFY_EVT.
|
||||
std::vector<uint16_t> pending_gatt_regs_;
|
||||
// Bridge-initiated notify registrations awaiting REG_FOR_NOTIFY_EVT;
|
||||
// bounded by the node count (one in-flight registration per node).
|
||||
uint16_t pending_gatt_regs_[ESPHOME_BLE_CLIENT_MAX_NODES];
|
||||
uint8_t pending_gatt_reg_count_{0};
|
||||
// on_connected fan-out started; on_disconnected is owed at teardown.
|
||||
bool gatt_connected_{false};
|
||||
#endif
|
||||
|
||||
@@ -24,7 +24,7 @@ class BLEClient;
|
||||
|
||||
class BLEClientNode {
|
||||
public:
|
||||
#if defined(USE_BLE_CLIENT_GATT_NODES) || (defined(USE_BLE_GATT_CLIENT) && !defined(USE_ESP32))
|
||||
#ifdef USE_BLE_CLIENT_GATT_NODES
|
||||
// Neutral surface, delivered by both engines. The table is borrowed
|
||||
// backend storage, valid only for the duration of on_connected(): copy the
|
||||
// handles you need. All nodes see all completions/notifications; filter by
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#if defined(USE_BLE_CLIENT_GATT_NODES) || (defined(USE_BLE_GATT_CLIENT) && !defined(USE_ESP32))
|
||||
#ifdef USE_BLE_CLIENT_GATT_NODES
|
||||
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
// One of the two resolves per build; both define class Automation's TAG box.
|
||||
#include "automation.h"
|
||||
#include "automation_gatt.h"
|
||||
// One of the two engine headers resolves per build.
|
||||
#include "ble_client.h"
|
||||
#include "ble_client_gatt.h"
|
||||
#include "ble_client_node.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
namespace esphome::ble_client {
|
||||
|
||||
static const char *const BLE_WRITE_TAG = "ble_client.automation";
|
||||
|
||||
// Maximum bytes to log in hex format for BLE writes (many logging buffers are 256 chars)
|
||||
static constexpr size_t BLE_WRITE_MAX_LOG_BYTES = 64;
|
||||
|
||||
@@ -75,20 +77,20 @@ template<typename... Ts> class BLEClientWriteAction final : public Action<Ts...>
|
||||
// handle is armed before the backend is touched.
|
||||
bool write(const uint8_t *data, size_t len) {
|
||||
if (!this->ble_client_->connected()) {
|
||||
esph_log_w(Automation::TAG, "Cannot write to BLE characteristic - not connected");
|
||||
esph_log_w(BLE_WRITE_TAG, "Cannot write to BLE characteristic - not connected");
|
||||
return false;
|
||||
}
|
||||
if (!this->resolved_) {
|
||||
esph_log_w(Automation::TAG, "Cannot write to BLE characteristic - characteristic was not resolved");
|
||||
esph_log_w(BLE_WRITE_TAG, "Cannot write to BLE characteristic - characteristic was not resolved");
|
||||
return false;
|
||||
}
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||
char hex_buf[format_hex_pretty_size(BLE_WRITE_MAX_LOG_BYTES)];
|
||||
esph_log_vv(Automation::TAG, "Will write %d bytes: %s", len, format_hex_pretty_to(hex_buf, data, len));
|
||||
esph_log_vv(BLE_WRITE_TAG, "Will write %d bytes: %s", len, format_hex_pretty_to(hex_buf, data, len));
|
||||
#endif
|
||||
int err = this->ble_client_->write_characteristic(this->char_handle_, data, len, this->write_response_);
|
||||
if (err != 0) {
|
||||
esph_log_e(Automation::TAG, "Error writing to characteristic: %d!", err);
|
||||
esph_log_e(BLE_WRITE_TAG, "Error writing to characteristic: %d!", err);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -101,7 +103,7 @@ template<typename... Ts> class BLEClientWriteAction final : public Action<Ts...>
|
||||
if (chr == nullptr) {
|
||||
char char_buf[ble_device_base::UUID_STR_LEN];
|
||||
char service_buf[ble_device_base::UUID_STR_LEN];
|
||||
esph_log_w("ble_write_action", "Characteristic %s was not found in service %s", this->char_uuid_.to_str(char_buf),
|
||||
esph_log_w(BLE_WRITE_TAG, "Characteristic %s was not found in service %s", this->char_uuid_.to_str(char_buf),
|
||||
this->service_uuid_.to_str(service_buf));
|
||||
return;
|
||||
}
|
||||
@@ -111,13 +113,13 @@ template<typename... Ts> class BLEClientWriteAction final : public Action<Ts...>
|
||||
this->write_response_ = false;
|
||||
} else {
|
||||
char char_buf[ble_device_base::UUID_STR_LEN];
|
||||
esph_log_e(Automation::TAG, "Characteristic %s does not allow writing", this->char_uuid_.to_str(char_buf));
|
||||
esph_log_e(BLE_WRITE_TAG, "Characteristic %s does not allow writing", this->char_uuid_.to_str(char_buf));
|
||||
return;
|
||||
}
|
||||
this->char_handle_ = chr->value_handle;
|
||||
this->resolved_ = true;
|
||||
char char_buf[ble_device_base::UUID_STR_LEN];
|
||||
esph_log_d(Automation::TAG, "Found characteristic %s on device %s", this->char_uuid_.to_str(char_buf),
|
||||
esph_log_d(BLE_WRITE_TAG, "Found characteristic %s on device %s", this->char_uuid_.to_str(char_buf),
|
||||
this->ble_client_->address_str());
|
||||
}
|
||||
|
||||
@@ -135,13 +137,13 @@ template<typename... Ts> class BLEClientWriteAction final : public Action<Ts...>
|
||||
if (!this->resolved_ || handle != this->char_handle_) {
|
||||
// A parked chain waiting on a completion that never matches would
|
||||
// otherwise stall silently until disconnect.
|
||||
esph_log_d(Automation::TAG, "Write result for handle 0x%04x ignored, waiting on 0x%04x", handle,
|
||||
esph_log_d(BLE_WRITE_TAG, "Write result for handle 0x%04x ignored, waiting on 0x%04x", handle,
|
||||
this->char_handle_);
|
||||
return;
|
||||
}
|
||||
if (error != 0) {
|
||||
// Continue the chain (legacy parity) but leave a breadcrumb.
|
||||
esph_log_w(Automation::TAG, "Write completed with status %d", error);
|
||||
esph_log_w(BLE_WRITE_TAG, "Write completed with status %d", error);
|
||||
}
|
||||
this->ble_client_->run_later([this]() { this->play_next_tuple_(this->var_); });
|
||||
}
|
||||
@@ -163,4 +165,4 @@ template<typename... Ts> class BLEClientWriteAction final : public Action<Ts...>
|
||||
|
||||
} // namespace esphome::ble_client
|
||||
|
||||
#endif // USE_BLE_CLIENT_GATT_NODES || (USE_BLE_GATT_CLIENT && !USE_ESP32)
|
||||
#endif // USE_BLE_CLIENT_GATT_NODES
|
||||
|
||||
@@ -76,6 +76,20 @@ bool BluedroidServiceTable::walk_(ServiceFn &&on_service, CharFn &&on_char, Desc
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BluedroidServiceTable::count_services(esp_gatt_if_t gattc_if, uint16_t conn_id, uint16_t *total) {
|
||||
uint16_t primary = 0;
|
||||
uint16_t secondary = 0;
|
||||
if (esp_ble_gattc_get_attr_count(gattc_if, conn_id, ESP_GATT_DB_PRIMARY_SERVICE, 0x0001, 0xFFFF, 0, &primary) !=
|
||||
ESP_GATT_OK ||
|
||||
esp_ble_gattc_get_attr_count(gattc_if, conn_id, ESP_GATT_DB_SECONDARY_SERVICE, 0x0001, 0xFFFF, 0, &secondary) !=
|
||||
ESP_GATT_OK) {
|
||||
// A failed count must not read as an authoritative empty database.
|
||||
return false;
|
||||
}
|
||||
*total = primary + secondary;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BluedroidServiceTable::build(esp_gatt_if_t gattc_if, uint16_t conn_id, uint16_t service_total, uint8_t log_index) {
|
||||
this->free();
|
||||
this->gattc_if_ = gattc_if;
|
||||
|
||||
@@ -19,10 +19,13 @@ class BluedroidServiceTable {
|
||||
public:
|
||||
~BluedroidServiceTable() { this->free(); }
|
||||
|
||||
/// Two-pass build from the stack's cached database. service_total MUST be
|
||||
/// the esp_ble_gattc_get_attr_count(PRIMARY)+(SECONDARY) total, never the
|
||||
/// SEARCH_RES event count. log_index labels warnings. Frees any previous
|
||||
/// table first; on failure the table is left empty.
|
||||
/// The service count build() requires: the stack's PRIMARY+SECONDARY
|
||||
/// attribute totals, never the SEARCH_RES event count.
|
||||
static bool count_services(esp_gatt_if_t gattc_if, uint16_t conn_id, uint16_t *total);
|
||||
|
||||
/// Two-pass build from the stack's cached database (service_total from
|
||||
/// count_services()). log_index labels warnings. Frees any previous table
|
||||
/// first; on failure the table is left empty.
|
||||
bool build(esp_gatt_if_t gattc_if, uint16_t conn_id, uint16_t service_total, uint8_t log_index);
|
||||
|
||||
// The view is carved from the storage block and the counts on each call
|
||||
@@ -45,6 +48,7 @@ class BluedroidServiceTable {
|
||||
RAMAllocator<uint8_t> allocator(RAMAllocator<uint8_t>::ALLOC_INTERNAL);
|
||||
allocator.deallocate(this->storage_, 0);
|
||||
this->storage_ = nullptr;
|
||||
this->service_total_ = 0;
|
||||
this->char_total_ = 0;
|
||||
this->desc_total_ = 0;
|
||||
}
|
||||
|
||||
@@ -488,6 +488,7 @@
|
||||
#define ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT 1
|
||||
#define USE_BLE_SCAN_RESPONSE_MERGER
|
||||
#define USE_BLE_GATT_CLIENT
|
||||
#define USE_BLE_CLIENT_GATT_NODES
|
||||
#define ESPHOME_BLE_GATT_CLIENT_COUNT 3
|
||||
#define ESPHOME_BLE_CLIENT_MAX_NODES 1
|
||||
#define USE_RP2040_VARIANT_RP2040
|
||||
|
||||
Reference in New Issue
Block a user