ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
ble_server.cpp
Go to the documentation of this file.
1#include "ble_server.h"
2
4#include "esphome/core/log.h"
7
8#ifdef USE_ESP32
9
10#include <freertos/FreeRTOSConfig.h>
11#include <esp_bt_main.h>
12#ifndef CONFIG_BT_CONTROLLER_DISABLED
13#include <esp_bt.h>
14#endif
15#include <freertos/task.h>
16#include <esp_gap_ble_api.h>
17
19
20ESPHOME_LOG_TAG(TAG, "esp32_ble_server");
21
23 if (this->parent_->is_failed()) {
24 this->mark_failed();
25 ESP_LOGE(TAG, "BLE Server was marked failed by ESP32BLE");
26 return;
27 }
28 global_ble_server = this;
29}
30
32 if (!this->parent_->is_active()) {
33 return;
34 }
35 switch (this->state_) {
36 case RUNNING: {
37 // Start all services that are pending to start
38 if (!this->services_to_start_.empty()) {
39 size_t write_idx = 0;
40 for (auto *service : this->services_to_start_) {
41 if (service->is_created()) {
42 service->start(); // Needs to be called once per characteristic in the service
43 }
44 // Remove services that have started or are starting
45 if (!service->is_starting() && !service->is_running()) {
46 this->services_to_start_[write_idx++] = service;
47 }
48 }
49 this->services_to_start_.erase(this->services_to_start_.begin() + write_idx, this->services_to_start_.end());
50 }
51 break;
52 }
53 case INIT: {
54 esp_err_t err = esp_ble_gatts_app_register(0);
55 if (err != ESP_OK) {
56 ESP_LOGE(TAG, "esp_ble_gatts_app_register failed: %d", err);
57 this->mark_failed();
58 return;
59 }
60 this->state_ = REGISTERING;
61 break;
62 }
63 case REGISTERING: {
64 if (this->registered_) {
65 // Create the device information service first so
66 // it is at the top of the GATT table
68 // Create all services previously created
69 for (auto &entry : this->services_) {
70 if (entry.service == this->device_information_service_) {
71 continue;
72 }
73 entry.service->do_create(this);
74 }
75 this->state_ = STARTING_SERVICE;
76 }
77 break;
78 }
79 case STARTING_SERVICE: {
81 this->state_ = RUNNING;
84 ESP_LOGD(TAG, "BLE server setup successfully");
85 } else if (this->device_information_service_->is_created()) {
87 }
88 break;
89 }
90 }
91}
92
93bool BLEServer::can_proceed() { return this->is_running() || !this->parent_->is_active(); }
94
96 if (this->is_running()) {
97 this->parent_->advertising_set_manufacturer_data(this->manufacturer_data_);
98 }
99}
100
103 return;
104 this->advertising_requested_ = true;
105 this->parent_->advertising_start();
106}
107
109 if (!this->advertising_requested_)
110 return;
111 this->advertising_requested_ = false;
112 this->parent_->advertising_stop();
113}
114
115BLEService *BLEServer::create_service(ESPBTUUID uuid, bool advertise, uint16_t num_handles) {
116#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
117 char uuid_buf[esp32_ble::UUID_STR_LEN];
118 uuid.to_str(uuid_buf);
119 ESP_LOGV(TAG, "Creating BLE service - %s", uuid_buf);
120#endif
121 // Calculate the inst_id for the service
122 uint8_t inst_id = 0;
123 for (; inst_id < 0xFF; inst_id++) {
124 if (this->get_service(uuid, inst_id) == nullptr) {
125 break;
126 }
127 }
128 if (inst_id == 0xFF) {
129 char warn_uuid_buf[esp32_ble::UUID_STR_LEN];
130 uuid.to_str(warn_uuid_buf);
131 ESP_LOGW(TAG, "Could not create BLE service %s, too many instances", warn_uuid_buf);
132 return nullptr;
133 }
134 BLEService *service = // NOLINT(cppcoreguidelines-owning-memory)
135 new BLEService(uuid, num_handles, inst_id, advertise);
136 this->services_.push_back({uuid, inst_id, service});
137 if (this->parent_->is_active() && this->registered_) {
138 service->do_create(this);
139 }
140 return service;
141}
142
143void BLEServer::remove_service(ESPBTUUID uuid, uint8_t inst_id) {
144#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
145 char uuid_buf[esp32_ble::UUID_STR_LEN];
146 uuid.to_str(uuid_buf);
147 ESP_LOGV(TAG, "Removing BLE service - %s %d", uuid_buf, inst_id);
148#endif
149 for (auto it = this->services_.begin(); it != this->services_.end(); ++it) {
150 if (it->uuid == uuid && it->inst_id == inst_id) {
151 it->service->do_delete();
152 delete it->service; // NOLINT(cppcoreguidelines-owning-memory)
153 this->services_.erase(it);
154 return;
155 }
156 }
157 char warn_uuid_buf[esp32_ble::UUID_STR_LEN];
158 uuid.to_str(warn_uuid_buf);
159 ESP_LOGW(TAG, "BLE service %s %d does not exist", warn_uuid_buf, inst_id);
160}
161
162BLEService *BLEServer::get_service(ESPBTUUID uuid, uint8_t inst_id) {
163 for (auto &entry : this->services_) {
164 if (entry.uuid == uuid && entry.inst_id == inst_id) {
165 return entry.service;
166 }
167 }
168 return nullptr;
169}
170
172 for (auto &entry : this->callbacks_) {
173 if (entry.type == type) {
174 entry.callback(conn_id);
175 }
176 }
177}
178
179void BLEServer::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
180 esp_ble_gatts_cb_param_t *param) {
181 switch (event) {
182 case ESP_GATTS_CONNECT_EVT: {
183 ESP_LOGD(TAG, "BLE Client connected");
184 this->add_client_(param->connect.conn_id);
185 // Resume advertising so additional clients can discover and connect
187 this->parent_->advertising_refresh();
188 }
189 this->dispatch_callbacks_(CallbackType::ON_CONNECT, param->connect.conn_id);
190 break;
191 }
192 case ESP_GATTS_DISCONNECT_EVT: {
193 ESP_LOGD(TAG, "BLE Client disconnected");
194 this->remove_client_(param->disconnect.conn_id);
195 this->parent_->advertising_refresh();
196 this->dispatch_callbacks_(CallbackType::ON_DISCONNECT, param->disconnect.conn_id);
197 break;
198 }
199 case ESP_GATTS_REG_EVT: {
200 this->gatts_if_ = gatts_if;
201 this->registered_ = true;
202 break;
203 }
204 default:
205 break;
206 }
207
208 for (auto &entry : this->services_) {
209 entry.service->gatts_event_handler(event, gatts_if, param);
210 }
211}
212
213int8_t BLEServer::find_client_index_(uint16_t conn_id) const {
214 for (uint8_t i = 0; i < this->client_count_; i++) {
215 if (this->clients_[i] == conn_id)
216 return i;
217 }
218 return -1;
219}
220
221void BLEServer::add_client_(uint16_t conn_id) {
222 // Check if already in list
223 if (this->find_client_index_(conn_id) >= 0)
224 return;
225 // Add if there's space
226 if (this->client_count_ < USE_ESP32_BLE_MAX_CONNECTIONS) {
227 this->clients_[this->client_count_++] = conn_id;
228 } else {
229 // This should never happen since max clients is known at compile time
230 ESP_LOGE(TAG, "Client array full");
231 }
232}
233
234void BLEServer::remove_client_(uint16_t conn_id) {
235 int8_t index = this->find_client_index_(conn_id);
236 if (index >= 0) {
237 // Replace with last element and decrement count (client order not preserved)
238 this->clients_[index] = this->clients_[--this->client_count_];
239 }
240}
241
243 // Advertising is re-requested once the server is running again after BLE is re-enabled
244 this->release_advertising_();
245 // Delete all clients
246 this->client_count_ = 0;
247 // Delete all services
248 for (auto &entry : this->services_) {
249 entry.service->do_delete();
250 }
251 this->registered_ = false;
252 this->state_ = INIT;
253}
254
256
258 ESP_LOGCONFIG(TAG,
259 "ESP32 BLE Server:\n"
260 " Max clients: %u",
261 this->max_clients_);
262}
263
264BLEServer *global_ble_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
265
266} // namespace esphome::esp32_ble_server
267
268#endif
void mark_failed()
Mark this component as failed.
std::vector< CallbackEntry > callbacks_
Definition ble_server.h:100
std::vector< uint8_t > manufacturer_data_
Definition ble_server.h:102
void remove_client_(uint16_t conn_id)
float get_setup_priority() const override
enum esphome::esp32_ble_server::BLEServer::State INIT
void dispatch_callbacks_(CallbackType type, uint16_t conn_id)
ESPHOME_ALWAYS_INLINE bool is_running()
Definition ble_server.h:34
BLEService * get_service(ESPBTUUID uuid, uint8_t inst_id=0)
void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
std::vector< BLEService * > services_to_start_
Definition ble_server.h:112
BLEService * create_service(ESPBTUUID uuid, bool advertise=false, uint16_t num_handles=15)
void remove_service(ESPBTUUID uuid, uint8_t inst_id=0)
void add_client_(uint16_t conn_id)
std::vector< ServiceEntry > services_
Definition ble_server.h:111
uint16_t clients_[USE_ESP32_BLE_MAX_CONNECTIONS]
Definition ble_server.h:108
int8_t find_client_index_(uint16_t conn_id) const
void do_create(BLEServer *server)
uint8_t type
ESPHOME_LOG_TAG(TAG, "esp32_ble")
constexpr float AFTER_BLUETOOTH
Definition component.h:49