ESPHome 2026.10.0-dev
Loading...
Searching...
No Matches
bluetooth_connection.h
Go to the documentation of this file.
1// Shared types and helpers for the per-platform GATT connection backends and
2// the Bluetooth proxy that drives them.
3
4#pragma once
5
7
10
11#include <array>
12#include <cstddef>
13#include <cstdint>
14
15#ifdef USE_ESP32
16#include <esp_err.h>
17#endif
18
19// USE_BLUETOOTH_PROXY_CONNECTIONS is the single spelling of "this build has
20// proxy connection slots": codegen emits it per configured slot, and each
21// slot brings a GATT backend, so it also implies USE_BLE_GATT_CLIENT (not
22// the converse: a backend can exist without proxy slots). The hub
23// wrapper, the proxy's connection surface and the API's connection messages
24// all gate on it. The address-scoped maintenance functions below are only
25// reached from that gated surface; the #else stubs just keep this header
26// parsing on arms without a backend.
27
28namespace esphome::api {
29class BluetoothGATTGetServicesResponse;
30} // namespace esphome::api
31
33
34// Connection-owned error type for the API error fields, which are plain
35// integers on the wire. Aliases esp_err_t on esp32 (where the values come from
36// IDF calls); a bare int elsewhere. Owning the name instead of probing for
37// esp_err_t keeps the header independent of how a platform's SDK spells its
38// error type.
39#ifdef USE_ESP32
40using conn_err_t = esp_err_t;
41static constexpr conn_err_t CONN_OK = ESP_OK;
42#else
43using conn_err_t = int;
44static constexpr conn_err_t CONN_OK = 0;
45#endif
46
47// The ESPHome-private "not connected" wire value, shared with the neutral
48// GATT contract so backend and wrapper cannot drift.
49static constexpr conn_err_t GATT_NOT_CONNECTED = ble_device_base::GATT_ERR_NOT_CONNECTED;
50
51// What the platform's connection backend supports beyond GATT operations;
52// the proxy derives its feature flags and legacy version from these.
53#if defined(USE_ESP32)
54static constexpr bool SUPPORTS_PAIRING = true;
55static constexpr bool SUPPORTS_CACHE_CLEARING = true;
56#elif defined(USE_RP2040_BLE) && defined(USE_BLE_GATT_CLIENT)
57// The rp2 BTstack backend pairs (just works + bonding); it has no service
58// cache to clear. Keyed on the backend, not the generic client define, so a
59// future backend without pairing keeps the stub arm below.
60static constexpr bool SUPPORTS_PAIRING = true;
61static constexpr bool SUPPORTS_CACHE_CLEARING = false;
62#else
63static constexpr bool SUPPORTS_PAIRING = false;
64static constexpr bool SUPPORTS_CACHE_CLEARING = false;
65#endif
66
67// Address-scoped (not connection-scoped) maintenance requests.
68#if (defined(USE_ESP32) || defined(USE_RP2040_BLE)) && defined(USE_BLE_GATT_CLIENT)
70#else
71inline conn_err_t unpair_device(uint64_t) { return GATT_NOT_CONNECTED; }
72#endif
73#if defined(USE_ESP32) && defined(USE_BLE_GATT_CLIENT)
75#else
76inline conn_err_t clear_gatt_cache(uint64_t) { return GATT_NOT_CONNECTED; }
77#endif
78
79// send_service_ cursor states; >= 0 is the next service index to stream.
80static constexpr int DONE_SENDING_SERVICES = -2;
81static constexpr int INIT_SENDING_SERVICES = -3;
82static constexpr int SERVICES_DONE_PENDING = -4; // all batches delivered, done-message still owed
83// Every sentinel must stay below the >= 0 streaming gate and clear of
84// GATT_NOT_CONNECTED (-1) so cursor and error values can never be confused.
85static_assert(DONE_SENDING_SERVICES < 0 && INIT_SENDING_SERVICES < 0 && SERVICES_DONE_PENDING < 0);
86static_assert(DONE_SENDING_SERVICES != GATT_NOT_CONNECTED && INIT_SENDING_SERVICES != GATT_NOT_CONNECTED &&
87 SERVICES_DONE_PENDING != GATT_NOT_CONNECTED);
88// Owed-done retries stop here (~3 s at the 100 ms drain cadence): a done
89// delivered near the client's 30 s timeout could land on a fresh request's
90// empty accumulator and cache as an empty database.
91static constexpr uint8_t SERVICES_DONE_RETRY_LIMIT = 30;
92// Owed-ack retries stop after ~25 s of subscribed drain time from the first
93// refusal, keeping most of the client's 30 s GATT window for congestion to
94// clear while still bounding how stale a delivered reply can be.
95static constexpr uint16_t PENDING_ACK_RETRY_LIMIT = 250;
96
97// ---- Service-streaming size budget, shared by every platform's streamer ----
98
99// Conservative MTU limit for API messages (accounts for WPA3 overhead)
100static constexpr size_t MAX_PACKET_SIZE = 1360;
101
102// Constants for size estimation
103static constexpr uint8_t SERVICE_OVERHEAD_LEGACY = 25; // UUID(20) + handle(4) + overhead(1)
104static constexpr uint8_t SERVICE_OVERHEAD_EFFICIENT = 10; // UUID(6) + handle(4)
105static constexpr uint8_t CHAR_SIZE_128BIT = 35; // UUID(20) + handle(4) + props(4) + overhead(7)
106static constexpr uint8_t DESC_SIZE_128BIT = 25; // UUID(20) + handle(4) + overhead(1)
107static constexpr uint8_t DESC_PER_CHAR = 1; // Assume 1 descriptor per characteristic
108
112inline size_t estimate_service_size(uint16_t char_count, bool use_efficient_uuids) {
113 size_t service_overhead = use_efficient_uuids ? SERVICE_OVERHEAD_EFFICIENT : SERVICE_OVERHEAD_LEGACY;
114 return service_overhead + (CHAR_SIZE_128BIT + DESC_SIZE_128BIT * DESC_PER_CHAR) * char_count;
115}
116
117// ---- UUID wire packing, shared by every platform's streamer ----
118
119// This function is allocation-free and directly packs UUIDs into the output
120// array using precalculated constants for the Bluetooth base UUID. ESPBTUUID
121// stores its 128-bit form little-endian (same as Bluedroid).
122inline void fill_128bit_uuid_array(std::array<uint64_t, 2> &out, const ble_device_base::ESPBTUUID &uuid) {
124 if (uuid.type() == ESPBTUUID::Type::UUID128) {
125 const uint8_t *u = uuid.uuid128();
126 // out[0] = bytes 8-15 (big-endian), out[1] = bytes 0-7 (big-endian)
127 out[0] = ((uint64_t) u[15] << 56) | ((uint64_t) u[14] << 48) | ((uint64_t) u[13] << 40) | ((uint64_t) u[12] << 32) |
128 ((uint64_t) u[11] << 24) | ((uint64_t) u[10] << 16) | ((uint64_t) u[9] << 8) | ((uint64_t) u[8]);
129 out[1] = ((uint64_t) u[7] << 56) | ((uint64_t) u[6] << 48) | ((uint64_t) u[5] << 40) | ((uint64_t) u[4] << 32) |
130 ((uint64_t) u[3] << 24) | ((uint64_t) u[2] << 16) | ((uint64_t) u[1] << 8) | ((uint64_t) u[0]);
131 return;
132 }
133 // 16/32-bit UUID inserted into the Bluetooth base UUID:
134 // 00000000-0000-1000-8000-00805F9B34FB
135 uint32_t value = uuid.type() == ESPBTUUID::Type::UUID16 ? uuid.uuid16() : uuid.uuid32();
136 out[0] = ((uint64_t) value << 32) | 0x00001000ULL; // Base UUID bytes 8-11
137 out[1] = 0x800000805F9B34FBULL; // Base UUID bytes 0-7
138}
139
143inline void fill_gatt_uuid(std::array<uint64_t, 2> &uuid_128, uint32_t &short_uuid,
144 const ble_device_base::ESPBTUUID &uuid, bool use_efficient_uuids) {
146 if (!use_efficient_uuids || uuid.type() == ESPBTUUID::Type::UUID128) {
147 fill_128bit_uuid_array(uuid_128, uuid);
148 } else if (uuid.type() == ESPBTUUID::Type::UUID16) {
149 short_uuid = uuid.uuid16();
150 } else {
151 short_uuid = uuid.uuid32();
152 }
153}
154
155#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
159enum class BatchClose : uint8_t { CONTINUE, SEND };
160
165BatchClose close_service_batch(api::BluetoothGATTGetServicesResponse &resp, size_t &current_size, int16_t &send_service,
166 uint8_t connection_index, const char *address_str);
167#endif // USE_BLUETOOTH_PROXY_CONNECTIONS
168
169} // namespace esphome::bluetooth_connection
uint8_t address
Definition bl0906.h:4
conn_err_t unpair_device(uint64_t address)
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...
void fill_128bit_uuid_array(std::array< uint64_t, 2 > &out, const ble_device_base::ESPBTUUID &uuid)
BatchClose
Result of close_service_batch: keep filling the batch or send it now.
BatchClose close_service_batch(api::BluetoothGATTGetServicesResponse &resp, size_t &current_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...
conn_err_t clear_gatt_cache(uint64_t address)
static void uint32_t