mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Merge branch 'btp_uuids_dynamic_services' into integration
This commit is contained in:
		| @@ -37,6 +37,35 @@ static void fill_gatt_uuid(std::array<uint64_t, 2> &uuid_128, uint32_t &short_uu | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Constants for size estimation | ||||||
|  | static constexpr uint8_t SERVICE_OVERHEAD_LEGACY = 25;     // UUID(20) + handle(4) + overhead(1) | ||||||
|  | static constexpr uint8_t SERVICE_OVERHEAD_EFFICIENT = 10;  // UUID(6) + handle(4) | ||||||
|  | static constexpr uint8_t CHAR_SIZE_128BIT = 35;            // UUID(20) + handle(4) + props(4) + overhead(7) | ||||||
|  | static constexpr uint8_t DESC_SIZE_128BIT = 25;            // UUID(20) + handle(4) + overhead(1) | ||||||
|  | static constexpr uint8_t DESC_SIZE_16BIT = 10;             // UUID(6) + handle(4) | ||||||
|  | static constexpr uint8_t DESC_PER_CHAR = 1;                // Assume 1 descriptor per characteristic | ||||||
|  |  | ||||||
|  | // Helper to estimate service size before fetching all data | ||||||
|  | static size_t estimate_service_size(uint16_t char_count, bool use_efficient_uuids) { | ||||||
|  |   size_t service_overhead = use_efficient_uuids ? SERVICE_OVERHEAD_EFFICIENT : SERVICE_OVERHEAD_LEGACY; | ||||||
|  |   // Always assume 128-bit UUIDs for characteristics to be safe | ||||||
|  |   size_t char_size = CHAR_SIZE_128BIT; | ||||||
|  |   // Assume one 128-bit descriptor per characteristic | ||||||
|  |   size_t desc_size = DESC_SIZE_128BIT * DESC_PER_CHAR; | ||||||
|  |  | ||||||
|  |   return service_overhead + (char_size + desc_size) * char_count; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Helper to calculate actual service size | ||||||
|  | static size_t get_service_size(api::BluetoothGATTService &service) { | ||||||
|  |   api::ProtoSize service_size; | ||||||
|  |   service.calculate_size(service_size); | ||||||
|  |   size_t size = service_size.get_size(); | ||||||
|  |   ESP_LOGV(TAG, "Service size calculation: uuid[0]=%llx uuid[1]=%llx short_uuid=%u handle=%u -> size=%d", | ||||||
|  |            service.uuid[0], service.uuid[1], service.short_uuid, service.handle, size); | ||||||
|  |   return size; | ||||||
|  | } | ||||||
|  |  | ||||||
| bool BluetoothConnection::supports_efficient_uuids_() const { | bool BluetoothConnection::supports_efficient_uuids_() const { | ||||||
|   auto *api_conn = this->proxy_->get_api_connection(); |   auto *api_conn = this->proxy_->get_api_connection(); | ||||||
|   return api_conn && api_conn->client_supports_api_version(1, 12); |   return api_conn && api_conn->client_supports_api_version(1, 12); | ||||||
| @@ -95,16 +124,23 @@ void BluetoothConnection::send_service_for_discovery_() { | |||||||
|   // Check if client supports efficient UUIDs |   // Check if client supports efficient UUIDs | ||||||
|   bool use_efficient_uuids = this->supports_efficient_uuids_(); |   bool use_efficient_uuids = this->supports_efficient_uuids_(); | ||||||
|  |  | ||||||
|   // Prepare response for up to 3 services |   // Prepare response | ||||||
|   api::BluetoothGATTGetServicesResponse resp; |   api::BluetoothGATTGetServicesResponse resp; | ||||||
|   resp.address = this->address_; |   resp.address = this->address_; | ||||||
|  |  | ||||||
|   // Process up to 3 services in this iteration |   // Dynamic batching based on actual size | ||||||
|   uint8_t services_to_process = |   static constexpr size_t MAX_PACKET_SIZE = | ||||||
|       std::min(MAX_SERVICES_PER_BATCH, static_cast<uint8_t>(this->service_count_ - this->send_service_)); |       1360;  // Conservative MTU limit for API messages (accounts for WPA3 overhead) | ||||||
|   resp.services.reserve(services_to_process); |  | ||||||
|  |  | ||||||
|   for (int service_idx = 0; service_idx < services_to_process; service_idx++) { |   // Keep running total of actual message size | ||||||
|  |   size_t current_size = 0; | ||||||
|  |   api::ProtoSize size; | ||||||
|  |   resp.calculate_size(size); | ||||||
|  |   current_size = size.get_size(); | ||||||
|  |   ESP_LOGV(TAG, "[%d] [%s] Starting batch with base size: %d, send_service_: %d", this->connection_index_, | ||||||
|  |            this->address_str().c_str(), current_size, this->send_service_); | ||||||
|  |  | ||||||
|  |   while (this->send_service_ < this->service_count_) { | ||||||
|     esp_gattc_service_elem_t service_result; |     esp_gattc_service_elem_t service_result; | ||||||
|     uint16_t service_count = 1; |     uint16_t service_count = 1; | ||||||
|     esp_gatt_status_t service_status = esp_ble_gattc_get_service(this->gattc_if_, this->conn_id_, nullptr, |     esp_gatt_status_t service_status = esp_ble_gattc_get_service(this->gattc_if_, this->conn_id_, nullptr, | ||||||
| @@ -118,15 +154,7 @@ void BluetoothConnection::send_service_for_discovery_() { | |||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     this->send_service_++; |     // Get the number of characteristics BEFORE adding to response | ||||||
|     resp.services.emplace_back(); |  | ||||||
|     auto &service_resp = resp.services.back(); |  | ||||||
|  |  | ||||||
|     fill_gatt_uuid(service_resp.uuid, service_resp.short_uuid, service_result.uuid, use_efficient_uuids); |  | ||||||
|  |  | ||||||
|     service_resp.handle = service_result.start_handle; |  | ||||||
|  |  | ||||||
|     // Get the number of characteristics directly with one call |  | ||||||
|     uint16_t total_char_count = 0; |     uint16_t total_char_count = 0; | ||||||
|     esp_gatt_status_t char_count_status = |     esp_gatt_status_t char_count_status = | ||||||
|         esp_ble_gattc_get_attr_count(this->gattc_if_, this->conn_id_, ESP_GATT_DB_CHARACTERISTIC, |         esp_ble_gattc_get_attr_count(this->gattc_if_, this->conn_id_, ESP_GATT_DB_CHARACTERISTIC, | ||||||
| @@ -139,11 +167,26 @@ void BluetoothConnection::send_service_for_discovery_() { | |||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (total_char_count == 0) { |     // If this service likely won't fit, send current batch (unless it's the first) | ||||||
|       // No characteristics, continue to next service |     size_t estimated_size = estimate_service_size(total_char_count, use_efficient_uuids); | ||||||
|       continue; |     if (!resp.services.empty() && (current_size + estimated_size > MAX_PACKET_SIZE)) { | ||||||
|  |       // This service likely won't fit, send current batch | ||||||
|  |       break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // Now add the service since we know it will likely fit | ||||||
|  |     resp.services.emplace_back(); | ||||||
|  |     auto &service_resp = resp.services.back(); | ||||||
|  |  | ||||||
|  |     fill_gatt_uuid(service_resp.uuid, service_resp.short_uuid, service_result.uuid, use_efficient_uuids); | ||||||
|  |  | ||||||
|  |     service_resp.handle = service_result.start_handle; | ||||||
|  |  | ||||||
|  |     ESP_LOGV(TAG, "[%d] [%s] Service UUID: %llx,%llx short:%u handle:%u", this->connection_index_, | ||||||
|  |              this->address_str().c_str(), service_resp.uuid[0], service_resp.uuid[1], service_resp.short_uuid, | ||||||
|  |              service_resp.handle); | ||||||
|  |  | ||||||
|  |     if (total_char_count > 0) { | ||||||
|       // Reserve space and process characteristics |       // Reserve space and process characteristics | ||||||
|       service_resp.characteristics.reserve(total_char_count); |       service_resp.characteristics.reserve(total_char_count); | ||||||
|       uint16_t char_offset = 0; |       uint16_t char_offset = 0; | ||||||
| @@ -181,8 +224,8 @@ void BluetoothConnection::send_service_for_discovery_() { | |||||||
|             this->gattc_if_, this->conn_id_, ESP_GATT_DB_DESCRIPTOR, 0, 0, char_result.char_handle, &total_desc_count); |             this->gattc_if_, this->conn_id_, ESP_GATT_DB_DESCRIPTOR, 0, 0, char_result.char_handle, &total_desc_count); | ||||||
|  |  | ||||||
|         if (desc_count_status != ESP_GATT_OK) { |         if (desc_count_status != ESP_GATT_OK) { | ||||||
|         ESP_LOGE(TAG, "[%d] [%s] Error getting descriptor count for char handle %d, status=%d", this->connection_index_, |           ESP_LOGE(TAG, "[%d] [%s] Error getting descriptor count for char handle %d, status=%d", | ||||||
|                  this->address_str().c_str(), char_result.char_handle, desc_count_status); |                    this->connection_index_, this->address_str().c_str(), char_result.char_handle, desc_count_status); | ||||||
|           this->send_service_ = DONE_SENDING_SERVICES; |           this->send_service_ = DONE_SENDING_SERVICES; | ||||||
|           return; |           return; | ||||||
|         } |         } | ||||||
| @@ -221,9 +264,48 @@ void BluetoothConnection::send_service_for_discovery_() { | |||||||
|           desc_offset++; |           desc_offset++; | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  |     }  // end if (total_char_count > 0) | ||||||
|  |  | ||||||
|  |     // Calculate the actual size of just this service | ||||||
|  |     size_t service_size = get_service_size(service_resp) + 1;  // +1 for field tag | ||||||
|  |  | ||||||
|  |     // Check if adding this service would exceed the limit | ||||||
|  |     if (current_size + service_size > MAX_PACKET_SIZE) { | ||||||
|  |       // We would go over - pop the last service if we have more than one | ||||||
|  |       if (resp.services.size() > 1) { | ||||||
|  |         resp.services.pop_back(); | ||||||
|  |         ESP_LOGD(TAG, "[%d] [%s] Service %d would exceed limit (current: %d + service: %d > %d), sending current batch", | ||||||
|  |                  this->connection_index_, this->address_str().c_str(), this->send_service_, current_size, service_size, | ||||||
|  |                  MAX_PACKET_SIZE); | ||||||
|  |         // Don't increment send_service_ - we'll retry this service in next batch | ||||||
|  |       } else { | ||||||
|  |         // This single service is too large, but we have to send it anyway | ||||||
|  |         current_size += service_size; | ||||||
|  |         ESP_LOGV(TAG, "[%d] [%s] Service %d is too large (%d bytes) but sending anyway", this->connection_index_, | ||||||
|  |                  this->address_str().c_str(), this->send_service_, service_size); | ||||||
|  |         // Increment so we don't get stuck | ||||||
|  |         this->send_service_++; | ||||||
|  |       } | ||||||
|  |       // Send what we have | ||||||
|  |       break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   // Send the message with 1-3 services |     // Now we know we're keeping this service, add its size | ||||||
|  |     current_size += service_size; | ||||||
|  |  | ||||||
|  |     // Log the difference between estimate and actual size | ||||||
|  |     int size_diff = (int) service_size - (int) estimated_size; | ||||||
|  |     ESP_LOGV(TAG, "[%d] [%s] Service %d actual: %d, estimated: %d, diff: %+d", this->connection_index_, | ||||||
|  |              this->address_str().c_str(), this->send_service_, service_size, estimated_size, size_diff); | ||||||
|  |     ESP_LOGV(TAG, "[%d] [%s] Total size now: %d", this->connection_index_, this->address_str().c_str(), current_size); | ||||||
|  |  | ||||||
|  |     // Successfully added this service, increment counter | ||||||
|  |     this->send_service_++; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Send the message with dynamically batched services | ||||||
|  |   ESP_LOGD(TAG, "[%d] [%s] Sending batch with %d services, total size %d", this->connection_index_, | ||||||
|  |            this->address_str().c_str(), resp.services.size(), current_size); | ||||||
|   api_conn->send_message(resp, api::BluetoothGATTGetServicesResponse::MESSAGE_TYPE); |   api_conn->send_message(resp, api::BluetoothGATTGetServicesResponse::MESSAGE_TYPE); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,7 +22,6 @@ namespace esphome::bluetooth_proxy { | |||||||
|  |  | ||||||
| static const esp_err_t ESP_GATT_NOT_CONNECTED = -1; | static const esp_err_t ESP_GATT_NOT_CONNECTED = -1; | ||||||
| static const int DONE_SENDING_SERVICES = -2; | static const int DONE_SENDING_SERVICES = -2; | ||||||
| static const uint8_t MAX_SERVICES_PER_BATCH = 3; |  | ||||||
|  |  | ||||||
| using namespace esp32_ble_client; | using namespace esp32_ble_client; | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user