3#if defined(USE_ESP32_BLE) && defined(USE_BLE_GATT_CLIENT)
7#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
23static const char *
const TAG =
"bluetooth_connection";
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;
51 auto st = this->
state();
52 if (st == ClientState::INIT) {
55 auto ret = esp_ble_gattc_app_register(this->
app_id);
57 ESP_LOGE(TAG,
"gattc app register failed: app_id=%d code=%d", this->
app_id,
ret);
66 ESP_LOGE(TAG,
"[%d] Timeout waiting for teardown, forcing IDLE", this->
connection_index_);
76 if (this->
state() == ClientState::IDLE) {
85 auto st = this->
state();
86 if (st != ClientState::IDLE && st != ClientState::INIT) {
101 ESP_LOGE(TAG,
" Registration failed; if the error was ESP_GATT_NO_RESOURCES, reduce the connection slots");
108 if (this->
gattc_if_ == ESP_GATT_IF_NONE) {
110 ESP_LOGW(TAG,
"[%d] Connect rejected, GATT app not registered", this->
connection_index_);
111 return ble_device_base::GATT_ERR_NOT_CONNECTED;
115 if (this->
state() != ClientState::IDLE) {
117 return ESP_GATT_BUSY;
123 this->
set_state(ClientState::DISCOVERED);
128 auto st = this->
state();
129 if (st == ClientState::CONNECTING || st == ClientState::CONNECTED || st == ClientState::ESTABLISHED) {
133 if (st == ClientState::DISCONNECTING) {
134 ESP_LOGW(TAG,
"[%d] Cannot connect, still waiting for CLOSE_EVT", this->
connection_index_);
144 this->
set_state(ClientState::CONNECTING);
148 esp_ble_gap_set_prefer_conn_params(this->
remote_bda_, FAST_MIN_CONN_INTERVAL,
149 FAST_MAX_CONN_INTERVAL, 0, FAST_CONN_TIMEOUT));
152 esp_ble_gap_set_prefer_conn_params(this->
remote_bda_, MEDIUM_MIN_CONN_INTERVAL,
153 MEDIUM_MAX_CONN_INTERVAL, 0, MEDIUM_CONN_TIMEOUT));
166 auto st = this->
state();
167 if (st == ClientState::DISCONNECTING) {
172 if (st == ClientState::IDLE) {
173 return ble_device_base::GATT_ERR_NOT_CONNECTED;
175 if (st == ClientState::DISCOVERED) {
178 return ble_device_base::GATT_ERR_NOT_CONNECTED;
205 ESP_LOGE(TAG,
"[%d] esp_ble_gattc_close error: %d", this->
connection_index_, err);
223 return ble_device_base::GATT_ERR_NOT_CONNECTED;
252 return ble_device_base::GATT_ERR_NOT_CONNECTED;
255 handle, ESP_GATT_AUTH_REQ_NONE));
260 return ble_device_base::GATT_ERR_NOT_CONNECTED;
264 "esp_ble_gattc_write_char",
265 esp_ble_gattc_write_char(this->
gattc_if_, this->
conn_id_, handle,
len,
const_cast<uint8_t *
>(data),
266 response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP,
267 ESP_GATT_AUTH_REQ_NONE));
272 return ble_device_base::GATT_ERR_NOT_CONNECTED;
275 "esp_ble_gattc_read_char_descr",
276 esp_ble_gattc_read_char_descr(this->
gattc_if_, this->
conn_id_, handle, ESP_GATT_AUTH_REQ_NONE));
281 return ble_device_base::GATT_ERR_NOT_CONNECTED;
284 "esp_ble_gattc_write_char_descr",
285 esp_ble_gattc_write_char_descr(this->
gattc_if_, this->
conn_id_, handle,
len,
const_cast<uint8_t *
>(data),
286 ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE));
291 return ble_device_base::GATT_ERR_NOT_CONNECTED;
304 return ble_device_base::GATT_ERR_NOT_CONNECTED;
306 return esp_ble_set_encryption(this->
remote_bda_, ESP_BLE_SEC_ENCRYPT);
318#ifndef CONFIG_BT_GATTC_CACHE_NVS_FLASH
331 return memcmp(addr, this->
remote_bda_,
sizeof(esp_bd_addr_t)) == 0;
343 this->
set_state(ClientState::DISCONNECTING);
349 uint16_t timeout,
const char *param_type) {
350 esp_ble_conn_update_params_t conn_params = {{0}};
351 memcpy(conn_params.bda, this->remote_bda_,
sizeof(esp_bd_addr_t));
352 conn_params.min_int = min_interval;
353 conn_params.max_int = max_interval;
354 conn_params.latency = latency;
355 conn_params.timeout = timeout;
357 return this->
check_and_log_error_(
"esp_ble_gap_update_conn_params", esp_ble_gap_update_conn_params(&conn_params));
368 ESP_LOGW(TAG,
"[%d] %s failed, status=%d", this->
connection_index_, operation, code);
375 this->
update_conn_params_(MEDIUM_MIN_CONN_INTERVAL, MEDIUM_MAX_CONN_INTERVAL, 0, MEDIUM_CONN_TIMEOUT,
"medium");
376 if (
status != ESP_GATT_OK) {
382 uint16_t primary = 0;
383 uint16_t secondary = 0;
384 auto primary_status = esp_ble_gattc_get_attr_count(this->
gattc_if_, this->
conn_id_, ESP_GATT_DB_PRIMARY_SERVICE,
385 0x0001, 0xFFFF, 0, &primary);
386 auto secondary_status = esp_ble_gattc_get_attr_count(this->
gattc_if_, this->
conn_id_, ESP_GATT_DB_SECONDARY_SERVICE,
387 0x0001, 0xFFFF, 0, &secondary);
388 if (primary_status != ESP_GATT_OK || secondary_status != ESP_GATT_OK) {
390 auto count_status = primary_status != ESP_GATT_OK ? primary_status : secondary_status;
407#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
432 if (api_conn ==
nullptr) {
445 esp_gattc_service_elem_t service_result;
446 uint16_t svc_count = 1;
447 esp_gatt_status_t svc_status = esp_ble_gattc_get_service(this->
gattc_if_, this->
conn_id_,
nullptr, &service_result,
449 if (svc_status != ESP_GATT_OK || svc_count == 0) {
450 ESP_LOGE(TAG,
"[%d] [%s] Service walk failed (service %d), aborting stream", conn.
connection_index_,
455 uint16_t total_char_count = 0;
456 auto char_count_status =
457 esp_ble_gattc_get_attr_count(this->
gattc_if_, this->
conn_id_, ESP_GATT_DB_CHARACTERISTIC,
458 service_result.start_handle, service_result.end_handle, 0, &total_char_count);
459 if (char_count_status != ESP_GATT_OK) {
467 if (!resp.
services.empty() && current_size + estimated_size > MAX_PACKET_SIZE) {
472 auto &service_resp = resp.
services.back();
475 service_resp.handle = service_result.start_handle;
477 if (total_char_count > 0) {
478 service_resp.characteristics.init(total_char_count);
479 uint16_t char_offset = 0;
480 esp_gattc_char_elem_t char_result;
483 while (char_offset < total_char_count) {
485 auto char_status = esp_ble_gattc_get_all_char(this->
gattc_if_, this->
conn_id_, service_result.start_handle,
486 service_result.end_handle, &char_result, &cc, char_offset);
487 if (char_status != ESP_GATT_OK || cc == 0) {
494 service_resp.characteristics.emplace_back();
495 auto &characteristic_resp = service_resp.characteristics.back();
496 fill_gatt_uuid(characteristic_resp.uuid, characteristic_resp.short_uuid,
498 characteristic_resp.handle = char_result.char_handle;
499 characteristic_resp.properties = char_result.properties;
501 uint16_t total_desc_count = 0;
502 auto desc_count_status = esp_ble_gattc_get_attr_count(this->
gattc_if_, this->
conn_id_, ESP_GATT_DB_DESCRIPTOR,
503 0, 0, char_result.char_handle, &total_desc_count);
504 if (desc_count_status != ESP_GATT_OK) {
511 if (total_desc_count > 0) {
512 characteristic_resp.descriptors.init(total_desc_count);
513 uint16_t desc_offset = 0;
514 esp_gattc_descr_elem_t desc_result;
515 while (desc_offset < total_desc_count) {
517 auto desc_status = esp_ble_gattc_get_all_descr(this->
gattc_if_, this->
conn_id_, char_result.char_handle,
518 &desc_result, &dc, desc_offset);
519 if (desc_status != ESP_GATT_OK || dc == 0) {
524 characteristic_resp.descriptors.emplace_back();
525 auto &descriptor_resp = characteristic_resp.descriptors.back();
528 descriptor_resp.handle = desc_result.handle;
544 if (!api_conn->send_message(resp)) {
556 auto st = this->
state();
557 if (st == ClientState::IDLE) {
560 ESP_LOGD(TAG,
"[%d] OPEN_EVT in IDLE state (status=%d)", this->
connection_index_, param->open.status);
561 if (param->open.status == ESP_GATT_OK || param->open.status == ESP_GATT_ALREADY_OPEN) {
567 if (st != ClientState::CONNECTING) {
570 if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN) {
586 this->
set_state(ClientState::ESTABLISHED);
597 esp_ble_gattc_search_service(this->
gattc_if_, param->open.conn_id,
nullptr)) == 0) {
611 if (param->disconnect.reason == ESP_GATT_CONN_TERMINATE_PEER_USER && this->state() == ClientState::CONNECTED) {
614 ESP_LOGD(TAG,
"[%d] DISCONNECT_EVT reason=0x%02x", this->
connection_index_, param->disconnect.reason);
616 if (this->
state() == ClientState::IDLE) {
629 esp_ble_gattc_cb_param_t *param) {
630 if (event == ESP_GATTC_REG_EVT && this->
app_id != param->reg.app_id)
632 if (event != ESP_GATTC_REG_EVT && esp_gattc_if != ESP_GATT_IF_NONE && esp_gattc_if != this->
gattc_if_)
636 case ESP_GATTC_REG_EVT: {
637 if (param->reg.status == ESP_GATT_OK) {
640 ESP_LOGE(TAG,
"[%d] gattc app registration failed, status=%d", this->
connection_index_, param->reg.status);
645 case ESP_GATTC_CONNECT_EVT: {
648 this->
conn_id_ = param->connect.conn_id;
650 auto ret = esp_ble_gattc_send_mtu_req(this->
gattc_if_, param->connect.conn_id);
658 case ESP_GATTC_OPEN_EVT: {
664 case ESP_GATTC_CFG_MTU_EVT: {
665 if (this->
conn_id_ != param->cfg_mtu.conn_id)
667 if (param->cfg_mtu.status != ESP_GATT_OK) {
677 true, param->cfg_mtu.status == ESP_GATT_OK ? param->cfg_mtu.mtu : ble_device_base::DEFAULT_ATT_MTU, 0);
684 case ESP_GATTC_DISCONNECT_EVT: {
685 if (!this->
check_addr_(param->disconnect.remote_bda))
690 case ESP_GATTC_CLOSE_EVT: {
691 if (this->
conn_id_ != param->close.conn_id)
700 case ESP_GATTC_SEARCH_CMPL_EVT: {
701 if (this->
conn_id_ != param->search_cmpl.conn_id)
704 if (this->
state() == ClientState::DISCONNECTING) {
712 this->
set_state(ClientState::ESTABLISHED);
716 case ESP_GATTC_READ_CHAR_EVT:
717 case ESP_GATTC_READ_DESCR_EVT: {
718 if (this->
conn_id_ != param->read.conn_id)
720 bool ok = param->read.status == ESP_GATT_OK;
722 ok ? param->read.value_len : 0, ok ? 0 : param->read.status);
725 case ESP_GATTC_WRITE_CHAR_EVT:
726 case ESP_GATTC_WRITE_DESCR_EVT: {
727 if (this->
conn_id_ != param->write.conn_id)
730 param->write.status == ESP_GATT_OK ? 0 : param->write.status);
733 case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
735 param->reg_for_notify.status == ESP_GATT_OK ? 0 : param->reg_for_notify.status);
738 case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
740 param->unreg_for_notify.handle,
false,
741 param->unreg_for_notify.status == ESP_GATT_OK ? 0 : param->unreg_for_notify.status);
744 case ESP_GATTC_NOTIFY_EVT: {
745 if (this->
conn_id_ != param->notify.conn_id)
747 ESP_LOGV(TAG,
"[%d] NOTIFY_EVT handle=0x%2X", this->
connection_index_, param->notify.handle);
759 case ESP_GAP_BLE_SEC_REQ_EVT: {
760 if (!this->
check_addr_(param->ble_security.auth_cmpl.bd_addr))
765 esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr,
true));
771 case ESP_GAP_BLE_AUTH_CMPL_EVT: {
772 if (!this->
check_addr_(param->ble_security.auth_cmpl.bd_addr))
775 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_
void ble_before_disabled_event_handler() override
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