3#if defined(USE_ESP32_BLE) && defined(USE_BLE_GATT_CLIENT)
7#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
23static const char *
const TAG =
"bluetooth_connection.bluedroid";
25using ble_device_base::FAST_CONN_TIMEOUT;
26using ble_device_base::FAST_MAX_CONN_INTERVAL;
27using ble_device_base::FAST_MIN_CONN_INTERVAL;
28using ble_device_base::MEDIUM_CONN_TIMEOUT;
29using ble_device_base::MEDIUM_MAX_CONN_INTERVAL;
30using ble_device_base::MEDIUM_MIN_CONN_INTERVAL;
31using esp32_ble_tracker::ClientState;
32using esp32_ble_tracker::ConnectionType;
42 static uint8_t connection_index = 0;
50 auto down_st = this->
state();
51 if (down_st != ClientState::IDLE && down_st != ClientState::INIT) {
59 auto st = this->
state();
60 if (st == ClientState::INIT) {
63 auto ret = esp_ble_gattc_app_register(this->
app_id);
65 ESP_LOGE(TAG,
"gattc app register failed: app_id=%d code=%d", this->
app_id,
ret);
74 ESP_LOGE(TAG,
"[%d] Timeout waiting for teardown, forcing IDLE", this->
connection_index_);
84 if (this->
state() == ClientState::IDLE) {
93 ESP_LOGE(TAG,
" Registration failed; if the error was ESP_GATT_NO_RESOURCES, reduce the connection slots");
102 if (this->
state() != ClientState::IDLE) {
104 return ESP_GATT_BUSY;
110 this->
set_state(ClientState::DISCOVERED);
115 auto st = this->
state();
116 if (st == ClientState::CONNECTING || st == ClientState::CONNECTED || st == ClientState::ESTABLISHED) {
120 if (st == ClientState::DISCONNECTING) {
121 ESP_LOGW(TAG,
"[%d] Cannot connect, still waiting for CLOSE_EVT", this->
connection_index_);
131 this->
set_state(ClientState::CONNECTING);
135 esp_ble_gap_set_prefer_conn_params(this->
remote_bda_, FAST_MIN_CONN_INTERVAL,
136 FAST_MAX_CONN_INTERVAL, 0, FAST_CONN_TIMEOUT));
139 esp_ble_gap_set_prefer_conn_params(this->
remote_bda_, MEDIUM_MIN_CONN_INTERVAL,
140 MEDIUM_MAX_CONN_INTERVAL, 0, MEDIUM_CONN_TIMEOUT));
153 auto st = this->
state();
154 if (st == ClientState::DISCONNECTING) {
159 if (st == ClientState::IDLE) {
160 return ble_device_base::GATT_ERR_NOT_CONNECTED;
162 if (st == ClientState::DISCOVERED) {
165 return ble_device_base::GATT_ERR_NOT_CONNECTED;
192 ESP_LOGE(TAG,
"[%d] esp_ble_gattc_close error: %d", this->
connection_index_, err);
210 return ble_device_base::GATT_ERR_NOT_CONNECTED;
239 return ble_device_base::GATT_ERR_NOT_CONNECTED;
242 handle, ESP_GATT_AUTH_REQ_NONE));
247 return ble_device_base::GATT_ERR_NOT_CONNECTED;
251 "esp_ble_gattc_write_char",
252 esp_ble_gattc_write_char(this->
gattc_if_, this->
conn_id_, handle,
len,
const_cast<uint8_t *
>(data),
253 response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP,
254 ESP_GATT_AUTH_REQ_NONE));
259 return ble_device_base::GATT_ERR_NOT_CONNECTED;
262 "esp_ble_gattc_read_char_descr",
263 esp_ble_gattc_read_char_descr(this->
gattc_if_, this->
conn_id_, handle, ESP_GATT_AUTH_REQ_NONE));
268 return ble_device_base::GATT_ERR_NOT_CONNECTED;
271 "esp_ble_gattc_write_char_descr",
272 esp_ble_gattc_write_char_descr(this->
gattc_if_, this->
conn_id_, handle,
len,
const_cast<uint8_t *
>(data),
273 ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE));
278 return ble_device_base::GATT_ERR_NOT_CONNECTED;
291 return ble_device_base::GATT_ERR_NOT_CONNECTED;
293 return esp_ble_set_encryption(this->
remote_bda_, ESP_BLE_SEC_ENCRYPT);
305#ifndef CONFIG_BT_GATTC_CACHE_NVS_FLASH
318 return memcmp(addr, this->
remote_bda_,
sizeof(esp_bd_addr_t)) == 0;
330 this->
set_state(ClientState::DISCONNECTING);
336 uint16_t timeout,
const char *param_type) {
337 esp_ble_conn_update_params_t conn_params = {{0}};
338 memcpy(conn_params.bda, this->remote_bda_,
sizeof(esp_bd_addr_t));
339 conn_params.min_int = min_interval;
340 conn_params.max_int = max_interval;
341 conn_params.latency = latency;
342 conn_params.timeout = timeout;
344 return this->
check_and_log_error_(
"esp_ble_gap_update_conn_params", esp_ble_gap_update_conn_params(&conn_params));
355 ESP_LOGW(TAG,
"[%d] %s failed, status=%d", this->
connection_index_, operation, code);
362 this->
update_conn_params_(MEDIUM_MIN_CONN_INTERVAL, MEDIUM_MAX_CONN_INTERVAL, 0, MEDIUM_CONN_TIMEOUT,
"medium");
363 if (
status != ESP_GATT_OK) {
369 uint16_t primary = 0;
370 uint16_t secondary = 0;
371 auto primary_status = esp_ble_gattc_get_attr_count(this->
gattc_if_, this->
conn_id_, ESP_GATT_DB_PRIMARY_SERVICE,
372 0x0001, 0xFFFF, 0, &primary);
373 auto secondary_status = esp_ble_gattc_get_attr_count(this->
gattc_if_, this->
conn_id_, ESP_GATT_DB_SECONDARY_SERVICE,
374 0x0001, 0xFFFF, 0, &secondary);
375 if (primary_status != ESP_GATT_OK || secondary_status != ESP_GATT_OK) {
377 auto count_status = primary_status != ESP_GATT_OK ? primary_status : secondary_status;
394#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
419 if (api_conn ==
nullptr) {
432 esp_gattc_service_elem_t service_result;
433 uint16_t svc_count = 1;
434 esp_gatt_status_t svc_status = esp_ble_gattc_get_service(this->
gattc_if_, this->
conn_id_,
nullptr, &service_result,
436 if (svc_status != ESP_GATT_OK || svc_count == 0) {
437 ESP_LOGE(TAG,
"[%d] [%s] Service walk failed (service %d), aborting stream", conn.
connection_index_,
442 uint16_t total_char_count = 0;
443 auto char_count_status =
444 esp_ble_gattc_get_attr_count(this->
gattc_if_, this->
conn_id_, ESP_GATT_DB_CHARACTERISTIC,
445 service_result.start_handle, service_result.end_handle, 0, &total_char_count);
446 if (char_count_status != ESP_GATT_OK) {
454 if (!resp.
services.empty() && current_size + estimated_size > MAX_PACKET_SIZE) {
459 auto &service_resp = resp.
services.back();
462 service_resp.handle = service_result.start_handle;
464 if (total_char_count > 0) {
465 service_resp.characteristics.init(total_char_count);
466 uint16_t char_offset = 0;
467 esp_gattc_char_elem_t char_result;
470 while (char_offset < total_char_count) {
472 auto char_status = esp_ble_gattc_get_all_char(this->
gattc_if_, this->
conn_id_, service_result.start_handle,
473 service_result.end_handle, &char_result, &cc, char_offset);
474 if (char_status != ESP_GATT_OK || cc == 0) {
481 service_resp.characteristics.emplace_back();
482 auto &characteristic_resp = service_resp.characteristics.back();
483 fill_gatt_uuid(characteristic_resp.uuid, characteristic_resp.short_uuid,
485 characteristic_resp.handle = char_result.char_handle;
486 characteristic_resp.properties = char_result.properties;
488 uint16_t total_desc_count = 0;
489 auto desc_count_status = esp_ble_gattc_get_attr_count(this->
gattc_if_, this->
conn_id_, ESP_GATT_DB_DESCRIPTOR,
490 0, 0, char_result.char_handle, &total_desc_count);
491 if (desc_count_status != ESP_GATT_OK) {
498 if (total_desc_count > 0) {
499 characteristic_resp.descriptors.init(total_desc_count);
500 uint16_t desc_offset = 0;
501 esp_gattc_descr_elem_t desc_result;
502 while (desc_offset < total_desc_count) {
504 auto desc_status = esp_ble_gattc_get_all_descr(this->
gattc_if_, this->
conn_id_, char_result.char_handle,
505 &desc_result, &dc, desc_offset);
506 if (desc_status != ESP_GATT_OK || dc == 0) {
511 characteristic_resp.descriptors.emplace_back();
512 auto &descriptor_resp = characteristic_resp.descriptors.back();
515 descriptor_resp.handle = desc_result.handle;
531 if (!api_conn->send_message(resp)) {
543 auto st = this->
state();
544 if (st == ClientState::IDLE) {
547 ESP_LOGD(TAG,
"[%d] OPEN_EVT in IDLE state (status=%d)", this->
connection_index_, param->open.status);
548 if (param->open.status == ESP_GATT_OK || param->open.status == ESP_GATT_ALREADY_OPEN) {
554 if (st != ClientState::CONNECTING) {
557 if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN) {
573 this->
set_state(ClientState::ESTABLISHED);
584 esp_ble_gattc_search_service(this->
gattc_if_, param->open.conn_id,
nullptr)) == 0) {
598 if (param->disconnect.reason == ESP_GATT_CONN_TERMINATE_PEER_USER && this->state() == ClientState::CONNECTED) {
601 ESP_LOGD(TAG,
"[%d] DISCONNECT_EVT reason=0x%02x", this->
connection_index_, param->disconnect.reason);
603 if (this->
state() == ClientState::IDLE) {
616 esp_ble_gattc_cb_param_t *param) {
617 if (event == ESP_GATTC_REG_EVT && this->
app_id != param->reg.app_id)
619 if (event != ESP_GATTC_REG_EVT && esp_gattc_if != ESP_GATT_IF_NONE && esp_gattc_if != this->
gattc_if_)
623 case ESP_GATTC_REG_EVT: {
624 if (param->reg.status == ESP_GATT_OK) {
627 ESP_LOGE(TAG,
"[%d] gattc app registration failed, status=%d", this->
connection_index_, param->reg.status);
632 case ESP_GATTC_CONNECT_EVT: {
635 this->
conn_id_ = param->connect.conn_id;
637 auto ret = esp_ble_gattc_send_mtu_req(this->
gattc_if_, param->connect.conn_id);
645 case ESP_GATTC_OPEN_EVT: {
651 case ESP_GATTC_CFG_MTU_EVT: {
652 if (this->
conn_id_ != param->cfg_mtu.conn_id)
654 if (param->cfg_mtu.status != ESP_GATT_OK) {
664 true, param->cfg_mtu.status == ESP_GATT_OK ? param->cfg_mtu.mtu : ble_device_base::DEFAULT_ATT_MTU, 0);
671 case ESP_GATTC_DISCONNECT_EVT: {
672 if (!this->
check_addr_(param->disconnect.remote_bda))
677 case ESP_GATTC_CLOSE_EVT: {
678 if (this->
conn_id_ != param->close.conn_id)
687 case ESP_GATTC_SEARCH_CMPL_EVT: {
688 if (this->
conn_id_ != param->search_cmpl.conn_id)
691 if (this->
state() == ClientState::DISCONNECTING) {
699 this->
set_state(ClientState::ESTABLISHED);
703 case ESP_GATTC_READ_CHAR_EVT:
704 case ESP_GATTC_READ_DESCR_EVT: {
705 if (this->
conn_id_ != param->read.conn_id)
707 bool ok = param->read.status == ESP_GATT_OK;
709 ok ? param->read.value_len : 0, ok ? 0 : param->read.status);
712 case ESP_GATTC_WRITE_CHAR_EVT:
713 case ESP_GATTC_WRITE_DESCR_EVT: {
714 if (this->
conn_id_ != param->write.conn_id)
717 param->write.status == ESP_GATT_OK ? 0 : param->write.status);
720 case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
722 param->reg_for_notify.status == ESP_GATT_OK ? 0 : param->reg_for_notify.status);
725 case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
727 param->unreg_for_notify.handle,
false,
728 param->unreg_for_notify.status == ESP_GATT_OK ? 0 : param->unreg_for_notify.status);
731 case ESP_GATTC_NOTIFY_EVT: {
732 if (this->
conn_id_ != param->notify.conn_id)
734 ESP_LOGV(TAG,
"[%d] NOTIFY_EVT handle=0x%2X", this->
connection_index_, param->notify.handle);
746 case ESP_GAP_BLE_SEC_REQ_EVT: {
747 if (!this->
check_addr_(param->ble_security.auth_cmpl.bd_addr))
752 esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr,
true));
758 case ESP_GAP_BLE_AUTH_CMPL_EVT: {
759 if (!this->
check_addr_(param->ble_security.auth_cmpl.bd_addr))
762 param->ble_security.auth_cmpl.success ? 0 : param->ble_security.auth_cmpl.fail_reason);
void mark_failed()
Mark this component as failed.
void enable_loop()
Enable this component's loop.
void disable_loop()
Disable this component's loop.
uint32_t calculate_size() const
std::vector< BluetoothGATTService > services
static ESPBTUUID from_uuid(esp_bt_uuid_t uuid)
Source compatibility with the historical esp32_ble API (esp32 builds only).
virtual void on_notify_state(uint16_t handle, bool enabled, int error)
virtual void on_notify_data(uint16_t handle, const uint8_t *data, uint16_t len)
virtual void on_write_result(uint16_t handle, int error)
virtual void on_service_discovery_done(int error)
virtual void on_pairing_result(int status)
virtual void on_connection_state(bool connected, uint16_t mtu, int error)
virtual void on_read_result(uint16_t handle, const uint8_t *data, uint16_t len, int error)
bool check_addr_(const esp_bd_addr_t &addr) const
void disconnect() override
uint32_t disconnecting_started_
int handle_search_cmpl_(esp_gatt_status_t status)
esp_err_t update_conn_params_(uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t timeout, const char *param_type)
int read_descriptor(uint16_t handle)
SearchState search_state_
esp_bd_addr_t remote_bda_
ble_device_base::GattClientListener * listener_
void handle_open_evt_(esp_ble_gattc_cb_param_t *param)
int write_descriptor(uint16_t handle, const uint8_t *data, uint16_t len)
uint8_t remote_addr_type_
int write_characteristic(uint16_t handle, const uint8_t *data, uint16_t len, bool response)
void handle_disconnect_evt_(esp_ble_gattc_cb_param_t *param)
bool cancel_gatt_disconnect()
static constexpr uint16_t UNSET_CONN_ID
esp32_ble_tracker::ConnectionType connection_type_
uint8_t connection_index_
void unconditional_disconnect_()
int update_connection_params(uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t timeout)
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override
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 dump_config() override
void deliver_pending_search_()
void stream_service_batch(BluetoothConnection &conn)
In-place service streamer (the proxy wrapper detects and prefers it): builds one api response batch d...
int read_characteristic(uint16_t handle)
int check_and_log_error_(const char *operation, esp_err_t err)
int notify_characteristic(uint16_t handle, bool enable)
void set_disconnecting_()
void log_gattc_warning_(const char *operation, int code)
void abort_service_stream(conn_err_t err)
Streamer abort: latch the GATT cause, park the cursor, tear down.
bool batch_stalled_
Set while a refused batch is retrying, so only the first one warns.
void send_services_done_()
Send services-done and settle the cursor: DONE when it lands (or no subscriber), SERVICES_DONE_PENDIN...
void park_service_stream_()
Park the stream without services-done and free any held table: an interrupted stream must never be de...
bluetooth_proxy::BluetoothProxy * proxy_
uint8_t connection_index_
void note_batch_stalled_()
Warn on the stall's leading edge only.
char address_str_[MAC_ADDRESS_PRETTY_BUFFER_SIZE]
api::APIConnection * get_api_connection()
bool client_supports_efficient_uuids() const
Whether the subscribed API client understands 16/32-bit UUID fields.
ClientState state() const
virtual void set_state(ClientState st)
Set the client state with IDLE handling (clears want_disconnect_).
bool disconnect_pending() const
void uint64_to_mac_msb_first(uint64_t address, uint8_t out[6])
Unpack a uint64 BLE address into printable (MSB-first) byte order — the order bd_addr_t / esp_bd_addr...
size_t estimate_service_size(uint16_t char_count, bool use_efficient_uuids)
Estimate the wire size of a service (service overhead + its characteristics, assuming 128-bit UUIDs a...
BatchClose close_service_batch(api::BluetoothGATTGetServicesResponse &resp, size_t ¤t_size, int16_t &send_service, uint8_t connection_index, const char *address_str)
Close out the service just packed into resp (account its actual wire size, advance the cursor) and de...
void fill_gatt_uuid(std::array< uint64_t, 2 > &uuid_128, uint32_t &short_uuid, const ble_device_base::ESPBTUUID &uuid, bool use_efficient_uuids)
Fill the UUID in the appropriate wire format based on client support and UUID type (128-bit array for...
uint32_t IRAM_ATTR HOT millis()
spi_device_handle_t handle