1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-04 19:03:47 +01:00

[esp32_ble_server] Create custom services, characteristics and descriptors (#7009)

Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
This commit is contained in:
Rodrigo Martín
2025-01-28 12:00:28 +01:00
committed by GitHub
parent dd18a219db
commit f7f8bf4da4
26 changed files with 1221 additions and 342 deletions

View File

@@ -32,70 +32,36 @@ BLECharacteristic::BLECharacteristic(const ESPBTUUID uuid, uint32_t properties)
this->set_write_no_response_property((properties & PROPERTY_WRITE_NR) != 0);
}
void BLECharacteristic::set_value(std::vector<uint8_t> value) {
void BLECharacteristic::set_value(ByteBuffer buffer) { this->set_value(buffer.get_data()); }
void BLECharacteristic::set_value(const std::vector<uint8_t> &buffer) {
xSemaphoreTake(this->set_value_lock_, 0L);
this->value_ = std::move(value);
this->value_ = buffer;
xSemaphoreGive(this->set_value_lock_);
}
void BLECharacteristic::set_value(const std::string &value) {
this->set_value(std::vector<uint8_t>(value.begin(), value.end()));
}
void BLECharacteristic::set_value(const uint8_t *data, size_t length) {
this->set_value(std::vector<uint8_t>(data, data + length));
}
void BLECharacteristic::set_value(uint8_t &data) {
uint8_t temp[1];
temp[0] = data;
this->set_value(temp, 1);
}
void BLECharacteristic::set_value(uint16_t &data) {
uint8_t temp[2];
temp[0] = data;
temp[1] = data >> 8;
this->set_value(temp, 2);
}
void BLECharacteristic::set_value(uint32_t &data) {
uint8_t temp[4];
temp[0] = data;
temp[1] = data >> 8;
temp[2] = data >> 16;
temp[3] = data >> 24;
this->set_value(temp, 4);
}
void BLECharacteristic::set_value(int &data) {
uint8_t temp[4];
temp[0] = data;
temp[1] = data >> 8;
temp[2] = data >> 16;
temp[3] = data >> 24;
this->set_value(temp, 4);
}
void BLECharacteristic::set_value(float &data) {
float temp = data;
this->set_value((uint8_t *) &temp, 4);
}
void BLECharacteristic::set_value(double &data) {
double temp = data;
this->set_value((uint8_t *) &temp, 8);
}
void BLECharacteristic::set_value(bool &data) {
uint8_t temp[1];
temp[0] = data;
this->set_value(temp, 1);
void BLECharacteristic::set_value(const std::string &buffer) {
this->set_value(std::vector<uint8_t>(buffer.begin(), buffer.end()));
}
void BLECharacteristic::notify(bool notification) {
if (!notification) {
ESP_LOGW(TAG, "notification=false is not yet supported");
// TODO: Handle when notification=false
}
if (this->service_->get_server()->get_connected_client_count() == 0)
void BLECharacteristic::notify() {
if (this->service_ == nullptr || this->service_->get_server() == nullptr ||
this->service_->get_server()->get_connected_client_count() == 0)
return;
for (auto &client : this->service_->get_server()->get_clients()) {
size_t length = this->value_.size();
esp_err_t err = esp_ble_gatts_send_indicate(this->service_->get_server()->get_gatts_if(), client.first,
this->handle_, length, this->value_.data(), false);
// If the client is not in the list of clients to notify, skip it
if (this->clients_to_notify_.count(client) == 0)
continue;
// If the client is in the list of clients to notify, check if it requires an ack (i.e. INDICATE)
bool require_ack = this->clients_to_notify_[client];
// TODO: Remove this block when INDICATE acknowledgment is supported
if (require_ack) {
ESP_LOGW(TAG, "INDICATE acknowledgment is not yet supported (i.e. it works as a NOTIFY)");
require_ack = false;
}
esp_err_t err = esp_ble_gatts_send_indicate(this->service_->get_server()->get_gatts_if(), client, this->handle_,
length, this->value_.data(), require_ack);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ble_gatts_send_indicate failed %d", err);
return;
@@ -103,7 +69,24 @@ void BLECharacteristic::notify(bool notification) {
}
}
void BLECharacteristic::add_descriptor(BLEDescriptor *descriptor) { this->descriptors_.push_back(descriptor); }
void BLECharacteristic::add_descriptor(BLEDescriptor *descriptor) {
// If the descriptor is the CCCD descriptor, listen to its write event to know if the client wants to be notified
if (descriptor->get_uuid() == ESPBTUUID::from_uint16(ESP_GATT_UUID_CHAR_CLIENT_CONFIG)) {
descriptor->on(BLEDescriptorEvt::VectorEvt::ON_WRITE, [this](const std::vector<uint8_t> &value, uint16_t conn_id) {
if (value.size() != 2)
return;
uint16_t cccd = encode_uint16(value[1], value[0]);
bool notify = (cccd & 1) != 0;
bool indicate = (cccd & 2) != 0;
if (notify || indicate) {
this->clients_to_notify_[conn_id] = indicate;
} else {
this->clients_to_notify_.erase(conn_id);
}
});
}
this->descriptors_.push_back(descriptor);
}
void BLECharacteristic::remove_descriptor(BLEDescriptor *descriptor) {
this->descriptors_.erase(std::remove(this->descriptors_.begin(), this->descriptors_.end(), descriptor),
@@ -223,6 +206,9 @@ void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt
if (!param->read.need_rsp)
break; // For some reason you can request a read but not want a response
this->EventEmitter<BLECharacteristicEvt::EmptyEvt, uint16_t>::emit_(BLECharacteristicEvt::EmptyEvt::ON_READ,
param->read.conn_id);
uint16_t max_offset = 22;
esp_gatt_rsp_t response;
@@ -262,13 +248,13 @@ void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt
}
case ESP_GATTS_WRITE_EVT: {
if (this->handle_ != param->write.handle)
return;
break;
if (param->write.is_prep) {
this->value_.insert(this->value_.end(), param->write.value, param->write.value + param->write.len);
this->write_event_ = true;
} else {
this->set_value(param->write.value, param->write.len);
this->set_value(ByteBuffer::wrap(param->write.value, param->write.len));
}
if (param->write.need_rsp) {
@@ -289,7 +275,8 @@ void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt
}
if (!param->write.is_prep) {
this->on_write_(this->value_);
this->EventEmitter<BLECharacteristicEvt::VectorEvt, std::vector<uint8_t>, uint16_t>::emit_(
BLECharacteristicEvt::VectorEvt::ON_WRITE, this->value_, param->write.conn_id);
}
break;
@@ -300,7 +287,8 @@ void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt
break;
this->write_event_ = false;
if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) {
this->on_write_(this->value_);
this->EventEmitter<BLECharacteristicEvt::VectorEvt, std::vector<uint8_t>, uint16_t>::emit_(
BLECharacteristicEvt::VectorEvt::ON_WRITE, this->value_, param->exec_write.conn_id);
}
esp_err_t err =
esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, nullptr);