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);
280 ESP_LOGW(TAG,
"[%d] [%s] GATT reply for handle 0x%04X deferred, TCP buffer full", this->
connection_index_,
283 ESP_LOGW(TAG,
"[%d] [%s] GATT reply for handle 0x%04X dropped for handle 0x%04X", this->
connection_index_,
303 ESP_LOGW(TAG,
"[%d] [%s] GATT reply for handle 0x%04X undeliverable, abandoning", this->
connection_index_,
319 if (api_connection ==
nullptr)
360 if (api_connection ==
nullptr)
381 return GATT_NOT_CONNECTED;
389 return this->
backend_->read_characteristic(handle);
398 return this->
backend_->write_characteristic(handle, data,
static_cast<uint16_t
>(
length), response);
406 return this->
backend_->read_descriptor(handle);
417 return this->
backend_->write_descriptor(handle, data,
static_cast<uint16_t
>(
length));
425 enable ?
"Registering for" :
"Unregistering for",
handle);
426 return this->
backend_->notify_characteristic(handle, enable);
433 return this->
backend_->update_connection_params(min_interval, max_interval, latency, timeout);
463 auto table = this->
backend_->get_service_table();
473 if (api_conn ==
nullptr) {
474 ESP_LOGW(TAG,
"[%d] [%s] API connection lost while streaming services", this->
connection_index_,
496 if (!resp.
services.empty() && (current_size + estimated_size > MAX_PACKET_SIZE)) {
501 auto &service_resp = resp.
services.back();
502 fill_gatt_uuid(service_resp.uuid, service_resp.short_uuid, service.uuid, use_efficient_uuids);
503 service_resp.handle = service.start_handle;
510 uint16_t char_count = service.characteristic_count;
511 if (char_count != 0 && service.first_characteristic + char_count > table.characteristic_count) {
512 ESP_LOGE(TAG,
"[%d] [%s] Characteristic range out of bounds (service %d), aborting stream",
517 if (char_count > 0) {
518 service_resp.characteristics.init(char_count);
519 for (uint16_t ci = 0; ci < char_count; ci++) {
520 const auto &chr = table.characteristics[service.first_characteristic + ci];
521 service_resp.characteristics.emplace_back();
522 auto &characteristic_resp = service_resp.characteristics.back();
523 fill_gatt_uuid(characteristic_resp.uuid, characteristic_resp.short_uuid, chr.uuid, use_efficient_uuids);
524 characteristic_resp.handle = chr.value_handle;
525 characteristic_resp.properties = chr.properties;
526 uint16_t desc_count = chr.descriptor_count;
527 if (desc_count != 0 && chr.first_descriptor + desc_count > table.descriptor_count) {
528 ESP_LOGE(TAG,
"[%d] [%s] Descriptor range out of bounds (service %d), aborting stream",
533 if (desc_count == 0) {
536 characteristic_resp.descriptors.init(desc_count);
537 for (uint16_t di = 0; di < desc_count; di++) {
538 const auto &desc = table.descriptors[chr.first_descriptor + di];
539 characteristic_resp.descriptors.emplace_back();
540 auto &descriptor_resp = characteristic_resp.descriptors.back();
541 fill_gatt_uuid(descriptor_resp.uuid, descriptor_resp.short_uuid, desc.uuid, use_efficient_uuids);
542 descriptor_resp.handle = desc.handle;
557 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)
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...
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