ESPHome  2025.2.0
ble_characteristic.cpp
Go to the documentation of this file.
1 #include "ble_characteristic.h"
2 #include "ble_server.h"
3 #include "ble_service.h"
4 
5 #include "esphome/core/log.h"
6 
7 #ifdef USE_ESP32
8 
9 namespace esphome {
10 namespace esp32_ble_server {
11 
12 static 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 
21 BLECharacteristic::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 
35 void BLECharacteristic::set_value(ByteBuffer buffer) { this->set_value(buffer.get_data()); }
36 
37 void 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 }
42 void 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 ||
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 
187 void 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
void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
const std::unordered_set< uint16_t > & get_clients()
Definition: ble_server.h:62
EventEmitterListenerID on(EvtType event, std::function< void(Args...)> listener)
Definition: event_emitter.h:19
std::vector< uint8_t > get_data()
Definition: bytebuffer.h:300
void remove_descriptor(BLEDescriptor *descriptor)
std::vector< BLEDescriptor * > descriptors_
static ESPBTUUID from_uuid(esp_bt_uuid_t uuid)
Definition: ble_uuid.cpp:97
A class modelled on the Java ByteBuffer class.
Definition: bytebuffer.h:38
static ESPBTUUID from_uint16(uint16_t uuid)
Definition: ble_uuid.cpp:16
BLECharacteristic(ESPBTUUID uuid, uint32_t properties)
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
std::string to_string() const
Definition: ble_uuid.cpp:171
std::unordered_map< uint16_t, bool > clients_to_notify_
void add_descriptor(BLEDescriptor *descriptor)
uint16_t length
Definition: tt21100.cpp:12
void emit_(EvtType event, Args... args)
Definition: event_emitter.h:32
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
static ByteBuffer wrap(T value, Endian endianness=LITTLE)
Definition: bytebuffer.h:156
esp_bt_uuid_t get_uuid() const
Definition: ble_uuid.cpp:170