24#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
34static const char *
const TAG =
"bluetooth_connection";
47 uint8_t mac[MAC_ADDRESS_SIZE];
55 this->
state_ = ClientState::CONNECTING;
67 if (this->
state_ == ClientState::IDLE || this->
state_ == ClientState::DISCONNECTING) {
70 int err = this->
backend_->gatt_disconnect();
78 this->
state_ = ClientState::DISCONNECTING;
96 this->
state_ = ClientState::IDLE;
116 if (connected && this->
state_ == ClientState::DISCONNECTING) {
119 int err = this->
backend_->gatt_disconnect();
135 this->
state_ = ClientState::ESTABLISHED;
138 ESP_LOGD(TAG,
"[%d] [%s] Connected with cached services, sending connected (mtu=%u)", this->
connection_index_,
140 int param_err = this->
backend_->update_connection_params(ble_device_base::MEDIUM_MIN_CONN_INTERVAL,
141 ble_device_base::MEDIUM_MAX_CONN_INTERVAL, 0,
142 ble_device_base::MEDIUM_CONN_TIMEOUT);
143 if (param_err != 0) {
154 this->
state_ = ClientState::CONNECTED;
155 int err = this->
backend_->discover_services();
184 this->
state_ = ClientState::ESTABLISHED;
238 ESP_LOGW(TAG,
"[%d] [%s] Service batch deferred, TCP buffer full; retrying", this->
connection_index_,
243template<
typename Response>
258 if (api_connection ==
nullptr)
262 return send_handle_reply<api::BluetoothGATTWriteResponse>(api_connection, this->
address_, handle);
264 return send_handle_reply<api::BluetoothGATTNotifyResponse>(api_connection, this->
address_, handle);
283 ESP_LOGW(TAG,
"[%d] [%s] GATT reply for handle 0x%04X deferred, TCP buffer full", this->
connection_index_,
288 ESP_LOGW(TAG,
"[%d] [%s] GATT reply for handle 0x%04X dropped for handle 0x%04X", this->
connection_index_,
308 ESP_LOGW(TAG,
"[%d] [%s] GATT reply for handle 0x%04X undeliverable, abandoning", this->
connection_index_,
324 if (api_connection ==
nullptr)
365 if (api_connection ==
nullptr)
376 ESP_LOGW(TAG,
"[%d] [%s] Failed to send notify data response, handle 0x%04X", this->
connection_index_,
380 ESP_LOGV(TAG,
"[%d] [%s] Failed to send notify data response, handle 0x%04X", this->
connection_index_,
394 return GATT_NOT_CONNECTED;
402 return this->
backend_->read_characteristic(handle);
411 return this->
backend_->write_characteristic(handle, data,
static_cast<uint16_t
>(
length), response);
419 return this->
backend_->read_descriptor(handle);
430 return this->
backend_->write_descriptor(handle, data,
static_cast<uint16_t
>(
length));
438 enable ?
"Registering for" :
"Unregistering for",
handle);
439 return this->
backend_->notify_characteristic(handle, enable);
446 return this->
backend_->update_connection_params(min_interval, max_interval, latency, timeout);
476 auto table = this->
backend_->get_service_table();
486 if (api_conn ==
nullptr) {
487 ESP_LOGW(TAG,
"[%d] [%s] API connection lost while streaming services", this->
connection_index_,
509 if (!resp.
services.empty() && (current_size + estimated_size > MAX_PACKET_SIZE)) {
514 auto &service_resp = resp.
services.back();
515 fill_gatt_uuid(service_resp.uuid, service_resp.short_uuid, service.uuid, use_efficient_uuids);
516 service_resp.handle = service.start_handle;
523 uint16_t char_count = service.characteristic_count;
524 if (char_count != 0 && service.first_characteristic + char_count > table.characteristic_count) {
525 ESP_LOGE(TAG,
"[%d] [%s] Characteristic range out of bounds (service %d), aborting stream",
530 if (char_count > 0) {
531 service_resp.characteristics.init(char_count);
532 for (uint16_t ci = 0; ci < char_count; ci++) {
533 const auto &chr = table.characteristics[service.first_characteristic + ci];
534 service_resp.characteristics.emplace_back();
535 auto &characteristic_resp = service_resp.characteristics.back();
536 fill_gatt_uuid(characteristic_resp.uuid, characteristic_resp.short_uuid, chr.uuid, use_efficient_uuids);
537 characteristic_resp.handle = chr.value_handle;
538 characteristic_resp.properties = chr.properties;
539 uint16_t desc_count = chr.descriptor_count;
540 if (desc_count != 0 && chr.first_descriptor + desc_count > table.descriptor_count) {
541 ESP_LOGE(TAG,
"[%d] [%s] Descriptor range out of bounds (service %d), aborting stream",
546 if (desc_count == 0) {
549 characteristic_resp.descriptors.init(desc_count);
550 for (uint16_t di = 0; di < desc_count; di++) {
551 const auto &desc = table.descriptors[chr.first_descriptor + di];
552 characteristic_resp.descriptors.emplace_back();
553 auto &descriptor_resp = characteristic_resp.descriptors.back();
554 fill_gatt_uuid(descriptor_resp.uuid, descriptor_resp.short_uuid, desc.uuid, use_efficient_uuids);
555 descriptor_resp.handle = desc.handle;
570 if (!api_conn->send_message(resp)) {
bool send_message(const T &msg)
Returns false as soon as the TCP buffer is full.
uint32_t calculate_size() const
std::vector< BluetoothGATTService > services
void set_data(const uint8_t *data, size_t len)
void set_data(const uint8_t *data, size_t len)
bool try_send_ack_(PendingAck kind, uint16_t handle, conn_err_t error)
Sole construction site for these replies, shared by send and retry.
void on_read_result(uint16_t handle, const uint8_t *data, uint16_t len, int error) override
void initiate_connection(uint8_t address_type)
Start connecting with the API address type (BLE_ADDR_TYPE_* code space).
void latch_pending_ack_(PendingAck kind, uint16_t handle, conn_err_t error=0)
Latch a refused reply for the proxy drain.
void on_service_discovery_done(int error) override
conn_err_t write_descriptor(uint16_t handle, const uint8_t *data, size_t length, bool response)
void reset_connection_(conn_err_t reason)
bool connected_reply_owed_
An owed connected=true reply; the proxy's paced drain re-offers it.
ConnectionType connection_type_
void on_pairing_result(int status) override
bool has_pending_ack_() const
uint16_t pending_ack_handle_
void supersede_pending_ack_(uint16_t handle, PendingAck kind)
Drop an owed reply this re-ask makes stale.
void latch_pending_error_(conn_err_t err)
First cause wins: a later, less specific error must not overwrite it.
void abort_service_stream(conn_err_t err)
Streamer abort: latch the GATT cause, park the cursor, tear down.
void age_pending_ack_()
Advance the retry budget and abandon at the limit, without sending.
void send_gatt_error_(uint16_t handle, conn_err_t error)
Report a rejected request.
conn_err_t check_connected_op_(const char *action, const char *type) const
void age_services_done_()
Advance the retry budget and abandon at the limit, without sending.
uint8_t services_done_retries_
void log_gatt_operation_error_(const char *operation, uint16_t handle, int status)
bool notify_drop_warned_
Set on the first dropped notify; later drops log at verbose only.
void on_write_result(uint16_t handle, int error) override
bool batch_stalled_
Set while a refused batch is retrying, so only the first one warns.
conn_err_t pending_error_
void send_service_for_discovery_()
void flush_pending_ack_()
Re-offer the owed reply; clears on success, stays owed on a refusal.
void on_notify_state(uint16_t handle, bool enabled, int error) override
void set_address(uint64_t address)
conn_err_t notify_characteristic(uint16_t handle, bool enable)
void on_connection_state(bool connected, uint16_t mtu, int error) override
void send_services_done_()
Send services-done and settle the cursor: DONE when it lands (or no subscriber), SERVICES_DONE_PENDIN...
bool ack_deferred_warned_
Set once the deferred warn fired; with an unchanged pending_ack_handle_ it keeps re-deferrals of the ...
conn_err_t pending_ack_error_
void park_service_stream_()
Park the stream without services-done and free any held table: an interrupted stream must never be de...
ble_device_base::BLEGattConnection * backend_
bluetooth_proxy::BluetoothProxy * proxy_
uint8_t connection_index_
void flush_owed_replies_()
Re-offer everything this slot owes.
void note_batch_stalled_()
Warn on the stall's leading edge only.
char address_str_[MAC_ADDRESS_PRETTY_BUFFER_SIZE]
conn_err_t read_descriptor(uint16_t handle)
conn_err_t write_characteristic(uint16_t handle, const uint8_t *data, size_t length, bool response)
void on_notify_data(uint16_t handle, const uint8_t *data, uint16_t len) override
bool services_discovered_
conn_err_t read_characteristic(uint16_t handle)
void send_ack_(PendingAck kind, uint16_t handle, conn_err_t error=0)
First attempt: send, and latch it for the drain if the API refuses.
void clear_pending_ack_()
void send_connected_reply_()
Send the connected=true reply, latching it if the API refuses.
conn_err_t update_connection_params(uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t timeout)
uint8_t pending_ack_retries_
void clear_owed_flags_()
Drop everything this slot owes, in one write to the shared tail byte.
api::APIConnection * get_api_connection()
bool send_gatt_services_done(uint64_t address)
Same convention as send_device_connection: false only on a refused frame.
void reset_connection_slot_(BluetoothConnection *connection, conn_err_t reason)
Free a connection slot after teardown: notify the API client and reset the streaming cursor.
void send_connections_free()
void update_address_slot_(uint64_t old_address, uint64_t new_address)
Keep the pre-allocated connections-free message in step when a connection slot changes address (0 = f...
void send_device_pairing(uint64_t address, bool paired, conn_err_t error=CONN_OK)
bool send_gatt_error(uint64_t address, uint16_t handle, conn_err_t error)
False only when the API refused the frame, so the reply is still owed.
bool send_device_connection(uint64_t address, bool connected, uint16_t mtu=0, conn_err_t error=CONN_OK)
False only when a subscriber refused the frame; true = delivered or nobody subscribed.
bool client_supports_efficient_uuids() const
Whether the subscribed API client understands 16/32-bit UUID fields.
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...
PendingAck
A refused GATT reply owed to the current subscriber.
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...
char * format_mac_addr_upper(const uint8_t *mac, char *output)
Format MAC address as XX:XX:XX:XX:XX:XX (uppercase, colon separators)
spi_device_handle_t handle