ESPHome  2024.4.1
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(std::vector<uint8_t> value) {
36  xSemaphoreTake(this->set_value_lock_, 0L);
37  this->value_ = std::move(value);
38  xSemaphoreGive(this->set_value_lock_);
39 }
40 void BLECharacteristic::set_value(const std::string &value) {
41  this->set_value(std::vector<uint8_t>(value.begin(), value.end()));
42 }
43 void BLECharacteristic::set_value(const uint8_t *data, size_t length) {
44  this->set_value(std::vector<uint8_t>(data, data + length));
45 }
46 void BLECharacteristic::set_value(uint8_t &data) {
47  uint8_t temp[1];
48  temp[0] = data;
49  this->set_value(temp, 1);
50 }
51 void BLECharacteristic::set_value(uint16_t &data) {
52  uint8_t temp[2];
53  temp[0] = data;
54  temp[1] = data >> 8;
55  this->set_value(temp, 2);
56 }
57 void BLECharacteristic::set_value(uint32_t &data) {
58  uint8_t temp[4];
59  temp[0] = data;
60  temp[1] = data >> 8;
61  temp[2] = data >> 16;
62  temp[3] = data >> 24;
63  this->set_value(temp, 4);
64 }
66  uint8_t temp[4];
67  temp[0] = data;
68  temp[1] = data >> 8;
69  temp[2] = data >> 16;
70  temp[3] = data >> 24;
71  this->set_value(temp, 4);
72 }
73 void BLECharacteristic::set_value(float &data) {
74  float temp = data;
75  this->set_value((uint8_t *) &temp, 4);
76 }
77 void BLECharacteristic::set_value(double &data) {
78  double temp = data;
79  this->set_value((uint8_t *) &temp, 8);
80 }
81 void BLECharacteristic::set_value(bool &data) {
82  uint8_t temp[1];
83  temp[0] = data;
84  this->set_value(temp, 1);
85 }
86 
87 void BLECharacteristic::notify(bool notification) {
88  if (!notification) {
89  ESP_LOGW(TAG, "notification=false is not yet supported");
90  // TODO: Handle when notification=false
91  }
92  if (this->service_->get_server()->get_connected_client_count() == 0)
93  return;
94 
95  for (auto &client : this->service_->get_server()->get_clients()) {
96  size_t length = this->value_.size();
97  esp_err_t err = esp_ble_gatts_send_indicate(this->service_->get_server()->get_gatts_if(), client.first,
98  this->handle_, length, this->value_.data(), false);
99  if (err != ESP_OK) {
100  ESP_LOGE(TAG, "esp_ble_gatts_send_indicate failed %d", err);
101  return;
102  }
103  }
104 }
105 
106 void BLECharacteristic::add_descriptor(BLEDescriptor *descriptor) { this->descriptors_.push_back(descriptor); }
107 
109  this->descriptors_.erase(std::remove(this->descriptors_.begin(), this->descriptors_.end(), descriptor),
110  this->descriptors_.end());
111 }
112 
114  this->service_ = service;
115  esp_attr_control_t control;
116  control.auto_rsp = ESP_GATT_RSP_BY_APP;
117 
118  ESP_LOGV(TAG, "Creating characteristic - %s", this->uuid_.to_string().c_str());
119 
120  esp_bt_uuid_t uuid = this->uuid_.get_uuid();
121  esp_err_t err = esp_ble_gatts_add_char(service->get_handle(), &uuid, static_cast<esp_gatt_perm_t>(this->permissions_),
122  this->properties_, nullptr, &control);
123 
124  if (err != ESP_OK) {
125  ESP_LOGE(TAG, "esp_ble_gatts_add_char failed: %d", err);
126  return;
127  }
128 
129  this->state_ = CREATING;
130 }
131 
133  if (this->state_ == CREATED)
134  return true;
135 
136  if (this->state_ != CREATING_DEPENDENTS)
137  return false;
138 
139  bool created = true;
140  for (auto *descriptor : this->descriptors_) {
141  created &= descriptor->is_created();
142  }
143  if (created)
144  this->state_ = CREATED;
145  return this->state_ == CREATED;
146 }
147 
149  if (this->state_ == FAILED)
150  return true;
151 
152  bool failed = false;
153  for (auto *descriptor : this->descriptors_) {
154  failed |= descriptor->is_failed();
155  }
156  if (failed)
157  this->state_ = FAILED;
158  return this->state_ == FAILED;
159 }
160 
162  if (value) {
163  this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_BROADCAST);
164  } else {
165  this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_BROADCAST);
166  }
167 }
169  if (value) {
170  this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_INDICATE);
171  } else {
172  this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_INDICATE);
173  }
174 }
176  if (value) {
177  this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_NOTIFY);
178  } else {
179  this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_NOTIFY);
180  }
181 }
183  if (value) {
184  this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_READ);
185  } else {
186  this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_READ);
187  }
188 }
190  if (value) {
191  this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_WRITE);
192  } else {
193  this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_WRITE);
194  }
195 }
197  if (value) {
198  this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
199  } else {
200  this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
201  }
202 }
203 
204 void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
205  esp_ble_gatts_cb_param_t *param) {
206  switch (event) {
207  case ESP_GATTS_ADD_CHAR_EVT: {
208  if (this->uuid_ == ESPBTUUID::from_uuid(param->add_char.char_uuid)) {
209  this->handle_ = param->add_char.attr_handle;
210 
211  for (auto *descriptor : this->descriptors_) {
212  descriptor->do_create(this);
213  }
214 
215  this->state_ = CREATING_DEPENDENTS;
216  }
217  break;
218  }
219  case ESP_GATTS_READ_EVT: {
220  if (param->read.handle != this->handle_)
221  break; // Not this characteristic
222 
223  if (!param->read.need_rsp)
224  break; // For some reason you can request a read but not want a response
225 
226  uint16_t max_offset = 22;
227 
228  esp_gatt_rsp_t response;
229  if (param->read.is_long) {
230  if (this->value_.size() - this->value_read_offset_ < max_offset) {
231  // Last message in the chain
232  response.attr_value.len = this->value_.size() - this->value_read_offset_;
233  response.attr_value.offset = this->value_read_offset_;
234  memcpy(response.attr_value.value, this->value_.data() + response.attr_value.offset, response.attr_value.len);
235  this->value_read_offset_ = 0;
236  } else {
237  response.attr_value.len = max_offset;
238  response.attr_value.offset = this->value_read_offset_;
239  memcpy(response.attr_value.value, this->value_.data() + response.attr_value.offset, response.attr_value.len);
240  this->value_read_offset_ += max_offset;
241  }
242  } else {
243  response.attr_value.offset = 0;
244  if (this->value_.size() + 1 > max_offset) {
245  response.attr_value.len = max_offset;
246  this->value_read_offset_ = max_offset;
247  } else {
248  response.attr_value.len = this->value_.size();
249  }
250  memcpy(response.attr_value.value, this->value_.data(), response.attr_value.len);
251  }
252 
253  response.attr_value.handle = this->handle_;
254  response.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
255 
256  esp_err_t err =
257  esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_OK, &response);
258  if (err != ESP_OK) {
259  ESP_LOGE(TAG, "esp_ble_gatts_send_response failed: %d", err);
260  }
261  break;
262  }
263  case ESP_GATTS_WRITE_EVT: {
264  if (this->handle_ != param->write.handle)
265  return;
266 
267  if (param->write.is_prep) {
268  this->value_.insert(this->value_.end(), param->write.value, param->write.value + param->write.len);
269  this->write_event_ = true;
270  } else {
271  this->set_value(param->write.value, param->write.len);
272  }
273 
274  if (param->write.need_rsp) {
275  esp_gatt_rsp_t response;
276 
277  response.attr_value.len = param->write.len;
278  response.attr_value.handle = this->handle_;
279  response.attr_value.offset = param->write.offset;
280  response.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
281  memcpy(response.attr_value.value, param->write.value, param->write.len);
282 
283  esp_err_t err =
284  esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, &response);
285 
286  if (err != ESP_OK) {
287  ESP_LOGE(TAG, "esp_ble_gatts_send_response failed: %d", err);
288  }
289  }
290 
291  if (!param->write.is_prep) {
292  this->on_write_(this->value_);
293  }
294 
295  break;
296  }
297 
298  case ESP_GATTS_EXEC_WRITE_EVT: {
299  if (!this->write_event_)
300  break;
301  this->write_event_ = false;
302  if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) {
303  this->on_write_(this->value_);
304  }
305  esp_err_t err =
306  esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, nullptr);
307  if (err != ESP_OK) {
308  ESP_LOGE(TAG, "esp_ble_gatts_send_response failed: %d", err);
309  }
310  break;
311  }
312  default:
313  break;
314  }
315 
316  for (auto *descriptor : this->descriptors_) {
317  descriptor->gatts_event_handler(event, gatts_if, param);
318  }
319 }
320 
321 } // namespace esp32_ble_server
322 } // namespace esphome
323 
324 #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_map< uint16_t, void * > & get_clients()
Definition: ble_server.h:60
void set_value(const uint8_t *data, size_t length)
void remove_descriptor(BLEDescriptor *descriptor)
std::vector< BLEDescriptor * > descriptors_
static ESPBTUUID from_uuid(esp_bt_uuid_t uuid)
Definition: ble_uuid.cpp:91
BLECharacteristic(ESPBTUUID uuid, uint32_t properties)
std::function< void(const std::vector< uint8_t > &)> on_write_
std::string to_string() const
Definition: ble_uuid.cpp:165
void add_descriptor(BLEDescriptor *descriptor)
uint16_t length
Definition: tt21100.cpp:12
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
esp_bt_uuid_t get_uuid() const
Definition: ble_uuid.cpp:164