diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index 2200d3cef1..889c793017 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -20,14 +20,13 @@ void ESP32BLE::setup() { global_ble = this; ESP_LOGCONFIG(TAG, "Setting up BLE..."); - xTaskCreatePinnedToCore(ESP32BLE::ble_core_task_, - "ble_task", // name - 10000, // stack size - nullptr, // input params - 1, // priority - nullptr, // handle, not needed - 0 // core - ); + if (!ble_setup_()) { + ESP_LOGE(TAG, "BLE could not be set up"); + this->mark_failed(); + return; + } + + ESP_LOGD(TAG, "BLE setup complete"); } 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_() { esp_err_t err = nvs_flash_init(); if (err != ESP_OK) { @@ -84,7 +66,7 @@ bool ESP32BLE::ble_setup_() { return false; } - if (global_ble->has_server()) { + if (this->has_server()) { err = esp_ble_gatts_register_callback(ESP32BLE::gatts_event_handler); if (err != ESP_OK) { 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); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_ble_gattc_register_callback failed: %d", err); @@ -119,27 +101,28 @@ bool ESP32BLE::ble_setup_() { return true; } -// void ESP32BLE::loop() { -// BLEEvent *ble_event = this->ble_events_.pop(); -// while (ble_event != nullptr) { -// switch (ble_event->type_) { -// case ble_event->GATTS: -// this->real_gatts_event_handler_(ble_event->event_.gatts.gatts_event, ble_event->event_.gatts.gatts_if, -// &ble_event->event_.gatts.gatts_param); -// break; -// case ble_event->GAP: -// this->real_gap_event_handler_(ble_event->event_.gap.gap_event, &ble_event->event_.gap.gap_param); -// break; -// default: -// break; -// } -// delete ble_event; -// ble_event = this->ble_events_.pop(); -// } -// } +void ESP32BLE::loop() { + BLEEvent *ble_event = this->ble_events_.pop(); + while (ble_event != nullptr) { + switch (ble_event->type_) { + case ble_event->GATTS: + this->real_gatts_event_handler_(ble_event->event_.gatts.gatts_event, ble_event->event_.gatts.gatts_if, + &ble_event->event_.gatts.gatts_param); + break; + case ble_event->GAP: + this->real_gap_event_handler_(ble_event->event_.gap.gap_event, &ble_event->event_.gap.gap_param); + break; + default: + break; + } + delete ble_event; + 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) { - 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) { @@ -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, 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, diff --git a/esphome/components/esp32_ble/ble.h b/esphome/components/esp32_ble/ble.h index 8850f10d85..27cb9a2092 100644 --- a/esphome/components/esp32_ble/ble.h +++ b/esphome/components/esp32_ble/ble.h @@ -23,17 +23,14 @@ typedef struct { class ESP32BLE : public Component { public: void setup() override; - // void loop() override; + void loop() override; void dump_config() override; float get_setup_priority() const override; void mark_failed() override; - bool can_proceed() override; bool has_server() { return this->server_ != nullptr; } bool has_client() { return false; } - bool is_ready() { return this->ready_; } - void set_server(BLEServer *server) { this->server_ = server; } 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_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); - static void ble_core_task_(void *params); - static bool ble_setup_(); - - bool ready_{false}; + bool ble_setup_(); BLEServer *server_{nullptr}; Queue ble_events_; diff --git a/esphome/components/esp32_ble/ble_characteristic.cpp b/esphome/components/esp32_ble/ble_characteristic.cpp index 240b57eb38..127f973146 100644 --- a/esphome/components/esp32_ble/ble_characteristic.cpp +++ b/esphome/components/esp32_ble/ble_characteristic.cpp @@ -13,10 +13,8 @@ static const char *TAG = "esp32_ble.characteristic"; BLECharacteristic::BLECharacteristic(const ESPBTUUID uuid, uint32_t properties) : uuid_(uuid) { this->set_value_lock_ = xSemaphoreCreateBinary(); - this->create_lock_ = xSemaphoreCreateBinary(); - xSemaphoreGive(this->set_value_lock_); - xSemaphoreGive(this->create_lock_); + this->properties_ = (esp_gatt_char_prop_t) 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); } -bool BLECharacteristic::do_create(BLEService *service) { +void BLECharacteristic::do_create(BLEService *service) { this->service_ = service; esp_attr_control_t control; 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_bt_uuid_t uuid = this->uuid_.get_uuid(); @@ -114,15 +111,39 @@ bool BLECharacteristic::do_create(BLEService *service) { if (err != ESP_OK) { ESP_LOGE(TAG, "esp_ble_gatts_add_char failed: %d", err); + return; + } + + this->state_ = CREATING; +} + +bool BLECharacteristic::is_created() { + if (this->state_ == CREATED) + return true; + + if (this->state_ != CREATING_DEPENDENTS) return false; - } - - xSemaphoreWait(this->create_lock_, portMAX_DELAY); + bool created = true; for (auto *descriptor : this->descriptors_) { - descriptor->do_create(this); + created &= descriptor->is_created(); } - return true; + 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) { @@ -168,7 +189,12 @@ void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt case ESP_GATTS_ADD_CHAR_EVT: { if (this->uuid_ == ESPBTUUID::from_uuid(param->add_char.char_uuid)) { 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; } diff --git a/esphome/components/esp32_ble/ble_characteristic.h b/esphome/components/esp32_ble/ble_characteristic.h index 076f5c1a5f..648cfb939d 100644 --- a/esphome/components/esp32_ble/ble_characteristic.h +++ b/esphome/components/esp32_ble/ble_characteristic.h @@ -42,10 +42,10 @@ class BLECharacteristic { 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 on_write(const std::function &)> &func) { this->on_write_ = func; } + void on_write(const std::function &)> &&func) { this->on_write_ = std::move(func); } void add_descriptor(BLEDescriptor *descriptor); @@ -60,6 +60,9 @@ class BLECharacteristic { static const uint32_t PROPERTY_INDICATE = 1 << 4; static const uint32_t PROPERTY_WRITE_NR = 1 << 5; + bool is_created(); + bool is_failed(); + protected: bool write_event_{false}; BLEService *service_; @@ -70,13 +73,20 @@ class BLECharacteristic { uint16_t value_read_offset_{0}; std::vector value_; SemaphoreHandle_t set_value_lock_; - SemaphoreHandle_t create_lock_; std::vector descriptors_; std::function &)> on_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 diff --git a/esphome/components/esp32_ble/ble_descriptor.cpp b/esphome/components/esp32_ble/ble_descriptor.cpp index 4c4e3968e8..9738cc2fe7 100644 --- a/esphome/components/esp32_ble/ble_descriptor.cpp +++ b/esphome/components/esp32_ble/ble_descriptor.cpp @@ -16,30 +16,25 @@ BLEDescriptor::BLEDescriptor(ESPBTUUID uuid, uint16_t max_len) { this->value_.attr_len = 0; this->value_.attr_max_len = 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); } -bool BLEDescriptor::do_create(BLECharacteristic *characteristic) { +void BLEDescriptor::do_create(BLECharacteristic *characteristic) { this->characteristic_ = characteristic; esp_attr_control_t control; 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_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, this->permissions_, &this->value_, &control); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_ble_gatts_add_char_descr failed: %d", err); - return false; + this->state_ = FAILED; + return; } - xSemaphoreWait(this->create_lock_, portMAX_DELAY); - - return true; + this->state_ = CREATING; } 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_ == this->characteristic_->get_service()->get_last_created_characteristic()) { this->handle_ = param->add_char_descr.attr_handle; - xSemaphoreGive(this->create_lock_); + this->state_ = CREATED; } break; } diff --git a/esphome/components/esp32_ble/ble_descriptor.h b/esphome/components/esp32_ble/ble_descriptor.h index ffbb9645bd..bc7b48e331 100644 --- a/esphome/components/esp32_ble/ble_descriptor.h +++ b/esphome/components/esp32_ble/ble_descriptor.h @@ -16,22 +16,31 @@ class BLEDescriptor { public: BLEDescriptor(ESPBTUUID uuid, uint16_t max_len = 100); virtual ~BLEDescriptor(); - bool do_create(BLECharacteristic *characteristic); + void do_create(BLECharacteristic *characteristic); void set_value(const std::string &value); 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); + bool is_created() { return this->state_ == CREATED; } + bool is_failed() { return this->state_ == FAILED; } + protected: BLECharacteristic *characteristic_{nullptr}; ESPBTUUID uuid_; uint16_t handle_{0xFFFF}; - SemaphoreHandle_t create_lock_; esp_attr_value_t value_; 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 diff --git a/esphome/components/esp32_ble/ble_server.cpp b/esphome/components/esp32_ble/ble_server.cpp index a63312dd62..9d5be46113 100644 --- a/esphome/components/esp32_ble/ble_server.cpp +++ b/esphome/components/esp32_ble/ble_server.cpp @@ -18,7 +18,7 @@ namespace esp32_ble { 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 VERSION_UUID = 0x2A26; static const uint16_t MANUFACTURER_UUID = 0x2A29; @@ -32,59 +32,78 @@ void BLEServer::setup() { ESP_LOGD(TAG, "Setting up BLE Server..."); global_ble_server = this; - this->register_lock_ = xSemaphoreCreateBinary(); - xSemaphoreGive(this->register_lock_); + 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_() { - xSemaphoreTake(this->register_lock_, portMAX_DELAY); - esp_err_t err = esp_ble_gatts_app_register(0); - if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_ble_gatts_app_register failed: %d", err); - this->mark_failed(); - return; +void BLEServer::loop() { + switch (this->state_) { + case RUNNING: + return; + + case INIT: { + esp_err_t err = esp_ble_gatts_app_register(0); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_ble_gatts_app_register failed: %d", err); + this->mark_failed(); + return; + } + this->state_ = REGISTERING; + break; + } + case REGISTERING: { + if (this->registered_) { + this->device_information_service_ = this->create_service(device_information_service__UUID); + + this->create_device_characteristics_(); + + this->advertising_->set_scan_response(true); + this->advertising_->set_min_preferred_interval(0x06); + this->advertising_->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; + } } - xSemaphoreWait(this->register_lock_, portMAX_DELAY); - - this->device_information_service = this->create_service(DEVICE_INFORMATION_SERVICE_UUID); - - this->create_device_characteristics_(); - - this->advertising_->set_scan_response(true); - this->advertising_->set_min_preferred_interval(0x06); - this->advertising_->start(); - - this->device_information_service->start(); } bool BLEServer::create_device_characteristics_() { if (this->model_.has_value()) { 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()); } else { #ifdef ARDUINO_BOARD 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); #endif } 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); 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_); 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: { this->gatts_if_ = gatts_if; - xSemaphoreGive(this->register_lock_); + this->registered_ = true; break; } default: diff --git a/esphome/components/esp32_ble/ble_server.h b/esphome/components/esp32_ble/ble_server.h index 0713816ad3..5657773446 100644 --- a/esphome/components/esp32_ble/ble_server.h +++ b/esphome/components/esp32_ble/ble_server.h @@ -24,13 +24,17 @@ class BLEServiceComponent { virtual void setup_service(); virtual void on_client_connect(){}; virtual void on_client_disconnect(){}; + virtual void start(); + virtual void stop(); }; class BLEServer : public Component { public: void setup() override; + void loop() override; void dump_config() override; float get_setup_priority() const override; + bool can_proceed() override { return this->can_proceed_; } void teardown(); @@ -53,27 +57,35 @@ class BLEServer : public Component { protected: bool create_device_characteristics_(); - void setup_server_(); void add_client_(uint16_t conn_id, void *client) { this->clients_.insert(std::pair(conn_id, client)); } bool remove_client_(uint16_t conn_id) { return this->clients_.erase(conn_id) > 0; } + bool can_proceed_{false}; + std::string manufacturer_; optional model_; esp_gatt_if_t gatts_if_{0}; + bool registered_{false}; BLEAdvertising *advertising_; uint32_t connected_clients_{0}; std::map clients_; std::vector services_; - BLEService *device_information_service; + BLEService *device_information_service_; std::vector 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; diff --git a/esphome/components/esp32_ble/ble_service.cpp b/esphome/components/esp32_ble/ble_service.cpp index af8fe00a1d..4b85182696 100644 --- a/esphome/components/esp32_ble/ble_service.cpp +++ b/esphome/components/esp32_ble/ble_service.cpp @@ -10,15 +10,7 @@ namespace esp32_ble { static const char *TAG = "esp32_ble.service"; BLEService::BLEService(ESPBTUUID uuid, uint16_t num_handles, uint8_t 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_); -} + : uuid_(uuid), num_handles_(num_handles), inst_id_(inst_id) {} BLEService::~BLEService() { for (auto &chr : this->characteristics_) @@ -47,10 +39,9 @@ BLECharacteristic *BLEService::create_characteristic(ESPBTUUID uuid, esp_gatt_ch return characteristic; } -bool BLEService::do_create(BLEServer *server) { +void BLEService::do_create(BLEServer *server) { this->server_ = server; - xSemaphoreTake(this->create_lock_, portMAX_DELAY); esp_gatt_srvc_id_t srvc_id; srvc_id.is_primary = true; 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_); if (err != ESP_OK) { 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; } void BLEService::start() { - for (auto *characteristic : this->characteristics_) { - this->last_created_characteristic_ = characteristic; - characteristic->do_create(this); - } + if (this->do_create_characteristics_()) + return; - xSemaphoreTake(this->start_lock_, portMAX_DELAY); esp_err_t err = esp_ble_gatts_start_service(this->handle_); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_ble_gatts_start_service failed: %d", err); return; } - xSemaphoreWait(this->start_lock_, portMAX_DELAY); + this->running_state_ = STARTING; } void BLEService::stop() { - xSemaphoreTake(this->stop_lock_, portMAX_DELAY); esp_err_t err = esp_ble_gatts_stop_service(this->handle_); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_ble_gatts_stop_service failed: %d", err); 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, @@ -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) && this->inst_id_ == param->create.service_id.id.inst_id) { this->handle_ = param->create.service_handle; - xSemaphoreGive(this->create_lock_); + this->init_state_ = CREATED; } break; } case ESP_GATTS_START_EVT: { if (param->start.service_handle == this->handle_) { - xSemaphoreGive(this->start_lock_); + this->running_state_ = RUNNING; } break; } case ESP_GATTS_STOP_EVT: { if (param->start.service_handle == this->handle_) { - xSemaphoreGive(this->stop_lock_); + this->running_state_ = STOPPED; } break; } diff --git a/esphome/components/esp32_ble/ble_service.h b/esphome/components/esp32_ble/ble_service.h index 9a88d4e1e7..a539631846 100644 --- a/esphome/components/esp32_ble/ble_service.h +++ b/esphome/components/esp32_ble/ble_service.h @@ -33,26 +33,44 @@ class BLEService { 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 start(); void stop(); - protected: - bool errored_{false}; + bool is_created(); + bool is_failed(); + bool is_running() { return this->running_state_ == RUNNING; } + bool is_starting() { return this->running_state_ == STARTING; } + + protected: std::vector characteristics_; BLECharacteristic *last_created_characteristic_{nullptr}; + uint32_t created_characteristic_count_{0}; BLEServer *server_; ESPBTUUID uuid_; uint16_t num_handles_; uint16_t handle_{0xFFFF}; uint8_t inst_id_; - SemaphoreHandle_t create_lock_; - SemaphoreHandle_t start_lock_; - SemaphoreHandle_t stop_lock_; + bool do_create_characteristics_(); + + 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 diff --git a/esphome/components/esp32_ble/queue.h b/esphome/components/esp32_ble/queue.h index f5c861a917..3c87a90f22 100644 --- a/esphome/components/esp32_ble/queue.h +++ b/esphome/components/esp32_ble/queue.h @@ -53,7 +53,7 @@ template class Queue { 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. class BLEEvent { public: @@ -68,9 +68,18 @@ class BLEEvent { this->event_.gattc.gattc_if = i; memcpy(&this->event_.gattc.gattc_param, p, sizeof(esp_ble_gattc_cb_param_t)); // Need to also make a copy of notify event data. - if (e == ESP_GATTC_NOTIFY_EVT) { - memcpy(this->event_.gattc.notify_data, p->notify.value, p->notify.value_len); - this->event_.gattc.gattc_param.notify.value = this->event_.gattc.notify_data; + switch (e) { + case ESP_GATTC_NOTIFY_EVT: + 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; }; @@ -79,6 +88,15 @@ class BLEEvent { this->event_.gatts.gatts_event = e; this->event_.gatts.gatts_if = i; 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; }; @@ -92,13 +110,14 @@ class BLEEvent { esp_gattc_cb_event_t gattc_event; esp_gatt_if_t gattc_if; esp_ble_gattc_cb_param_t gattc_param; - uint8_t notify_data[64]; + uint8_t data[64]; } gattc; struct gatts_event { esp_gatts_cb_event_t gatts_event; esp_gatt_if_t gatts_if; esp_ble_gatts_cb_param_t gatts_param; + uint8_t data[64]; } gatts; } event_; enum ble_event_t : uint8_t { diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index a407d819b7..491f26f46e 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -14,7 +14,9 @@ ESP32ImprovComponent::ESP32ImprovComponent() { global_improv_component = this; } void ESP32ImprovComponent::setup_service() { this->service_ = esp32_ble::global_ble_server->create_service(improv::SERVICE_UUID, true); +} +void ESP32ImprovComponent::setup_characteristics() { this->status_ = this->service_->create_characteristic( improv::STATUS_UUID, esp32_ble::BLECharacteristic::PROPERTY_READ | esp32_ble::BLECharacteristic::PROPERTY_NOTIFY); esp32_ble::BLEDescriptor *status_descriptor = new esp32_ble::BLE2902(); @@ -62,16 +64,21 @@ void ESP32ImprovComponent::loop() { if (this->status_indicator_ != nullptr) this->status_indicator_->turn_off(); + if (this->service_->is_created() && !this->setup_complete_) { + this->setup_characteristics(); + } + if (this->should_start_ && this->setup_complete_) { - ESP_LOGD(TAG, "Starting Improv service..."); + if (this->service_->is_running()) { + this->service_->get_server()->get_advertising()->start(); - this->service_->start(); - this->service_->get_server()->get_advertising()->start(); - - this->set_state_(improv::STATE_AWAITING_AUTHORIZATION); - this->set_error_(improv::ERROR_NONE); - this->should_start_ = false; - ESP_LOGD(TAG, "Service started!"); + this->set_state_(improv::STATE_AWAITING_AUTHORIZATION); + this->set_error_(improv::ERROR_NONE); + this->should_start_ = false; + ESP_LOGD(TAG, "Service started!"); + } else { + this->service_->start(); + } } break; case improv::STATE_AWAITING_AUTHORIZATION: { @@ -191,7 +198,7 @@ void ESP32ImprovComponent::start() { this->should_start_ = true; } -void ESP32ImprovComponent::end() { +void ESP32ImprovComponent::stop() { this->set_timeout("end-service", 1000, [this] { this->service_->stop(); this->set_state_(improv::STATE_STOPPED); diff --git a/esphome/components/esp32_improv/esp32_improv_component.h b/esphome/components/esp32_improv/esp32_improv_component.h index 498a443ec3..e9b0c72ecd 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.h +++ b/esphome/components/esp32_improv/esp32_improv_component.h @@ -21,11 +21,12 @@ class ESP32ImprovComponent : public Component, public esp32_ble::BLEServiceCompo void dump_config() override; void loop() override; void setup_service() override; + void setup_characteristics(); void on_client_disconnect() override; float get_setup_priority() const override; - void start(); - void end(); + void start() override; + void stop() override; bool is_active() const { return this->state_ != improv::STATE_STOPPED; } void set_authorizer(binary_sensor::BinarySensor *authorizer) { this->authorizer_ = authorizer; } diff --git a/esphome/components/light/light_state.cpp b/esphome/components/light/light_state.cpp index 4e450f6ccb..749dea5419 100644 --- a/esphome/components/light/light_state.cpp +++ b/esphome/components/light/light_state.cpp @@ -407,7 +407,7 @@ LightColorValues LightCall::validate_() { this->color_temperature_.reset(); } - // sets RGB to 100% if only White specified + // If white channel is specified, set RGB to white color (when interlock is enabled) if (this->white_.has_value()) { if (traits.get_supports_color_interlock()) { if (!this->red_.has_value() && !this->green_.has_value() && !this->blue_.has_value()) { @@ -415,7 +415,7 @@ LightColorValues LightCall::validate_() { this->green_ = optional(1.0f); this->blue_ = optional(1.0f); } - // make white values binary aka 0.0f or 1.0f...this allows brightness to do its job + // make white values binary aka 0.0f or 1.0f... this allows brightness to do its job if (*this->white_ > 0.0f) { this->white_ = optional(1.0f); } else { @@ -423,44 +423,27 @@ LightColorValues LightCall::validate_() { } } } - // White to 0% if (exclusively) setting any RGB value that isn't 255,255,255 + // If only a color channel is specified, set white channel to 100% for white, otherwise 0% (when interlock is enabled) else if (this->red_.has_value() || this->green_.has_value() || this->blue_.has_value()) { if (traits.get_supports_color_interlock()) { - if (*this->red_ == 1.0f && *this->green_ == 1.0f && *this->blue_ == 1.0f && - traits.get_supports_rgb_white_value() && traits.get_supports_color_interlock()) { + if (*this->red_ == 1.0f && *this->green_ == 1.0f && *this->blue_ == 1.0f) { this->white_ = optional(1.0f); - } else if (!this->white_.has_value() || !traits.get_supports_rgb_white_value()) { + } else { this->white_ = optional(0.0f); } } } - // if changing Kelvin alone, change to white light + // If only a color temperature is specified, change to white light else if (this->color_temperature_.has_value()) { - if (!traits.get_supports_color_interlock()) { - if (!this->red_.has_value() && !this->green_.has_value() && !this->blue_.has_value()) { - this->red_ = optional(1.0f); - this->green_ = optional(1.0f); - this->blue_ = optional(1.0f); - } - } - // if setting Kelvin from color (i.e. switching to white light), set White to 100% + this->red_ = optional(1.0f); + this->green_ = optional(1.0f); + this->blue_ = optional(1.0f); + + // if setting color temperature from color (i.e. switching to white light), set White to 100% auto cv = this->parent_->remote_values; bool was_color = cv.get_red() != 1.0f || cv.get_blue() != 1.0f || cv.get_green() != 1.0f; - bool now_white = *this->red_ == 1.0f && *this->blue_ == 1.0f && *this->green_ == 1.0f; - if (traits.get_supports_color_interlock()) { - if (cv.get_white() < 1.0f) { - this->white_ = optional(1.0f); - } - - if (was_color && !this->red_.has_value() && !this->green_.has_value() && !this->blue_.has_value()) { - this->red_ = optional(1.0f); - this->green_ = optional(1.0f); - this->blue_ = optional(1.0f); - } - } else { - if (!this->white_.has_value() && was_color && now_white) { - this->white_ = optional(1.0f); - } + if (traits.get_supports_color_interlock() || was_color) { + this->white_ = optional(1.0f); } } diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index 42509f39f1..3e1132bb1d 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -46,6 +46,7 @@ WaveshareEPaperTypeBModel = waveshare_epaper_ns.enum("WaveshareEPaperTypeBModel" MODELS = { "1.54in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_1_54_IN), + "1.54inv2": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_1_54_IN_V2), "2.13in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_13_IN), "2.13in-ttgo": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN), "2.13in-ttgo-b1": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN_B1), @@ -67,7 +68,7 @@ def validate_full_update_every_only_type_a(value): if MODELS[value[CONF_MODEL]][0] != "a": raise cv.Invalid( "The 'full_update_every' option is only available for models " - "'1.54in', '2.13in', '2.90in', and '2.90inV2'." + "'1.54in', '1.54inV2', '2.13in', '2.90in', and '2.90inV2'." ) return value diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index 0fb783107d..daaa77b7fa 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -210,6 +210,9 @@ void WaveshareEPaperTypeA::dump_config() { case WAVESHARE_EPAPER_1_54_IN: ESP_LOGCONFIG(TAG, " Model: 1.54in"); break; + case WAVESHARE_EPAPER_1_54_IN_V2: + ESP_LOGCONFIG(TAG, " Model: 1.54inV2"); + break; case WAVESHARE_EPAPER_2_13_IN: ESP_LOGCONFIG(TAG, " Model: 2.13in"); break; @@ -334,7 +337,7 @@ void HOT WaveshareEPaperTypeA::display() { // COMMAND DISPLAY UPDATE CONTROL 2 this->command(0x22); - if (this->model_ == WAVESHARE_EPAPER_2_9_IN_V2) { + if (this->model_ == WAVESHARE_EPAPER_2_9_IN_V2 || this->model_ == WAVESHARE_EPAPER_1_54_IN_V2) { this->data(full_update ? 0xF7 : 0xFF); } else if (this->model_ == TTGO_EPAPER_2_13_IN_B73) { this->data(0xC7); @@ -353,6 +356,8 @@ int WaveshareEPaperTypeA::get_width_internal() { switch (this->model_) { case WAVESHARE_EPAPER_1_54_IN: return 200; + case WAVESHARE_EPAPER_1_54_IN_V2: + return 200; case WAVESHARE_EPAPER_2_13_IN: return 128; case TTGO_EPAPER_2_13_IN: @@ -372,6 +377,8 @@ int WaveshareEPaperTypeA::get_height_internal() { switch (this->model_) { case WAVESHARE_EPAPER_1_54_IN: return 200; + case WAVESHARE_EPAPER_1_54_IN_V2: + return 200; case WAVESHARE_EPAPER_2_13_IN: return 250; case TTGO_EPAPER_2_13_IN: diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index 0b8958e7f0..8ab77d653b 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -66,6 +66,7 @@ class WaveshareEPaper : public PollingComponent, enum WaveshareEPaperTypeAModel { WAVESHARE_EPAPER_1_54_IN = 0, + WAVESHARE_EPAPER_1_54_IN_V2, WAVESHARE_EPAPER_2_13_IN, WAVESHARE_EPAPER_2_9_IN, WAVESHARE_EPAPER_2_9_IN_V2, @@ -85,7 +86,7 @@ class WaveshareEPaperTypeA : public WaveshareEPaper { void display() override; void deep_sleep() override { - if (this->model_ == WAVESHARE_EPAPER_2_9_IN_V2) { + if (this->model_ == WAVESHARE_EPAPER_2_9_IN_V2 || this->model_ == WAVESHARE_EPAPER_1_54_IN_V2) { // COMMAND DEEP SLEEP MODE this->command(0x10); this->data(0x01); diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index da1f6f85a7..de7ffb4f09 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -508,7 +508,7 @@ void WiFiComponent::check_connecting_finished() { } #ifdef USE_IMPROV if (this->is_esp32_improv_active_()) { - esp32_improv::global_improv_component->end(); + esp32_improv::global_improv_component->stop(); } #endif diff --git a/esphome/const.py b/esphome/const.py index 159a74adc2..30df9abe17 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -2,7 +2,7 @@ MAJOR_VERSION = 1 MINOR_VERSION = 19 -PATCH_VERSION = "0b1" +PATCH_VERSION = "0b2" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}"