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