BLE loop use (#1882)

This commit is contained in:
Jesse Hills 2021-06-10 14:04:39 +12:00 committed by GitHub
parent ea0127e42b
commit 99d90845b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 272 additions and 165 deletions

View File

@ -20,14 +20,13 @@ void ESP32BLE::setup() {
global_ble = this; global_ble = this;
ESP_LOGCONFIG(TAG, "Setting up BLE..."); ESP_LOGCONFIG(TAG, "Setting up BLE...");
xTaskCreatePinnedToCore(ESP32BLE::ble_core_task_, if (!ble_setup_()) {
"ble_task", // name ESP_LOGE(TAG, "BLE could not be set up");
10000, // stack size this->mark_failed();
nullptr, // input params return;
1, // priority }
nullptr, // handle, not needed
0 // core ESP_LOGD(TAG, "BLE setup complete");
);
} }
void ESP32BLE::mark_failed() { void ESP32BLE::mark_failed() {
@ -37,23 +36,6 @@ void ESP32BLE::mark_failed() {
} }
} }
bool ESP32BLE::can_proceed() { return this->ready_; }
void ESP32BLE::ble_core_task_(void *params) {
if (!ble_setup_()) {
ESP_LOGE(TAG, "BLE could not be set up");
global_ble->mark_failed();
return;
}
global_ble->ready_ = true;
ESP_LOGD(TAG, "BLE Setup complete");
while (true) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
bool ESP32BLE::ble_setup_() { bool ESP32BLE::ble_setup_() {
esp_err_t err = nvs_flash_init(); esp_err_t err = nvs_flash_init();
if (err != ESP_OK) { if (err != ESP_OK) {
@ -84,7 +66,7 @@ bool ESP32BLE::ble_setup_() {
return false; return false;
} }
if (global_ble->has_server()) { if (this->has_server()) {
err = esp_ble_gatts_register_callback(ESP32BLE::gatts_event_handler); err = esp_ble_gatts_register_callback(ESP32BLE::gatts_event_handler);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ble_gatts_register_callback failed: %d", err); ESP_LOGE(TAG, "esp_ble_gatts_register_callback failed: %d", err);
@ -92,7 +74,7 @@ bool ESP32BLE::ble_setup_() {
} }
} }
if (global_ble->has_client()) { if (this->has_client()) {
err = esp_ble_gattc_register_callback(ESP32BLE::gattc_event_handler); err = esp_ble_gattc_register_callback(ESP32BLE::gattc_event_handler);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ble_gattc_register_callback failed: %d", err); ESP_LOGE(TAG, "esp_ble_gattc_register_callback failed: %d", err);
@ -119,27 +101,28 @@ bool ESP32BLE::ble_setup_() {
return true; return true;
} }
// void ESP32BLE::loop() { void ESP32BLE::loop() {
// BLEEvent *ble_event = this->ble_events_.pop(); BLEEvent *ble_event = this->ble_events_.pop();
// while (ble_event != nullptr) { while (ble_event != nullptr) {
// switch (ble_event->type_) { switch (ble_event->type_) {
// case ble_event->GATTS: case ble_event->GATTS:
// this->real_gatts_event_handler_(ble_event->event_.gatts.gatts_event, ble_event->event_.gatts.gatts_if, this->real_gatts_event_handler_(ble_event->event_.gatts.gatts_event, ble_event->event_.gatts.gatts_if,
// &ble_event->event_.gatts.gatts_param); &ble_event->event_.gatts.gatts_param);
// break; break;
// case ble_event->GAP: case ble_event->GAP:
// this->real_gap_event_handler_(ble_event->event_.gap.gap_event, &ble_event->event_.gap.gap_param); this->real_gap_event_handler_(ble_event->event_.gap.gap_event, &ble_event->event_.gap.gap_param);
// break; break;
// default: default:
// break; break;
// } }
// delete ble_event; delete ble_event;
// ble_event = this->ble_events_.pop(); ble_event = this->ble_events_.pop();
// } }
// } }
void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
global_ble->real_gap_event_handler_(event, param); BLEEvent *new_event = new BLEEvent(event, param);
global_ble->ble_events_.push(new_event);
} }
void ESP32BLE::real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { void ESP32BLE::real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
@ -152,7 +135,8 @@ void ESP32BLE::real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap
void ESP32BLE::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, void ESP32BLE::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t *param) { esp_ble_gatts_cb_param_t *param) {
global_ble->real_gatts_event_handler_(event, gatts_if, param); BLEEvent *new_event = new BLEEvent(event, gatts_if, param);
global_ble->ble_events_.push(new_event);
} }
void ESP32BLE::real_gatts_event_handler_(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, void ESP32BLE::real_gatts_event_handler_(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,

View File

@ -23,17 +23,14 @@ typedef struct {
class ESP32BLE : public Component { class ESP32BLE : public Component {
public: public:
void setup() override; void setup() override;
// void loop() override; void loop() override;
void dump_config() override; void dump_config() override;
float get_setup_priority() const override; float get_setup_priority() const override;
void mark_failed() override; void mark_failed() override;
bool can_proceed() override;
bool has_server() { return this->server_ != nullptr; } bool has_server() { return this->server_ != nullptr; }
bool has_client() { return false; } bool has_client() { return false; }
bool is_ready() { return this->ready_; }
void set_server(BLEServer *server) { this->server_ = server; } void set_server(BLEServer *server) { this->server_ = server; }
protected: protected:
@ -45,10 +42,7 @@ class ESP32BLE : public Component {
void real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); void real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
void real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); void real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
static void ble_core_task_(void *params); bool ble_setup_();
static bool ble_setup_();
bool ready_{false};
BLEServer *server_{nullptr}; BLEServer *server_{nullptr};
Queue<BLEEvent> ble_events_; Queue<BLEEvent> ble_events_;

View File

@ -13,10 +13,8 @@ static const char *TAG = "esp32_ble.characteristic";
BLECharacteristic::BLECharacteristic(const ESPBTUUID uuid, uint32_t properties) : uuid_(uuid) { BLECharacteristic::BLECharacteristic(const ESPBTUUID uuid, uint32_t properties) : uuid_(uuid) {
this->set_value_lock_ = xSemaphoreCreateBinary(); this->set_value_lock_ = xSemaphoreCreateBinary();
this->create_lock_ = xSemaphoreCreateBinary();
xSemaphoreGive(this->set_value_lock_); xSemaphoreGive(this->set_value_lock_);
xSemaphoreGive(this->create_lock_);
this->properties_ = (esp_gatt_char_prop_t) 0; this->properties_ = (esp_gatt_char_prop_t) 0;
this->set_broadcast_property((properties & PROPERTY_BROADCAST) != 0); this->set_broadcast_property((properties & PROPERTY_BROADCAST) != 0);
@ -100,12 +98,11 @@ void BLECharacteristic::notify(bool notification) {
void BLECharacteristic::add_descriptor(BLEDescriptor *descriptor) { this->descriptors_.push_back(descriptor); } void BLECharacteristic::add_descriptor(BLEDescriptor *descriptor) { this->descriptors_.push_back(descriptor); }
bool BLECharacteristic::do_create(BLEService *service) { void BLECharacteristic::do_create(BLEService *service) {
this->service_ = service; this->service_ = service;
esp_attr_control_t control; esp_attr_control_t control;
control.auto_rsp = ESP_GATT_RSP_BY_APP; control.auto_rsp = ESP_GATT_RSP_BY_APP;
xSemaphoreTake(this->create_lock_, portMAX_DELAY);
ESP_LOGV(TAG, "Creating characteristic - %s", this->uuid_.to_string().c_str()); ESP_LOGV(TAG, "Creating characteristic - %s", this->uuid_.to_string().c_str());
esp_bt_uuid_t uuid = this->uuid_.get_uuid(); esp_bt_uuid_t uuid = this->uuid_.get_uuid();
@ -114,15 +111,39 @@ bool BLECharacteristic::do_create(BLEService *service) {
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ble_gatts_add_char failed: %d", err); ESP_LOGE(TAG, "esp_ble_gatts_add_char failed: %d", err);
return false; return;
} }
xSemaphoreWait(this->create_lock_, portMAX_DELAY); this->state_ = CREATING;
}
for (auto *descriptor : this->descriptors_) { bool BLECharacteristic::is_created() {
descriptor->do_create(this); if (this->state_ == CREATED)
}
return true; return true;
if (this->state_ != CREATING_DEPENDENTS)
return false;
bool created = true;
for (auto *descriptor : this->descriptors_) {
created &= descriptor->is_created();
}
if (created)
this->state_ = CREATED;
return this->state_ == CREATED;
}
bool BLECharacteristic::is_failed() {
if (this->state_ == FAILED)
return true;
bool failed = false;
for (auto *descriptor : this->descriptors_) {
failed |= descriptor->is_failed();
}
if (failed)
this->state_ = FAILED;
return this->state_ == FAILED;
} }
void BLECharacteristic::set_broadcast_property(bool value) { void BLECharacteristic::set_broadcast_property(bool value) {
@ -168,7 +189,12 @@ void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt
case ESP_GATTS_ADD_CHAR_EVT: { case ESP_GATTS_ADD_CHAR_EVT: {
if (this->uuid_ == ESPBTUUID::from_uuid(param->add_char.char_uuid)) { if (this->uuid_ == ESPBTUUID::from_uuid(param->add_char.char_uuid)) {
this->handle_ = param->add_char.attr_handle; this->handle_ = param->add_char.attr_handle;
xSemaphoreGive(this->create_lock_);
for (auto *descriptor : this->descriptors_) {
descriptor->do_create(this);
}
this->state_ = CREATING_DEPENDENTS;
} }
break; break;
} }

View File

@ -42,10 +42,10 @@ class BLECharacteristic {
void notify(bool notification = true); void notify(bool notification = true);
bool do_create(BLEService *service); void do_create(BLEService *service);
void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
void on_write(const std::function<void(const std::vector<uint8_t> &)> &func) { this->on_write_ = func; } void on_write(const std::function<void(const std::vector<uint8_t> &)> &&func) { this->on_write_ = std::move(func); }
void add_descriptor(BLEDescriptor *descriptor); void add_descriptor(BLEDescriptor *descriptor);
@ -60,6 +60,9 @@ class BLECharacteristic {
static const uint32_t PROPERTY_INDICATE = 1 << 4; static const uint32_t PROPERTY_INDICATE = 1 << 4;
static const uint32_t PROPERTY_WRITE_NR = 1 << 5; static const uint32_t PROPERTY_WRITE_NR = 1 << 5;
bool is_created();
bool is_failed();
protected: protected:
bool write_event_{false}; bool write_event_{false};
BLEService *service_; BLEService *service_;
@ -70,13 +73,20 @@ class BLECharacteristic {
uint16_t value_read_offset_{0}; uint16_t value_read_offset_{0};
std::vector<uint8_t> value_; std::vector<uint8_t> value_;
SemaphoreHandle_t set_value_lock_; SemaphoreHandle_t set_value_lock_;
SemaphoreHandle_t create_lock_;
std::vector<BLEDescriptor *> descriptors_; std::vector<BLEDescriptor *> descriptors_;
std::function<void(const std::vector<uint8_t> &)> on_write_; std::function<void(const std::vector<uint8_t> &)> on_write_;
esp_gatt_perm_t permissions_ = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; esp_gatt_perm_t permissions_ = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
enum State : uint8_t {
FAILED = 0x00,
INIT,
CREATING,
CREATING_DEPENDENTS,
CREATED,
} state_{INIT};
}; };
} // namespace esp32_ble } // namespace esp32_ble

View File

@ -16,30 +16,25 @@ BLEDescriptor::BLEDescriptor(ESPBTUUID uuid, uint16_t max_len) {
this->value_.attr_len = 0; this->value_.attr_len = 0;
this->value_.attr_max_len = max_len; this->value_.attr_max_len = max_len;
this->value_.attr_value = (uint8_t *) malloc(max_len); this->value_.attr_value = (uint8_t *) malloc(max_len);
this->create_lock_ = xSemaphoreCreateBinary();
xSemaphoreGive(this->create_lock_);
} }
BLEDescriptor::~BLEDescriptor() { free(this->value_.attr_value); } BLEDescriptor::~BLEDescriptor() { free(this->value_.attr_value); }
bool BLEDescriptor::do_create(BLECharacteristic *characteristic) { void BLEDescriptor::do_create(BLECharacteristic *characteristic) {
this->characteristic_ = characteristic; this->characteristic_ = characteristic;
esp_attr_control_t control; esp_attr_control_t control;
control.auto_rsp = ESP_GATT_AUTO_RSP; control.auto_rsp = ESP_GATT_AUTO_RSP;
xSemaphoreTake(this->create_lock_, portMAX_DELAY);
ESP_LOGV(TAG, "Creating descriptor - %s", this->uuid_.to_string().c_str()); ESP_LOGV(TAG, "Creating descriptor - %s", this->uuid_.to_string().c_str());
esp_bt_uuid_t uuid = this->uuid_.get_uuid(); esp_bt_uuid_t uuid = this->uuid_.get_uuid();
esp_err_t err = esp_ble_gatts_add_char_descr(this->characteristic_->get_service()->get_handle(), &uuid, esp_err_t err = esp_ble_gatts_add_char_descr(this->characteristic_->get_service()->get_handle(), &uuid,
this->permissions_, &this->value_, &control); this->permissions_, &this->value_, &control);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ble_gatts_add_char_descr failed: %d", err); ESP_LOGE(TAG, "esp_ble_gatts_add_char_descr failed: %d", err);
return false; this->state_ = FAILED;
return;
} }
xSemaphoreWait(this->create_lock_, portMAX_DELAY); this->state_ = CREATING;
return true;
} }
void BLEDescriptor::set_value(const std::string &value) { this->set_value((uint8_t *) value.data(), value.length()); } void BLEDescriptor::set_value(const std::string &value) { this->set_value((uint8_t *) value.data(), value.length()); }
@ -60,7 +55,7 @@ void BLEDescriptor::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_
this->characteristic_->get_service()->get_handle() == param->add_char_descr.service_handle && this->characteristic_->get_service()->get_handle() == param->add_char_descr.service_handle &&
this->characteristic_ == this->characteristic_->get_service()->get_last_created_characteristic()) { this->characteristic_ == this->characteristic_->get_service()->get_last_created_characteristic()) {
this->handle_ = param->add_char_descr.attr_handle; this->handle_ = param->add_char_descr.attr_handle;
xSemaphoreGive(this->create_lock_); this->state_ = CREATED;
} }
break; break;
} }

View File

@ -16,22 +16,31 @@ class BLEDescriptor {
public: public:
BLEDescriptor(ESPBTUUID uuid, uint16_t max_len = 100); BLEDescriptor(ESPBTUUID uuid, uint16_t max_len = 100);
virtual ~BLEDescriptor(); virtual ~BLEDescriptor();
bool do_create(BLECharacteristic *characteristic); void do_create(BLECharacteristic *characteristic);
void set_value(const std::string &value); void set_value(const std::string &value);
void set_value(const uint8_t *data, size_t length); void set_value(const uint8_t *data, size_t length);
void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
bool is_created() { return this->state_ == CREATED; }
bool is_failed() { return this->state_ == FAILED; }
protected: protected:
BLECharacteristic *characteristic_{nullptr}; BLECharacteristic *characteristic_{nullptr};
ESPBTUUID uuid_; ESPBTUUID uuid_;
uint16_t handle_{0xFFFF}; uint16_t handle_{0xFFFF};
SemaphoreHandle_t create_lock_;
esp_attr_value_t value_; esp_attr_value_t value_;
esp_gatt_perm_t permissions_ = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; esp_gatt_perm_t permissions_ = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
enum State : uint8_t {
FAILED = 0x00,
INIT,
CREATING,
CREATED,
} state_{INIT};
}; };
} // namespace esp32_ble } // namespace esp32_ble

View File

@ -18,7 +18,7 @@ namespace esp32_ble {
static const char *TAG = "esp32_ble.server"; static const char *TAG = "esp32_ble.server";
static const uint16_t DEVICE_INFORMATION_SERVICE_UUID = 0x180A; static const uint16_t device_information_service__UUID = 0x180A;
static const uint16_t MODEL_UUID = 0x2A24; static const uint16_t MODEL_UUID = 0x2A24;
static const uint16_t VERSION_UUID = 0x2A26; static const uint16_t VERSION_UUID = 0x2A26;
static const uint16_t MANUFACTURER_UUID = 0x2A29; static const uint16_t MANUFACTURER_UUID = 0x2A29;
@ -32,30 +32,28 @@ void BLEServer::setup() {
ESP_LOGD(TAG, "Setting up BLE Server..."); ESP_LOGD(TAG, "Setting up BLE Server...");
global_ble_server = this; global_ble_server = this;
this->register_lock_ = xSemaphoreCreateBinary();
xSemaphoreGive(this->register_lock_);
this->advertising_ = new BLEAdvertising(); this->advertising_ = new BLEAdvertising();
this->setup_server_();
for (auto *component : this->service_components_) {
component->setup_service();
}
ESP_LOGD(TAG, "BLE Server set up complete...");
} }
void BLEServer::setup_server_() { void BLEServer::loop() {
xSemaphoreTake(this->register_lock_, portMAX_DELAY); switch (this->state_) {
case RUNNING:
return;
case INIT: {
esp_err_t err = esp_ble_gatts_app_register(0); esp_err_t err = esp_ble_gatts_app_register(0);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ble_gatts_app_register failed: %d", err); ESP_LOGE(TAG, "esp_ble_gatts_app_register failed: %d", err);
this->mark_failed(); this->mark_failed();
return; return;
} }
xSemaphoreWait(this->register_lock_, portMAX_DELAY); this->state_ = REGISTERING;
break;
this->device_information_service = this->create_service(DEVICE_INFORMATION_SERVICE_UUID); }
case REGISTERING: {
if (this->registered_) {
this->device_information_service_ = this->create_service(device_information_service__UUID);
this->create_device_characteristics_(); this->create_device_characteristics_();
@ -63,28 +61,49 @@ void BLEServer::setup_server_() {
this->advertising_->set_min_preferred_interval(0x06); this->advertising_->set_min_preferred_interval(0x06);
this->advertising_->start(); this->advertising_->start();
this->device_information_service->start(); this->state_ = STARTING_SERVICE;
}
break;
}
case STARTING_SERVICE: {
if (this->device_information_service_->is_running()) {
for (auto *component : this->service_components_) {
component->setup_service();
}
this->state_ = SETTING_UP_COMPONENT_SERVICES;
} else if (!this->device_information_service_->is_starting()) {
this->device_information_service_->start();
}
break;
}
case SETTING_UP_COMPONENT_SERVICES: {
this->state_ = RUNNING;
this->can_proceed_ = true;
ESP_LOGD(TAG, "BLE server setup successfully");
break;
}
}
} }
bool BLEServer::create_device_characteristics_() { bool BLEServer::create_device_characteristics_() {
if (this->model_.has_value()) { if (this->model_.has_value()) {
BLECharacteristic *model = BLECharacteristic *model =
this->device_information_service->create_characteristic(MODEL_UUID, BLECharacteristic::PROPERTY_READ); this->device_information_service_->create_characteristic(MODEL_UUID, BLECharacteristic::PROPERTY_READ);
model->set_value(this->model_.value()); model->set_value(this->model_.value());
} else { } else {
#ifdef ARDUINO_BOARD #ifdef ARDUINO_BOARD
BLECharacteristic *model = BLECharacteristic *model =
this->device_information_service->create_characteristic(MODEL_UUID, BLECharacteristic::PROPERTY_READ); this->device_information_service_->create_characteristic(MODEL_UUID, BLECharacteristic::PROPERTY_READ);
model->set_value(ARDUINO_BOARD); model->set_value(ARDUINO_BOARD);
#endif #endif
} }
BLECharacteristic *version = BLECharacteristic *version =
this->device_information_service->create_characteristic(VERSION_UUID, BLECharacteristic::PROPERTY_READ); this->device_information_service_->create_characteristic(VERSION_UUID, BLECharacteristic::PROPERTY_READ);
version->set_value("ESPHome " ESPHOME_VERSION); version->set_value("ESPHome " ESPHOME_VERSION);
BLECharacteristic *manufacturer = BLECharacteristic *manufacturer =
this->device_information_service->create_characteristic(MANUFACTURER_UUID, BLECharacteristic::PROPERTY_READ); this->device_information_service_->create_characteristic(MANUFACTURER_UUID, BLECharacteristic::PROPERTY_READ);
manufacturer->set_value(this->manufacturer_); manufacturer->set_value(this->manufacturer_);
return true; return true;
@ -134,7 +153,7 @@ void BLEServer::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t ga
} }
case ESP_GATTS_REG_EVT: { case ESP_GATTS_REG_EVT: {
this->gatts_if_ = gatts_if; this->gatts_if_ = gatts_if;
xSemaphoreGive(this->register_lock_); this->registered_ = true;
break; break;
} }
default: default:

View File

@ -24,13 +24,17 @@ class BLEServiceComponent {
virtual void setup_service(); virtual void setup_service();
virtual void on_client_connect(){}; virtual void on_client_connect(){};
virtual void on_client_disconnect(){}; virtual void on_client_disconnect(){};
virtual void start();
virtual void stop();
}; };
class BLEServer : public Component { class BLEServer : public Component {
public: public:
void setup() override; void setup() override;
void loop() override;
void dump_config() override; void dump_config() override;
float get_setup_priority() const override; float get_setup_priority() const override;
bool can_proceed() override { return this->can_proceed_; }
void teardown(); void teardown();
@ -53,27 +57,35 @@ class BLEServer : public Component {
protected: protected:
bool create_device_characteristics_(); bool create_device_characteristics_();
void setup_server_();
void add_client_(uint16_t conn_id, void *client) { void add_client_(uint16_t conn_id, void *client) {
this->clients_.insert(std::pair<uint16_t, void *>(conn_id, client)); this->clients_.insert(std::pair<uint16_t, void *>(conn_id, client));
} }
bool remove_client_(uint16_t conn_id) { return this->clients_.erase(conn_id) > 0; } bool remove_client_(uint16_t conn_id) { return this->clients_.erase(conn_id) > 0; }
bool can_proceed_{false};
std::string manufacturer_; std::string manufacturer_;
optional<std::string> model_; optional<std::string> model_;
esp_gatt_if_t gatts_if_{0}; esp_gatt_if_t gatts_if_{0};
bool registered_{false};
BLEAdvertising *advertising_; BLEAdvertising *advertising_;
uint32_t connected_clients_{0}; uint32_t connected_clients_{0};
std::map<uint16_t, void *> clients_; std::map<uint16_t, void *> clients_;
std::vector<BLEService *> services_; std::vector<BLEService *> services_;
BLEService *device_information_service; BLEService *device_information_service_;
std::vector<BLEServiceComponent *> service_components_; std::vector<BLEServiceComponent *> service_components_;
SemaphoreHandle_t register_lock_; enum State : uint8_t {
INIT = 0x00,
REGISTERING,
STARTING_SERVICE,
SETTING_UP_COMPONENT_SERVICES,
RUNNING,
} state_{INIT};
}; };
extern BLEServer *global_ble_server; extern BLEServer *global_ble_server;

View File

@ -10,15 +10,7 @@ namespace esp32_ble {
static const char *TAG = "esp32_ble.service"; static const char *TAG = "esp32_ble.service";
BLEService::BLEService(ESPBTUUID uuid, uint16_t num_handles, uint8_t inst_id) BLEService::BLEService(ESPBTUUID uuid, uint16_t num_handles, uint8_t inst_id)
: uuid_(uuid), num_handles_(num_handles), inst_id_(inst_id) { : uuid_(uuid), num_handles_(num_handles), inst_id_(inst_id) {}
this->create_lock_ = xSemaphoreCreateBinary();
this->start_lock_ = xSemaphoreCreateBinary();
this->stop_lock_ = xSemaphoreCreateBinary();
xSemaphoreGive(this->create_lock_);
xSemaphoreGive(this->start_lock_);
xSemaphoreGive(this->stop_lock_);
}
BLEService::~BLEService() { BLEService::~BLEService() {
for (auto &chr : this->characteristics_) for (auto &chr : this->characteristics_)
@ -47,10 +39,9 @@ BLECharacteristic *BLEService::create_characteristic(ESPBTUUID uuid, esp_gatt_ch
return characteristic; return characteristic;
} }
bool BLEService::do_create(BLEServer *server) { void BLEService::do_create(BLEServer *server) {
this->server_ = server; this->server_ = server;
xSemaphoreTake(this->create_lock_, portMAX_DELAY);
esp_gatt_srvc_id_t srvc_id; esp_gatt_srvc_id_t srvc_id;
srvc_id.is_primary = true; srvc_id.is_primary = true;
srvc_id.id.inst_id = this->inst_id_; srvc_id.id.inst_id = this->inst_id_;
@ -59,36 +50,58 @@ bool BLEService::do_create(BLEServer *server) {
esp_err_t err = esp_ble_gatts_create_service(server->get_gatts_if(), &srvc_id, this->num_handles_); esp_err_t err = esp_ble_gatts_create_service(server->get_gatts_if(), &srvc_id, this->num_handles_);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ble_gatts_create_service failed: %d", err); ESP_LOGE(TAG, "esp_ble_gatts_create_service failed: %d", err);
return false; this->init_state_ = FAILED;
return;
} }
xSemaphoreWait(this->create_lock_, portMAX_DELAY); this->init_state_ = CREATING;
}
bool BLEService::do_create_characteristics_() {
if (this->created_characteristic_count_ >= this->characteristics_.size() &&
(this->last_created_characteristic_ == nullptr || this->last_created_characteristic_->is_created()))
return false; // Signifies there are no characteristics, or they are all finished being created.
if (this->last_created_characteristic_ != nullptr && !this->last_created_characteristic_->is_created())
return true; // Signifies that the previous characteristic is still being created.
auto *characteristic = this->characteristics_[this->created_characteristic_count_++];
this->last_created_characteristic_ = characteristic;
characteristic->do_create(this);
return true; return true;
} }
void BLEService::start() { void BLEService::start() {
for (auto *characteristic : this->characteristics_) { if (this->do_create_characteristics_())
this->last_created_characteristic_ = characteristic; return;
characteristic->do_create(this);
}
xSemaphoreTake(this->start_lock_, portMAX_DELAY);
esp_err_t err = esp_ble_gatts_start_service(this->handle_); esp_err_t err = esp_ble_gatts_start_service(this->handle_);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ble_gatts_start_service failed: %d", err); ESP_LOGE(TAG, "esp_ble_gatts_start_service failed: %d", err);
return; return;
} }
xSemaphoreWait(this->start_lock_, portMAX_DELAY); this->running_state_ = STARTING;
} }
void BLEService::stop() { void BLEService::stop() {
xSemaphoreTake(this->stop_lock_, portMAX_DELAY);
esp_err_t err = esp_ble_gatts_stop_service(this->handle_); esp_err_t err = esp_ble_gatts_stop_service(this->handle_);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ble_gatts_stop_service failed: %d", err); ESP_LOGE(TAG, "esp_ble_gatts_stop_service failed: %d", err);
return; return;
} }
xSemaphoreWait(this->stop_lock_, portMAX_DELAY); this->running_state_ = STOPPING;
}
bool BLEService::is_created() { return this->init_state_ == CREATED; }
bool BLEService::is_failed() {
if (this->init_state_ == FAILED)
return true;
bool failed = false;
for (auto *characteristic : this->characteristics_)
failed |= characteristic->is_failed();
if (failed)
this->init_state_ = FAILED;
return this->init_state_ == FAILED;
} }
void BLEService::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, void BLEService::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
@ -98,19 +111,19 @@ void BLEService::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t g
if (this->uuid_ == ESPBTUUID::from_uuid(param->create.service_id.id.uuid) && if (this->uuid_ == ESPBTUUID::from_uuid(param->create.service_id.id.uuid) &&
this->inst_id_ == param->create.service_id.id.inst_id) { this->inst_id_ == param->create.service_id.id.inst_id) {
this->handle_ = param->create.service_handle; this->handle_ = param->create.service_handle;
xSemaphoreGive(this->create_lock_); this->init_state_ = CREATED;
} }
break; break;
} }
case ESP_GATTS_START_EVT: { case ESP_GATTS_START_EVT: {
if (param->start.service_handle == this->handle_) { if (param->start.service_handle == this->handle_) {
xSemaphoreGive(this->start_lock_); this->running_state_ = RUNNING;
} }
break; break;
} }
case ESP_GATTS_STOP_EVT: { case ESP_GATTS_STOP_EVT: {
if (param->start.service_handle == this->handle_) { if (param->start.service_handle == this->handle_) {
xSemaphoreGive(this->stop_lock_); this->running_state_ = STOPPED;
} }
break; break;
} }

View File

@ -33,26 +33,44 @@ class BLEService {
BLEServer *get_server() { return this->server_; } BLEServer *get_server() { return this->server_; }
bool do_create(BLEServer *server); void do_create(BLEServer *server);
void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
void start(); void start();
void stop(); void stop();
protected: bool is_created();
bool errored_{false}; bool is_failed();
bool is_running() { return this->running_state_ == RUNNING; }
bool is_starting() { return this->running_state_ == STARTING; }
protected:
std::vector<BLECharacteristic *> characteristics_; std::vector<BLECharacteristic *> characteristics_;
BLECharacteristic *last_created_characteristic_{nullptr}; BLECharacteristic *last_created_characteristic_{nullptr};
uint32_t created_characteristic_count_{0};
BLEServer *server_; BLEServer *server_;
ESPBTUUID uuid_; ESPBTUUID uuid_;
uint16_t num_handles_; uint16_t num_handles_;
uint16_t handle_{0xFFFF}; uint16_t handle_{0xFFFF};
uint8_t inst_id_; uint8_t inst_id_;
SemaphoreHandle_t create_lock_; bool do_create_characteristics_();
SemaphoreHandle_t start_lock_;
SemaphoreHandle_t stop_lock_; enum InitState : uint8_t {
FAILED = 0x00,
INIT,
CREATING,
CREATING_DEPENDENTS,
CREATED,
} init_state_{INIT};
enum RunningState : uint8_t {
STARTING,
RUNNING,
STOPPING,
STOPPED,
} running_state_{STOPPED};
}; };
} // namespace esp32_ble } // namespace esp32_ble

View File

@ -53,7 +53,7 @@ template<class T> class Queue {
SemaphoreHandle_t m; SemaphoreHandle_t m;
}; };
// Received GAP and GATTC events are only queued, and get processed in the main loop(). // Received GAP, GATTC and GATTS events are only queued, and get processed in the main loop().
// This class stores each event in a single type. // This class stores each event in a single type.
class BLEEvent { class BLEEvent {
public: public:
@ -68,9 +68,18 @@ class BLEEvent {
this->event_.gattc.gattc_if = i; this->event_.gattc.gattc_if = i;
memcpy(&this->event_.gattc.gattc_param, p, sizeof(esp_ble_gattc_cb_param_t)); memcpy(&this->event_.gattc.gattc_param, p, sizeof(esp_ble_gattc_cb_param_t));
// Need to also make a copy of notify event data. // Need to also make a copy of notify event data.
if (e == ESP_GATTC_NOTIFY_EVT) { switch (e) {
memcpy(this->event_.gattc.notify_data, p->notify.value, p->notify.value_len); case ESP_GATTC_NOTIFY_EVT:
this->event_.gattc.gattc_param.notify.value = this->event_.gattc.notify_data; memcpy(this->event_.gattc.data, p->notify.value, p->notify.value_len);
this->event_.gattc.gattc_param.notify.value = this->event_.gattc.data;
break;
case ESP_GATTC_READ_CHAR_EVT:
case ESP_GATTC_READ_DESCR_EVT:
memcpy(this->event_.gattc.data, p->read.value, p->read.value_len);
this->event_.gattc.gattc_param.read.value = this->event_.gattc.data;
break;
default:
break;
} }
this->type_ = GATTC; this->type_ = GATTC;
}; };
@ -79,6 +88,15 @@ class BLEEvent {
this->event_.gatts.gatts_event = e; this->event_.gatts.gatts_event = e;
this->event_.gatts.gatts_if = i; this->event_.gatts.gatts_if = i;
memcpy(&this->event_.gatts.gatts_param, p, sizeof(esp_ble_gatts_cb_param_t)); memcpy(&this->event_.gatts.gatts_param, p, sizeof(esp_ble_gatts_cb_param_t));
// Need to also make a copy of write data.
switch (e) {
case ESP_GATTS_WRITE_EVT:
memcpy(this->event_.gatts.data, p->write.value, p->write.len);
this->event_.gatts.gatts_param.write.value = this->event_.gatts.data;
break;
default:
break;
}
this->type_ = GATTS; this->type_ = GATTS;
}; };
@ -92,13 +110,14 @@ class BLEEvent {
esp_gattc_cb_event_t gattc_event; esp_gattc_cb_event_t gattc_event;
esp_gatt_if_t gattc_if; esp_gatt_if_t gattc_if;
esp_ble_gattc_cb_param_t gattc_param; esp_ble_gattc_cb_param_t gattc_param;
uint8_t notify_data[64]; uint8_t data[64];
} gattc; } gattc;
struct gatts_event { struct gatts_event {
esp_gatts_cb_event_t gatts_event; esp_gatts_cb_event_t gatts_event;
esp_gatt_if_t gatts_if; esp_gatt_if_t gatts_if;
esp_ble_gatts_cb_param_t gatts_param; esp_ble_gatts_cb_param_t gatts_param;
uint8_t data[64];
} gatts; } gatts;
} event_; } event_;
enum ble_event_t : uint8_t { enum ble_event_t : uint8_t {

View File

@ -14,7 +14,9 @@ ESP32ImprovComponent::ESP32ImprovComponent() { global_improv_component = this; }
void ESP32ImprovComponent::setup_service() { void ESP32ImprovComponent::setup_service() {
this->service_ = esp32_ble::global_ble_server->create_service(improv::SERVICE_UUID, true); this->service_ = esp32_ble::global_ble_server->create_service(improv::SERVICE_UUID, true);
}
void ESP32ImprovComponent::setup_characteristics() {
this->status_ = this->service_->create_characteristic( this->status_ = this->service_->create_characteristic(
improv::STATUS_UUID, esp32_ble::BLECharacteristic::PROPERTY_READ | esp32_ble::BLECharacteristic::PROPERTY_NOTIFY); improv::STATUS_UUID, esp32_ble::BLECharacteristic::PROPERTY_READ | esp32_ble::BLECharacteristic::PROPERTY_NOTIFY);
esp32_ble::BLEDescriptor *status_descriptor = new esp32_ble::BLE2902(); esp32_ble::BLEDescriptor *status_descriptor = new esp32_ble::BLE2902();
@ -62,16 +64,21 @@ void ESP32ImprovComponent::loop() {
if (this->status_indicator_ != nullptr) if (this->status_indicator_ != nullptr)
this->status_indicator_->turn_off(); this->status_indicator_->turn_off();
if (this->should_start_ && this->setup_complete_) { if (this->service_->is_created() && !this->setup_complete_) {
ESP_LOGD(TAG, "Starting Improv service..."); this->setup_characteristics();
}
this->service_->start(); if (this->should_start_ && this->setup_complete_) {
if (this->service_->is_running()) {
this->service_->get_server()->get_advertising()->start(); this->service_->get_server()->get_advertising()->start();
this->set_state_(improv::STATE_AWAITING_AUTHORIZATION); this->set_state_(improv::STATE_AWAITING_AUTHORIZATION);
this->set_error_(improv::ERROR_NONE); this->set_error_(improv::ERROR_NONE);
this->should_start_ = false; this->should_start_ = false;
ESP_LOGD(TAG, "Service started!"); ESP_LOGD(TAG, "Service started!");
} else {
this->service_->start();
}
} }
break; break;
case improv::STATE_AWAITING_AUTHORIZATION: { case improv::STATE_AWAITING_AUTHORIZATION: {
@ -191,7 +198,7 @@ void ESP32ImprovComponent::start() {
this->should_start_ = true; this->should_start_ = true;
} }
void ESP32ImprovComponent::end() { void ESP32ImprovComponent::stop() {
this->set_timeout("end-service", 1000, [this] { this->set_timeout("end-service", 1000, [this] {
this->service_->stop(); this->service_->stop();
this->set_state_(improv::STATE_STOPPED); this->set_state_(improv::STATE_STOPPED);

View File

@ -21,11 +21,12 @@ class ESP32ImprovComponent : public Component, public esp32_ble::BLEServiceCompo
void dump_config() override; void dump_config() override;
void loop() override; void loop() override;
void setup_service() override; void setup_service() override;
void setup_characteristics();
void on_client_disconnect() override; void on_client_disconnect() override;
float get_setup_priority() const override; float get_setup_priority() const override;
void start(); void start() override;
void end(); void stop() override;
bool is_active() const { return this->state_ != improv::STATE_STOPPED; } bool is_active() const { return this->state_ != improv::STATE_STOPPED; }
void set_authorizer(binary_sensor::BinarySensor *authorizer) { this->authorizer_ = authorizer; } void set_authorizer(binary_sensor::BinarySensor *authorizer) { this->authorizer_ = authorizer; }

View File

@ -508,7 +508,7 @@ void WiFiComponent::check_connecting_finished() {
} }
#ifdef USE_IMPROV #ifdef USE_IMPROV
if (this->is_esp32_improv_active_()) { if (this->is_esp32_improv_active_()) {
esp32_improv::global_improv_component->end(); esp32_improv::global_improv_component->stop();
} }
#endif #endif