ESPHome 2025.5.0
Loading...
Searching...
No Matches
ble_characteristic.cpp
Go to the documentation of this file.
2#include "ble_server.h"
3#include "ble_service.h"
4
5#include "esphome/core/log.h"
6
7#ifdef USE_ESP32
8
9namespace esphome {
10namespace esp32_ble_server {
11
12static const char *const TAG = "esp32_ble_server.characteristic";
13
15 for (auto *descriptor : this->descriptors_) {
16 delete descriptor; // NOLINT(cppcoreguidelines-owning-memory)
17 }
18 vSemaphoreDelete(this->set_value_lock_);
19}
20
21BLECharacteristic::BLECharacteristic(const ESPBTUUID uuid, uint32_t properties) : uuid_(uuid) {
22 this->set_value_lock_ = xSemaphoreCreateBinary();
23 xSemaphoreGive(this->set_value_lock_);
24
25 this->properties_ = (esp_gatt_char_prop_t) 0;
26
27 this->set_broadcast_property((properties & PROPERTY_BROADCAST) != 0);
28 this->set_indicate_property((properties & PROPERTY_INDICATE) != 0);
29 this->set_notify_property((properties & PROPERTY_NOTIFY) != 0);
30 this->set_read_property((properties & PROPERTY_READ) != 0);
31 this->set_write_property((properties & PROPERTY_WRITE) != 0);
32 this->set_write_no_response_property((properties & PROPERTY_WRITE_NR) != 0);
33}
34
36
37void BLECharacteristic::set_value(const std::vector<uint8_t> &buffer) {
38 xSemaphoreTake(this->set_value_lock_, 0L);
39 this->value_ = buffer;
40 xSemaphoreGive(this->set_value_lock_);
41}
42void BLECharacteristic::set_value(const std::string &buffer) {
43 this->set_value(std::vector<uint8_t>(buffer.begin(), buffer.end()));
44}
45
47 if (this->service_ == nullptr || this->service_->get_server() == nullptr ||
48 this->service_->get_server()->get_connected_client_count() == 0)
49 return;
50
51 for (auto &client : this->service_->get_server()->get_clients()) {
52 size_t length = this->value_.size();
53 // If the client is not in the list of clients to notify, skip it
54 if (this->clients_to_notify_.count(client) == 0)
55 continue;
56 // If the client is in the list of clients to notify, check if it requires an ack (i.e. INDICATE)
57 bool require_ack = this->clients_to_notify_[client];
58 // TODO: Remove this block when INDICATE acknowledgment is supported
59 if (require_ack) {
60 ESP_LOGW(TAG, "INDICATE acknowledgment is not yet supported (i.e. it works as a NOTIFY)");
61 require_ack = false;
62 }
63 esp_err_t err = esp_ble_gatts_send_indicate(this->service_->get_server()->get_gatts_if(), client, this->handle_,
64 length, this->value_.data(), require_ack);
65 if (err != ESP_OK) {
66 ESP_LOGE(TAG, "esp_ble_gatts_send_indicate failed %d", err);
67 return;
68 }
69 }
70}
71
73 // If the descriptor is the CCCD descriptor, listen to its write event to know if the client wants to be notified
74 if (descriptor->get_uuid() == ESPBTUUID::from_uint16(ESP_GATT_UUID_CHAR_CLIENT_CONFIG)) {
75 descriptor->on(BLEDescriptorEvt::VectorEvt::ON_WRITE, [this](const std::vector<uint8_t> &value, uint16_t conn_id) {
76 if (value.size() != 2)
77 return;
78 uint16_t cccd = encode_uint16(value[1], value[0]);
79 bool notify = (cccd & 1) != 0;
80 bool indicate = (cccd & 2) != 0;
81 if (notify || indicate) {
82 this->clients_to_notify_[conn_id] = indicate;
83 } else {
84 this->clients_to_notify_.erase(conn_id);
85 }
86 });
87 }
88 this->descriptors_.push_back(descriptor);
89}
90
92 this->descriptors_.erase(std::remove(this->descriptors_.begin(), this->descriptors_.end(), descriptor),
93 this->descriptors_.end());
94}
95
97 this->service_ = service;
98 esp_attr_control_t control;
99 control.auto_rsp = ESP_GATT_RSP_BY_APP;
100
101 ESP_LOGV(TAG, "Creating characteristic - %s", this->uuid_.to_string().c_str());
102
103 esp_bt_uuid_t uuid = this->uuid_.get_uuid();
104 esp_err_t err = esp_ble_gatts_add_char(service->get_handle(), &uuid, static_cast<esp_gatt_perm_t>(this->permissions_),
105 this->properties_, nullptr, &control);
106
107 if (err != ESP_OK) {
108 ESP_LOGE(TAG, "esp_ble_gatts_add_char failed: %d", err);
109 return;
110 }
111
112 this->state_ = CREATING;
113}
114
116 if (this->state_ == CREATED)
117 return true;
118
119 if (this->state_ != CREATING_DEPENDENTS)
120 return false;
121
122 bool created = true;
123 for (auto *descriptor : this->descriptors_) {
124 created &= descriptor->is_created();
125 }
126 if (created)
127 this->state_ = CREATED;
128 return this->state_ == CREATED;
129}
130
132 if (this->state_ == FAILED)
133 return true;
134
135 bool failed = false;
136 for (auto *descriptor : this->descriptors_) {
137 failed |= descriptor->is_failed();
138 }
139 if (failed)
140 this->state_ = FAILED;
141 return this->state_ == FAILED;
142}
143
145 if (value) {
146 this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_BROADCAST);
147 } else {
148 this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_BROADCAST);
149 }
150}
152 if (value) {
153 this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_INDICATE);
154 } else {
155 this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_INDICATE);
156 }
157}
159 if (value) {
160 this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_NOTIFY);
161 } else {
162 this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_NOTIFY);
163 }
164}
166 if (value) {
167 this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_READ);
168 } else {
169 this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_READ);
170 }
171}
173 if (value) {
174 this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_WRITE);
175 } else {
176 this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_WRITE);
177 }
178}
180 if (value) {
181 this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
182 } else {
183 this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
184 }
185}
186
187void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
188 esp_ble_gatts_cb_param_t *param) {
189 switch (event) {
190 case ESP_GATTS_ADD_CHAR_EVT: {
191 if (this->uuid_ == ESPBTUUID::from_uuid(param->add_char.char_uuid)) {
192 this->handle_ = param->add_char.attr_handle;
193
194 for (auto *descriptor : this->descriptors_) {
195 descriptor->do_create(this);
196 }
197
198 this->state_ = CREATING_DEPENDENTS;
199 }
200 break;
201 }
202 case ESP_GATTS_READ_EVT: {
203 if (param->read.handle != this->handle_)
204 break; // Not this characteristic
205
206 if (!param->read.need_rsp)
207 break; // For some reason you can request a read but not want a response
208
210 param->read.conn_id);
211
212 uint16_t max_offset = 22;
213
214 esp_gatt_rsp_t response;
215 if (param->read.is_long) {
216 if (this->value_.size() - this->value_read_offset_ < max_offset) {
217 // Last message in the chain
218 response.attr_value.len = this->value_.size() - this->value_read_offset_;
219 response.attr_value.offset = this->value_read_offset_;
220 memcpy(response.attr_value.value, this->value_.data() + response.attr_value.offset, response.attr_value.len);
221 this->value_read_offset_ = 0;
222 } else {
223 response.attr_value.len = max_offset;
224 response.attr_value.offset = this->value_read_offset_;
225 memcpy(response.attr_value.value, this->value_.data() + response.attr_value.offset, response.attr_value.len);
226 this->value_read_offset_ += max_offset;
227 }
228 } else {
229 response.attr_value.offset = 0;
230 if (this->value_.size() + 1 > max_offset) {
231 response.attr_value.len = max_offset;
232 this->value_read_offset_ = max_offset;
233 } else {
234 response.attr_value.len = this->value_.size();
235 }
236 memcpy(response.attr_value.value, this->value_.data(), response.attr_value.len);
237 }
238
239 response.attr_value.handle = this->handle_;
240 response.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
241
242 esp_err_t err =
243 esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_OK, &response);
244 if (err != ESP_OK) {
245 ESP_LOGE(TAG, "esp_ble_gatts_send_response failed: %d", err);
246 }
247 break;
248 }
249 case ESP_GATTS_WRITE_EVT: {
250 if (this->handle_ != param->write.handle)
251 break;
252
253 if (param->write.is_prep) {
254 this->value_.insert(this->value_.end(), param->write.value, param->write.value + param->write.len);
255 this->write_event_ = true;
256 } else {
257 this->set_value(ByteBuffer::wrap(param->write.value, param->write.len));
258 }
259
260 if (param->write.need_rsp) {
261 esp_gatt_rsp_t response;
262
263 response.attr_value.len = param->write.len;
264 response.attr_value.handle = this->handle_;
265 response.attr_value.offset = param->write.offset;
266 response.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
267 memcpy(response.attr_value.value, param->write.value, param->write.len);
268
269 esp_err_t err =
270 esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, &response);
271
272 if (err != ESP_OK) {
273 ESP_LOGE(TAG, "esp_ble_gatts_send_response failed: %d", err);
274 }
275 }
276
277 if (!param->write.is_prep) {
279 BLECharacteristicEvt::VectorEvt::ON_WRITE, this->value_, param->write.conn_id);
280 }
281
282 break;
283 }
284
285 case ESP_GATTS_EXEC_WRITE_EVT: {
286 if (!this->write_event_)
287 break;
288 this->write_event_ = false;
289 if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) {
291 BLECharacteristicEvt::VectorEvt::ON_WRITE, this->value_, param->exec_write.conn_id);
292 }
293 esp_err_t err =
294 esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, nullptr);
295 if (err != ESP_OK) {
296 ESP_LOGE(TAG, "esp_ble_gatts_send_response failed: %d", err);
297 }
298 break;
299 }
300 default:
301 break;
302 }
303
304 for (auto *descriptor : this->descriptors_) {
305 descriptor->gatts_event_handler(event, gatts_if, param);
306 }
307}
308
309} // namespace esp32_ble_server
310} // namespace esphome
311
312#endif
A class modelled on the Java ByteBuffer class.
Definition bytebuffer.h:38
std::vector< uint8_t > get_data()
Definition bytebuffer.h:300
static ByteBuffer wrap(T value, Endian endianness=LITTLE)
Definition bytebuffer.h:156
std::string to_string() const
Definition ble_uuid.cpp:171
static ESPBTUUID from_uuid(esp_bt_uuid_t uuid)
Definition ble_uuid.cpp:97
static ESPBTUUID from_uint16(uint16_t uuid)
Definition ble_uuid.cpp:16
esp_bt_uuid_t get_uuid() const
Definition ble_uuid.cpp:170
void remove_descriptor(BLEDescriptor *descriptor)
BLECharacteristic(ESPBTUUID uuid, uint32_t properties)
void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
std::unordered_map< uint16_t, bool > clients_to_notify_
std::vector< BLEDescriptor * > descriptors_
void add_descriptor(BLEDescriptor *descriptor)
const std::unordered_set< uint16_t > & get_clients()
Definition ble_server.h:62
EventEmitterListenerID on(EvtType event, std::function< void(Args...)> listener)
void emit_(EvtType event, Args... args)
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
Definition helpers.h:191
uint16_t length
Definition tt21100.cpp:0