mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Inherit esp32_ble_beacon from esp32_ble (#6908)
				
					
				
			Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							172a358d01
						
					
				
				
					commit
					5d5f3276e9
				
			| @@ -7,10 +7,10 @@ from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant | |||||||
|  |  | ||||||
| DEPENDENCIES = ["esp32"] | DEPENDENCIES = ["esp32"] | ||||||
| CODEOWNERS = ["@jesserockz", "@Rapsssito"] | CODEOWNERS = ["@jesserockz", "@Rapsssito"] | ||||||
| CONFLICTS_WITH = ["esp32_ble_beacon"] |  | ||||||
|  |  | ||||||
| CONF_BLE_ID = "ble_id" | CONF_BLE_ID = "ble_id" | ||||||
| CONF_IO_CAPABILITY = "io_capability" | CONF_IO_CAPABILITY = "io_capability" | ||||||
|  | CONF_ADVERTISING_CYCLE_TIME = "advertising_cycle_time" | ||||||
|  |  | ||||||
| NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2] | NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2] | ||||||
|  |  | ||||||
| @@ -34,6 +34,19 @@ IO_CAPABILITY = { | |||||||
|     "display_yes_no": IoCapability.IO_CAP_IO, |     "display_yes_no": IoCapability.IO_CAP_IO, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | esp_power_level_t = cg.global_ns.enum("esp_power_level_t") | ||||||
|  |  | ||||||
|  | TX_POWER_LEVELS = { | ||||||
|  |     -12: esp_power_level_t.ESP_PWR_LVL_N12, | ||||||
|  |     -9: esp_power_level_t.ESP_PWR_LVL_N9, | ||||||
|  |     -6: esp_power_level_t.ESP_PWR_LVL_N6, | ||||||
|  |     -3: esp_power_level_t.ESP_PWR_LVL_N3, | ||||||
|  |     0: esp_power_level_t.ESP_PWR_LVL_N0, | ||||||
|  |     3: esp_power_level_t.ESP_PWR_LVL_P3, | ||||||
|  |     6: esp_power_level_t.ESP_PWR_LVL_P6, | ||||||
|  |     9: esp_power_level_t.ESP_PWR_LVL_P9, | ||||||
|  | } | ||||||
|  |  | ||||||
| CONFIG_SCHEMA = cv.Schema( | CONFIG_SCHEMA = cv.Schema( | ||||||
|     { |     { | ||||||
|         cv.GenerateID(): cv.declare_id(ESP32BLE), |         cv.GenerateID(): cv.declare_id(ESP32BLE), | ||||||
| @@ -41,6 +54,9 @@ CONFIG_SCHEMA = cv.Schema( | |||||||
|             IO_CAPABILITY, lower=True |             IO_CAPABILITY, lower=True | ||||||
|         ), |         ), | ||||||
|         cv.Optional(CONF_ENABLE_ON_BOOT, default=True): cv.boolean, |         cv.Optional(CONF_ENABLE_ON_BOOT, default=True): cv.boolean, | ||||||
|  |         cv.Optional( | ||||||
|  |             CONF_ADVERTISING_CYCLE_TIME, default="10s" | ||||||
|  |         ): cv.positive_time_period_milliseconds, | ||||||
|     } |     } | ||||||
| ).extend(cv.COMPONENT_SCHEMA) | ).extend(cv.COMPONENT_SCHEMA) | ||||||
|  |  | ||||||
| @@ -58,6 +74,7 @@ async def to_code(config): | |||||||
|     var = cg.new_Pvariable(config[CONF_ID]) |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|     cg.add(var.set_enable_on_boot(config[CONF_ENABLE_ON_BOOT])) |     cg.add(var.set_enable_on_boot(config[CONF_ENABLE_ON_BOOT])) | ||||||
|     cg.add(var.set_io_capability(config[CONF_IO_CAPABILITY])) |     cg.add(var.set_io_capability(config[CONF_IO_CAPABILITY])) | ||||||
|  |     cg.add(var.set_advertising_cycle_time(config[CONF_ADVERTISING_CYCLE_TIME])) | ||||||
|     await cg.register_component(var, config) |     await cg.register_component(var, config) | ||||||
|  |  | ||||||
|     if CORE.using_esp_idf: |     if CORE.using_esp_idf: | ||||||
|   | |||||||
| @@ -78,6 +78,11 @@ void ESP32BLE::advertising_set_manufacturer_data(const std::vector<uint8_t> &dat | |||||||
|   this->advertising_start(); |   this->advertising_start(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void ESP32BLE::advertising_register_raw_advertisement_callback(std::function<void(bool)> &&callback) { | ||||||
|  |   this->advertising_init_(); | ||||||
|  |   this->advertising_->register_raw_advertisement_callback(std::move(callback)); | ||||||
|  | } | ||||||
|  |  | ||||||
| void ESP32BLE::advertising_add_service_uuid(ESPBTUUID uuid) { | void ESP32BLE::advertising_add_service_uuid(ESPBTUUID uuid) { | ||||||
|   this->advertising_init_(); |   this->advertising_init_(); | ||||||
|   this->advertising_->add_service_uuid(uuid); |   this->advertising_->add_service_uuid(uuid); | ||||||
| @@ -102,7 +107,7 @@ bool ESP32BLE::ble_pre_setup_() { | |||||||
| void ESP32BLE::advertising_init_() { | void ESP32BLE::advertising_init_() { | ||||||
|   if (this->advertising_ != nullptr) |   if (this->advertising_ != nullptr) | ||||||
|     return; |     return; | ||||||
|   this->advertising_ = new BLEAdvertising();  // NOLINT(cppcoreguidelines-owning-memory) |   this->advertising_ = new BLEAdvertising(this->advertising_cycle_time_);  // NOLINT(cppcoreguidelines-owning-memory) | ||||||
|  |  | ||||||
|   this->advertising_->set_scan_response(true); |   this->advertising_->set_scan_response(true); | ||||||
|   this->advertising_->set_min_preferred_interval(0x06); |   this->advertising_->set_min_preferred_interval(0x06); | ||||||
| @@ -312,6 +317,9 @@ void ESP32BLE::loop() { | |||||||
|     delete ble_event;  // NOLINT(cppcoreguidelines-owning-memory) |     delete ble_event;  // NOLINT(cppcoreguidelines-owning-memory) | ||||||
|     ble_event = this->ble_events_.pop(); |     ble_event = this->ble_events_.pop(); | ||||||
|   } |   } | ||||||
|  |   if (this->advertising_ != nullptr) { | ||||||
|  |     this->advertising_->loop(); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| 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) { | ||||||
|   | |||||||
| @@ -3,6 +3,8 @@ | |||||||
| #include "ble_advertising.h" | #include "ble_advertising.h" | ||||||
| #include "ble_uuid.h" | #include "ble_uuid.h" | ||||||
|  |  | ||||||
|  | #include <functional> | ||||||
|  |  | ||||||
| #include "esphome/core/automation.h" | #include "esphome/core/automation.h" | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
| #include "esphome/core/defines.h" | #include "esphome/core/defines.h" | ||||||
| @@ -76,6 +78,11 @@ class ESP32BLE : public Component { | |||||||
|  public: |  public: | ||||||
|   void set_io_capability(IoCapability io_capability) { this->io_cap_ = (esp_ble_io_cap_t) io_capability; } |   void set_io_capability(IoCapability io_capability) { this->io_cap_ = (esp_ble_io_cap_t) io_capability; } | ||||||
|  |  | ||||||
|  |   void set_advertising_cycle_time(uint32_t advertising_cycle_time) { | ||||||
|  |     this->advertising_cycle_time_ = advertising_cycle_time; | ||||||
|  |   } | ||||||
|  |   uint32_t get_advertising_cycle_time() const { return this->advertising_cycle_time_; } | ||||||
|  |  | ||||||
|   void enable(); |   void enable(); | ||||||
|   void disable(); |   void disable(); | ||||||
|   bool is_active(); |   bool is_active(); | ||||||
| @@ -89,6 +96,7 @@ class ESP32BLE : public Component { | |||||||
|   void advertising_set_manufacturer_data(const std::vector<uint8_t> &data); |   void advertising_set_manufacturer_data(const std::vector<uint8_t> &data); | ||||||
|   void advertising_add_service_uuid(ESPBTUUID uuid); |   void advertising_add_service_uuid(ESPBTUUID uuid); | ||||||
|   void advertising_remove_service_uuid(ESPBTUUID uuid); |   void advertising_remove_service_uuid(ESPBTUUID uuid); | ||||||
|  |   void advertising_register_raw_advertisement_callback(std::function<void(bool)> &&callback); | ||||||
|  |  | ||||||
|   void register_gap_event_handler(GAPEventHandler *handler) { this->gap_event_handlers_.push_back(handler); } |   void register_gap_event_handler(GAPEventHandler *handler) { this->gap_event_handlers_.push_back(handler); } | ||||||
|   void register_gattc_event_handler(GATTcEventHandler *handler) { this->gattc_event_handlers_.push_back(handler); } |   void register_gattc_event_handler(GATTcEventHandler *handler) { this->gattc_event_handlers_.push_back(handler); } | ||||||
| @@ -121,6 +129,7 @@ class ESP32BLE : public Component { | |||||||
|   Queue<BLEEvent> ble_events_; |   Queue<BLEEvent> ble_events_; | ||||||
|   BLEAdvertising *advertising_; |   BLEAdvertising *advertising_; | ||||||
|   esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE}; |   esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE}; | ||||||
|  |   uint32_t advertising_cycle_time_; | ||||||
|   bool enable_on_boot_; |   bool enable_on_boot_; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,9 +10,9 @@ | |||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace esp32_ble { | namespace esp32_ble { | ||||||
|  |  | ||||||
| static const char *const TAG = "esp32_ble"; | static const char *const TAG = "esp32_ble.advertising"; | ||||||
|  |  | ||||||
| BLEAdvertising::BLEAdvertising() { | BLEAdvertising::BLEAdvertising(uint32_t advertising_cycle_time) : advertising_cycle_time_(advertising_cycle_time) { | ||||||
|   this->advertising_data_.set_scan_rsp = false; |   this->advertising_data_.set_scan_rsp = false; | ||||||
|   this->advertising_data_.include_name = true; |   this->advertising_data_.include_name = true; | ||||||
|   this->advertising_data_.include_txpower = true; |   this->advertising_data_.include_txpower = true; | ||||||
| @@ -64,7 +64,7 @@ void BLEAdvertising::set_manufacturer_data(const std::vector<uint8_t> &data) { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| void BLEAdvertising::start() { | esp_err_t BLEAdvertising::services_advertisement_() { | ||||||
|   int num_services = this->advertising_uuids_.size(); |   int num_services = this->advertising_uuids_.size(); | ||||||
|   if (num_services == 0) { |   if (num_services == 0) { | ||||||
|     this->advertising_data_.service_uuid_len = 0; |     this->advertising_data_.service_uuid_len = 0; | ||||||
| @@ -87,8 +87,8 @@ void BLEAdvertising::start() { | |||||||
|   this->advertising_data_.include_txpower = !this->scan_response_; |   this->advertising_data_.include_txpower = !this->scan_response_; | ||||||
|   err = esp_ble_gap_config_adv_data(&this->advertising_data_); |   err = esp_ble_gap_config_adv_data(&this->advertising_data_); | ||||||
|   if (err != ESP_OK) { |   if (err != ESP_OK) { | ||||||
|     ESP_LOGE(TAG, "esp_ble_gap_config_adv_data failed (Advertising): %d", err); |     ESP_LOGE(TAG, "esp_ble_gap_config_adv_data failed (Advertising): %s", esp_err_to_name(err)); | ||||||
|     return; |     return err; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (this->scan_response_) { |   if (this->scan_response_) { | ||||||
| @@ -101,8 +101,8 @@ void BLEAdvertising::start() { | |||||||
|     this->scan_response_data_.flag = 0; |     this->scan_response_data_.flag = 0; | ||||||
|     err = esp_ble_gap_config_adv_data(&this->scan_response_data_); |     err = esp_ble_gap_config_adv_data(&this->scan_response_data_); | ||||||
|     if (err != ESP_OK) { |     if (err != ESP_OK) { | ||||||
|       ESP_LOGE(TAG, "esp_ble_gap_config_adv_data failed (Scan response): %d", err); |       ESP_LOGE(TAG, "esp_ble_gap_config_adv_data failed (Scan response): %s", esp_err_to_name(err)); | ||||||
|       return; |       return err; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -113,8 +113,18 @@ void BLEAdvertising::start() { | |||||||
|  |  | ||||||
|   err = esp_ble_gap_start_advertising(&this->advertising_params_); |   err = esp_ble_gap_start_advertising(&this->advertising_params_); | ||||||
|   if (err != ESP_OK) { |   if (err != ESP_OK) { | ||||||
|     ESP_LOGE(TAG, "esp_ble_gap_start_advertising failed: %d", err); |     ESP_LOGE(TAG, "esp_ble_gap_start_advertising failed: %s", esp_err_to_name(err)); | ||||||
|     return; |     return err; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return ESP_OK; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void BLEAdvertising::start() { | ||||||
|  |   if (this->current_adv_index_ == -1) { | ||||||
|  |     this->services_advertisement_(); | ||||||
|  |   } else { | ||||||
|  |     this->raw_advertisements_callbacks_[this->current_adv_index_](true); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -124,6 +134,29 @@ void BLEAdvertising::stop() { | |||||||
|     ESP_LOGE(TAG, "esp_ble_gap_stop_advertising failed: %d", err); |     ESP_LOGE(TAG, "esp_ble_gap_stop_advertising failed: %d", err); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |   if (this->current_adv_index_ != -1) { | ||||||
|  |     this->raw_advertisements_callbacks_[this->current_adv_index_](false); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void BLEAdvertising::loop() { | ||||||
|  |   if (this->raw_advertisements_callbacks_.empty()) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   const uint32_t now = millis(); | ||||||
|  |   if (now - this->last_advertisement_time_ > this->advertising_cycle_time_) { | ||||||
|  |     this->stop(); | ||||||
|  |     this->current_adv_index_ += 1; | ||||||
|  |     if (this->current_adv_index_ >= this->raw_advertisements_callbacks_.size()) { | ||||||
|  |       this->current_adv_index_ = -1; | ||||||
|  |     } | ||||||
|  |     this->start(); | ||||||
|  |     this->last_advertisement_time_ = now; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void BLEAdvertising::register_raw_advertisement_callback(std::function<void(bool)> &&callback) { | ||||||
|  |   this->raw_advertisements_callbacks_.push_back(std::move(callback)); | ||||||
| } | } | ||||||
|  |  | ||||||
| }  // namespace esp32_ble | }  // namespace esp32_ble | ||||||
|   | |||||||
| @@ -1,20 +1,31 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include <array> | ||||||
|  | #include <functional> | ||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
| #ifdef USE_ESP32 | #ifdef USE_ESP32 | ||||||
|  |  | ||||||
|  | #include <esp_bt.h> | ||||||
| #include <esp_gap_ble_api.h> | #include <esp_gap_ble_api.h> | ||||||
| #include <esp_gatts_api.h> | #include <esp_gatts_api.h> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace esp32_ble { | namespace esp32_ble { | ||||||
|  |  | ||||||
|  | using raw_adv_data_t = struct { | ||||||
|  |   uint8_t *data; | ||||||
|  |   size_t length; | ||||||
|  |   esp_power_level_t power_level; | ||||||
|  | }; | ||||||
|  |  | ||||||
| class ESPBTUUID; | class ESPBTUUID; | ||||||
|  |  | ||||||
| class BLEAdvertising { | class BLEAdvertising { | ||||||
|  public: |  public: | ||||||
|   BLEAdvertising(); |   BLEAdvertising(uint32_t advertising_cycle_time); | ||||||
|  |  | ||||||
|  |   void loop(); | ||||||
|  |  | ||||||
|   void add_service_uuid(ESPBTUUID uuid); |   void add_service_uuid(ESPBTUUID uuid); | ||||||
|   void remove_service_uuid(ESPBTUUID uuid); |   void remove_service_uuid(ESPBTUUID uuid); | ||||||
| @@ -22,16 +33,25 @@ class BLEAdvertising { | |||||||
|   void set_min_preferred_interval(uint16_t interval) { this->advertising_data_.min_interval = interval; } |   void set_min_preferred_interval(uint16_t interval) { this->advertising_data_.min_interval = interval; } | ||||||
|   void set_manufacturer_data(const std::vector<uint8_t> &data); |   void set_manufacturer_data(const std::vector<uint8_t> &data); | ||||||
|   void set_service_data(const std::vector<uint8_t> &data); |   void set_service_data(const std::vector<uint8_t> &data); | ||||||
|  |   void register_raw_advertisement_callback(std::function<void(bool)> &&callback); | ||||||
|  |  | ||||||
|   void start(); |   void start(); | ||||||
|   void stop(); |   void stop(); | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|  |   esp_err_t services_advertisement_(); | ||||||
|  |  | ||||||
|   bool scan_response_; |   bool scan_response_; | ||||||
|   esp_ble_adv_data_t advertising_data_; |   esp_ble_adv_data_t advertising_data_; | ||||||
|   esp_ble_adv_data_t scan_response_data_; |   esp_ble_adv_data_t scan_response_data_; | ||||||
|   esp_ble_adv_params_t advertising_params_; |   esp_ble_adv_params_t advertising_params_; | ||||||
|   std::vector<ESPBTUUID> advertising_uuids_; |   std::vector<ESPBTUUID> advertising_uuids_; | ||||||
|  |  | ||||||
|  |   std::vector<std::function<void(bool)>> raw_advertisements_callbacks_; | ||||||
|  |  | ||||||
|  |   const uint32_t advertising_cycle_time_; | ||||||
|  |   uint32_t last_advertisement_time_{0}; | ||||||
|  |   int8_t current_adv_index_{-1};  // -1 means standard scan response | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace esp32_ble | }  // namespace esp32_ble | ||||||
|   | |||||||
| @@ -1,16 +1,21 @@ | |||||||
| import esphome.codegen as cg | import esphome.codegen as cg | ||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
|  | from esphome.components.esp32_ble import CONF_BLE_ID | ||||||
| from esphome.const import CONF_ID, CONF_TYPE, CONF_UUID, CONF_TX_POWER | from esphome.const import CONF_ID, CONF_TYPE, CONF_UUID, CONF_TX_POWER | ||||||
| from esphome.core import CORE, TimePeriod | from esphome.core import CORE, TimePeriod | ||||||
| from esphome.components.esp32 import add_idf_sdkconfig_option | from esphome.components.esp32 import add_idf_sdkconfig_option | ||||||
| from esphome.components import esp32_ble | from esphome.components import esp32_ble | ||||||
|  |  | ||||||
|  | AUTO_LOAD = ["esp32_ble"] | ||||||
| DEPENDENCIES = ["esp32"] | DEPENDENCIES = ["esp32"] | ||||||
| CONFLICTS_WITH = ["esp32_ble_tracker"] |  | ||||||
|  |  | ||||||
| esp32_ble_beacon_ns = cg.esphome_ns.namespace("esp32_ble_beacon") | esp32_ble_beacon_ns = cg.esphome_ns.namespace("esp32_ble_beacon") | ||||||
| ESP32BLEBeacon = esp32_ble_beacon_ns.class_("ESP32BLEBeacon", cg.Component) | ESP32BLEBeacon = esp32_ble_beacon_ns.class_( | ||||||
|  |     "ESP32BLEBeacon", | ||||||
|  |     cg.Component, | ||||||
|  |     esp32_ble.GAPEventHandler, | ||||||
|  |     cg.Parented.template(esp32_ble.ESP32BLE), | ||||||
|  | ) | ||||||
| CONF_MAJOR = "major" | CONF_MAJOR = "major" | ||||||
| CONF_MINOR = "minor" | CONF_MINOR = "minor" | ||||||
| CONF_MIN_INTERVAL = "min_interval" | CONF_MIN_INTERVAL = "min_interval" | ||||||
| @@ -28,6 +33,7 @@ CONFIG_SCHEMA = cv.All( | |||||||
|     cv.Schema( |     cv.Schema( | ||||||
|         { |         { | ||||||
|             cv.GenerateID(): cv.declare_id(ESP32BLEBeacon), |             cv.GenerateID(): cv.declare_id(ESP32BLEBeacon), | ||||||
|  |             cv.GenerateID(CONF_BLE_ID): cv.use_id(esp32_ble.ESP32BLE), | ||||||
|             cv.Required(CONF_TYPE): cv.one_of("IBEACON", upper=True), |             cv.Required(CONF_TYPE): cv.one_of("IBEACON", upper=True), | ||||||
|             cv.Required(CONF_UUID): cv.uuid, |             cv.Required(CONF_UUID): cv.uuid, | ||||||
|             cv.Optional(CONF_MAJOR, default=10167): cv.uint16_t, |             cv.Optional(CONF_MAJOR, default=10167): cv.uint16_t, | ||||||
| @@ -48,7 +54,7 @@ CONFIG_SCHEMA = cv.All( | |||||||
|                 min=-128, max=0 |                 min=-128, max=0 | ||||||
|             ), |             ), | ||||||
|             cv.Optional(CONF_TX_POWER, default="3dBm"): cv.All( |             cv.Optional(CONF_TX_POWER, default="3dBm"): cv.All( | ||||||
|                 cv.decibel, cv.one_of(-12, -9, -6, -3, 0, 3, 6, 9, int=True) |                 cv.decibel, cv.enum(esp32_ble.TX_POWER_LEVELS, int=True) | ||||||
|             ), |             ), | ||||||
|         } |         } | ||||||
|     ).extend(cv.COMPONENT_SCHEMA), |     ).extend(cv.COMPONENT_SCHEMA), | ||||||
| @@ -62,6 +68,10 @@ async def to_code(config): | |||||||
|     uuid = config[CONF_UUID].hex |     uuid = config[CONF_UUID].hex | ||||||
|     uuid_arr = [cg.RawExpression(f"0x{uuid[i:i + 2]}") for i in range(0, len(uuid), 2)] |     uuid_arr = [cg.RawExpression(f"0x{uuid[i:i + 2]}") for i in range(0, len(uuid), 2)] | ||||||
|     var = cg.new_Pvariable(config[CONF_ID], uuid_arr) |     var = cg.new_Pvariable(config[CONF_ID], uuid_arr) | ||||||
|  |  | ||||||
|  |     parent = await cg.get_variable(config[esp32_ble.CONF_BLE_ID]) | ||||||
|  |     cg.add(parent.register_gap_event_handler(var)) | ||||||
|  |  | ||||||
|     await cg.register_component(var, config) |     await cg.register_component(var, config) | ||||||
|     cg.add(var.set_major(config[CONF_MAJOR])) |     cg.add(var.set_major(config[CONF_MAJOR])) | ||||||
|     cg.add(var.set_minor(config[CONF_MINOR])) |     cg.add(var.set_minor(config[CONF_MINOR])) | ||||||
|   | |||||||
| @@ -3,14 +3,16 @@ | |||||||
|  |  | ||||||
| #ifdef USE_ESP32 | #ifdef USE_ESP32 | ||||||
|  |  | ||||||
| #include <nvs_flash.h> |  | ||||||
| #include <freertos/FreeRTOS.h> |  | ||||||
| #include <esp_bt_main.h> |  | ||||||
| #include <esp_bt.h> | #include <esp_bt.h> | ||||||
| #include <freertos/task.h> | #include <esp_bt_main.h> | ||||||
| #include <esp_gap_ble_api.h> | #include <esp_gap_ble_api.h> | ||||||
|  | #include <freertos/FreeRTOS.h> | ||||||
|  | #include <freertos/task.h> | ||||||
|  | #include <nvs_flash.h> | ||||||
| #include <cstring> | #include <cstring> | ||||||
|  |  | ||||||
| #include "esphome/core/hal.h" | #include "esphome/core/hal.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  |  | ||||||
| #ifdef USE_ARDUINO | #ifdef USE_ARDUINO | ||||||
| #include <esp32-hal-bt.h> | #include <esp32-hal-bt.h> | ||||||
| @@ -21,20 +23,6 @@ namespace esp32_ble_beacon { | |||||||
|  |  | ||||||
| static const char *const TAG = "esp32_ble_beacon"; | static const char *const TAG = "esp32_ble_beacon"; | ||||||
|  |  | ||||||
| // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) |  | ||||||
| static esp_ble_adv_params_t ble_adv_params = { |  | ||||||
|     .adv_int_min = 0x20, |  | ||||||
|     .adv_int_max = 0x40, |  | ||||||
|     .adv_type = ADV_TYPE_NONCONN_IND, |  | ||||||
|     .own_addr_type = BLE_ADDR_TYPE_PUBLIC, |  | ||||||
|     .peer_addr = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, |  | ||||||
|     .peer_addr_type = BLE_ADDR_TYPE_PUBLIC, |  | ||||||
|     .channel_map = ADV_CHNL_ALL, |  | ||||||
|     .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #define ENDIAN_CHANGE_U16(x) ((((x) &0xFF00) >> 8) + (((x) &0xFF) << 8)) |  | ||||||
|  |  | ||||||
| static const esp_ble_ibeacon_head_t IBEACON_COMMON_HEAD = { | static const esp_ble_ibeacon_head_t IBEACON_COMMON_HEAD = { | ||||||
|     .flags = {0x02, 0x01, 0x06}, .length = 0x1A, .type = 0xFF, .company_id = {0x4C, 0x00}, .beacon_type = {0x02, 0x15}}; |     .flags = {0x02, 0x01, 0x06}, .length = 0x1A, .type = 0xFF, .company_id = {0x4C, 0x00}, .beacon_type = {0x02, 0x15}}; | ||||||
|  |  | ||||||
| @@ -53,117 +41,62 @@ void ESP32BLEBeacon::dump_config() { | |||||||
|                 "  UUID: %s, Major: %u, Minor: %u, Min Interval: %ums, Max Interval: %ums, Measured Power: %d" |                 "  UUID: %s, Major: %u, Minor: %u, Min Interval: %ums, Max Interval: %ums, Measured Power: %d" | ||||||
|                 ", TX Power: %ddBm", |                 ", TX Power: %ddBm", | ||||||
|                 uuid, this->major_, this->minor_, this->min_interval_, this->max_interval_, this->measured_power_, |                 uuid, this->major_, this->minor_, this->min_interval_, this->max_interval_, this->measured_power_, | ||||||
|                 this->tx_power_); |                 (this->tx_power_ * 3) - 12); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | float ESP32BLEBeacon::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; } | ||||||
|  |  | ||||||
| void ESP32BLEBeacon::setup() { | void ESP32BLEBeacon::setup() { | ||||||
|   ESP_LOGCONFIG(TAG, "Setting up ESP32 BLE beacon..."); |   this->ble_adv_params_ = { | ||||||
|   global_esp32_ble_beacon = this; |       .adv_int_min = static_cast<uint16_t>(this->min_interval_ / 0.625f), | ||||||
|  |       .adv_int_max = static_cast<uint16_t>(this->max_interval_ / 0.625f), | ||||||
|  |       .adv_type = ADV_TYPE_NONCONN_IND, | ||||||
|  |       .own_addr_type = BLE_ADDR_TYPE_PUBLIC, | ||||||
|  |       .peer_addr = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, | ||||||
|  |       .peer_addr_type = BLE_ADDR_TYPE_PUBLIC, | ||||||
|  |       .channel_map = ADV_CHNL_ALL, | ||||||
|  |       .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, | ||||||
|  |   }; | ||||||
|  |  | ||||||
|   xTaskCreatePinnedToCore(ESP32BLEBeacon::ble_core_task, |   global_ble->advertising_register_raw_advertisement_callback([this](bool advertise) { | ||||||
|                           "ble_task",  // name |     this->advertising_ = advertise; | ||||||
|                           10000,       // stack size (in words) |     if (advertise) { | ||||||
|                           nullptr,     // input params |       this->on_advertise_(); | ||||||
|                           1,           // priority |     } | ||||||
|                           nullptr,     // Handle, not needed |   }); | ||||||
|                           0            // core |  | ||||||
|   ); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| float ESP32BLEBeacon::get_setup_priority() const { return setup_priority::BLUETOOTH; } |  | ||||||
| void ESP32BLEBeacon::ble_core_task(void *params) { |  | ||||||
|   ble_setup(); |  | ||||||
|  |  | ||||||
|   while (true) { |  | ||||||
|     delay(1000);  // NOLINT |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ESP32BLEBeacon::ble_setup() { |  | ||||||
|   ble_adv_params.adv_int_min = static_cast<uint16_t>(global_esp32_ble_beacon->min_interval_ / 0.625f); |  | ||||||
|   ble_adv_params.adv_int_max = static_cast<uint16_t>(global_esp32_ble_beacon->max_interval_ / 0.625f); |  | ||||||
|  |  | ||||||
|   // Initialize non-volatile storage for the bluetooth controller |  | ||||||
|   esp_err_t err = nvs_flash_init(); |  | ||||||
|   if (err != ESP_OK) { |  | ||||||
|     ESP_LOGE(TAG, "nvs_flash_init failed: %d", err); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| #ifdef USE_ARDUINO |  | ||||||
|   if (!btStart()) { |  | ||||||
|     ESP_LOGE(TAG, "btStart failed: %d", esp_bt_controller_get_status()); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
| #else |  | ||||||
|   if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { |  | ||||||
|     // start bt controller |  | ||||||
|     if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) { |  | ||||||
|       esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); |  | ||||||
|       err = esp_bt_controller_init(&cfg); |  | ||||||
|       if (err != ESP_OK) { |  | ||||||
|         ESP_LOGE(TAG, "esp_bt_controller_init failed: %s", esp_err_to_name(err)); |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|       while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) |  | ||||||
|         ; |  | ||||||
|     } |  | ||||||
|     if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) { |  | ||||||
|       err = esp_bt_controller_enable(ESP_BT_MODE_BLE); |  | ||||||
|       if (err != ESP_OK) { |  | ||||||
|         ESP_LOGE(TAG, "esp_bt_controller_enable failed: %s", esp_err_to_name(err)); |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { |  | ||||||
|       ESP_LOGE(TAG, "esp bt controller enable failed"); |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|   esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); |  | ||||||
|  |  | ||||||
|   err = esp_bluedroid_init(); |  | ||||||
|   if (err != ESP_OK) { |  | ||||||
|     ESP_LOGE(TAG, "esp_bluedroid_init failed: %d", err); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
|   err = esp_bluedroid_enable(); |  | ||||||
|   if (err != ESP_OK) { |  | ||||||
|     ESP_LOGE(TAG, "esp_bluedroid_enable failed: %d", err); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
|   err = esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, |  | ||||||
|                              static_cast<esp_power_level_t>((global_esp32_ble_beacon->tx_power_ + 12) / 3)); |  | ||||||
|   if (err != ESP_OK) { |  | ||||||
|     ESP_LOGE(TAG, "esp_ble_tx_power_set failed: %s", esp_err_to_name(err)); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
|   err = esp_ble_gap_register_callback(ESP32BLEBeacon::gap_event_handler); |  | ||||||
|   if (err != ESP_OK) { |  | ||||||
|     ESP_LOGE(TAG, "esp_ble_gap_register_callback failed: %d", err); |  | ||||||
|     return; |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void ESP32BLEBeacon::on_advertise_() { | ||||||
|   esp_ble_ibeacon_t ibeacon_adv_data; |   esp_ble_ibeacon_t ibeacon_adv_data; | ||||||
|   memcpy(&ibeacon_adv_data.ibeacon_head, &IBEACON_COMMON_HEAD, sizeof(esp_ble_ibeacon_head_t)); |   memcpy(&ibeacon_adv_data.ibeacon_head, &IBEACON_COMMON_HEAD, sizeof(esp_ble_ibeacon_head_t)); | ||||||
|   memcpy(&ibeacon_adv_data.ibeacon_vendor.proximity_uuid, global_esp32_ble_beacon->uuid_.data(), |   memcpy(&ibeacon_adv_data.ibeacon_vendor.proximity_uuid, this->uuid_.data(), | ||||||
|          sizeof(ibeacon_adv_data.ibeacon_vendor.proximity_uuid)); |          sizeof(ibeacon_adv_data.ibeacon_vendor.proximity_uuid)); | ||||||
|   ibeacon_adv_data.ibeacon_vendor.minor = ENDIAN_CHANGE_U16(global_esp32_ble_beacon->minor_); |   ibeacon_adv_data.ibeacon_vendor.minor = byteswap(this->minor_); | ||||||
|   ibeacon_adv_data.ibeacon_vendor.major = ENDIAN_CHANGE_U16(global_esp32_ble_beacon->major_); |   ibeacon_adv_data.ibeacon_vendor.major = byteswap(this->major_); | ||||||
|   ibeacon_adv_data.ibeacon_vendor.measured_power = static_cast<uint8_t>(global_esp32_ble_beacon->measured_power_); |   ibeacon_adv_data.ibeacon_vendor.measured_power = static_cast<uint8_t>(this->measured_power_); | ||||||
|  |  | ||||||
|   esp_ble_gap_config_adv_data_raw((uint8_t *) &ibeacon_adv_data, sizeof(ibeacon_adv_data)); |   ESP_LOGD(TAG, "Setting BLE TX power"); | ||||||
|  |   esp_err_t err = esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, this->tx_power_); | ||||||
|  |   if (err != ESP_OK) { | ||||||
|  |     ESP_LOGW(TAG, "esp_ble_tx_power_set failed: %s", esp_err_to_name(err)); | ||||||
|  |   } | ||||||
|  |   err = esp_ble_gap_config_adv_data_raw((uint8_t *) &ibeacon_adv_data, sizeof(ibeacon_adv_data)); | ||||||
|  |   if (err != ESP_OK) { | ||||||
|  |     ESP_LOGE(TAG, "esp_ble_gap_config_adv_data_raw failed: %s", esp_err_to_name(err)); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| void ESP32BLEBeacon::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { | void ESP32BLEBeacon::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { | ||||||
|  |   if (!this->advertising_) | ||||||
|  |     return; | ||||||
|  |  | ||||||
|   esp_err_t err; |   esp_err_t err; | ||||||
|   switch (event) { |   switch (event) { | ||||||
|     case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: { |     case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: { | ||||||
|       err = esp_ble_gap_start_advertising(&ble_adv_params); |       err = esp_ble_gap_start_advertising(&this->ble_adv_params_); | ||||||
|       if (err != ESP_OK) { |       if (err != ESP_OK) { | ||||||
|         ESP_LOGE(TAG, "esp_ble_gap_start_advertising failed: %d", err); |         ESP_LOGE(TAG, "esp_ble_gap_start_advertising failed: %s", esp_err_to_name(err)); | ||||||
|       } |       } | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
| @@ -181,6 +114,7 @@ void ESP32BLEBeacon::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap | |||||||
|       } else { |       } else { | ||||||
|         ESP_LOGD(TAG, "BLE stopped advertising successfully"); |         ESP_LOGD(TAG, "BLE stopped advertising successfully"); | ||||||
|       } |       } | ||||||
|  |       // this->advertising_ = false; | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     default: |     default: | ||||||
| @@ -188,8 +122,6 @@ void ESP32BLEBeacon::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| ESP32BLEBeacon *global_esp32_ble_beacon = nullptr;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) |  | ||||||
|  |  | ||||||
| }  // namespace esp32_ble_beacon | }  // namespace esp32_ble_beacon | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,39 +1,39 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/components/esp32_ble/ble.h" | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
|  |  | ||||||
| #ifdef USE_ESP32 | #ifdef USE_ESP32 | ||||||
|  |  | ||||||
| #include <esp_gap_ble_api.h> |  | ||||||
| #include <esp_bt.h> | #include <esp_bt.h> | ||||||
|  | #include <esp_gap_ble_api.h> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace esp32_ble_beacon { | namespace esp32_ble_beacon { | ||||||
|  |  | ||||||
| // NOLINTNEXTLINE(modernize-use-using) | using esp_ble_ibeacon_head_t = struct { | ||||||
| typedef struct { |  | ||||||
|   uint8_t flags[3]; |   uint8_t flags[3]; | ||||||
|   uint8_t length; |   uint8_t length; | ||||||
|   uint8_t type; |   uint8_t type; | ||||||
|   uint8_t company_id[2]; |   uint8_t company_id[2]; | ||||||
|   uint8_t beacon_type[2]; |   uint8_t beacon_type[2]; | ||||||
| } __attribute__((packed)) esp_ble_ibeacon_head_t; | } __attribute__((packed)); | ||||||
|  |  | ||||||
| // NOLINTNEXTLINE(modernize-use-using) | using esp_ble_ibeacon_vendor_t = struct { | ||||||
| typedef struct { |  | ||||||
|   uint8_t proximity_uuid[16]; |   uint8_t proximity_uuid[16]; | ||||||
|   uint16_t major; |   uint16_t major; | ||||||
|   uint16_t minor; |   uint16_t minor; | ||||||
|   uint8_t measured_power; |   uint8_t measured_power; | ||||||
| } __attribute__((packed)) esp_ble_ibeacon_vendor_t; | } __attribute__((packed)); | ||||||
|  |  | ||||||
| // NOLINTNEXTLINE(modernize-use-using) | using esp_ble_ibeacon_t = struct { | ||||||
| typedef struct { |  | ||||||
|   esp_ble_ibeacon_head_t ibeacon_head; |   esp_ble_ibeacon_head_t ibeacon_head; | ||||||
|   esp_ble_ibeacon_vendor_t ibeacon_vendor; |   esp_ble_ibeacon_vendor_t ibeacon_vendor; | ||||||
| } __attribute__((packed)) esp_ble_ibeacon_t; | } __attribute__((packed)); | ||||||
|  |  | ||||||
| class ESP32BLEBeacon : public Component { | using namespace esp32_ble; | ||||||
|  |  | ||||||
|  | class ESP32BLEBeacon : public Component, public GAPEventHandler, public Parented<ESP32BLE> { | ||||||
|  public: |  public: | ||||||
|   explicit ESP32BLEBeacon(const std::array<uint8_t, 16> &uuid) : uuid_(uuid) {} |   explicit ESP32BLEBeacon(const std::array<uint8_t, 16> &uuid) : uuid_(uuid) {} | ||||||
|  |  | ||||||
| @@ -46,12 +46,11 @@ class ESP32BLEBeacon : public Component { | |||||||
|   void set_min_interval(uint16_t val) { this->min_interval_ = val; } |   void set_min_interval(uint16_t val) { this->min_interval_ = val; } | ||||||
|   void set_max_interval(uint16_t val) { this->max_interval_ = val; } |   void set_max_interval(uint16_t val) { this->max_interval_ = val; } | ||||||
|   void set_measured_power(int8_t val) { this->measured_power_ = val; } |   void set_measured_power(int8_t val) { this->measured_power_ = val; } | ||||||
|   void set_tx_power(int8_t val) { this->tx_power_ = val; } |   void set_tx_power(esp_power_level_t val) { this->tx_power_ = val; } | ||||||
|  |   void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override; | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); |   void on_advertise_(); | ||||||
|   static void ble_core_task(void *params); |  | ||||||
|   static void ble_setup(); |  | ||||||
|  |  | ||||||
|   std::array<uint8_t, 16> uuid_; |   std::array<uint8_t, 16> uuid_; | ||||||
|   uint16_t major_{}; |   uint16_t major_{}; | ||||||
| @@ -59,12 +58,11 @@ class ESP32BLEBeacon : public Component { | |||||||
|   uint16_t min_interval_{}; |   uint16_t min_interval_{}; | ||||||
|   uint16_t max_interval_{}; |   uint16_t max_interval_{}; | ||||||
|   int8_t measured_power_{}; |   int8_t measured_power_{}; | ||||||
|   int8_t tx_power_{}; |   esp_power_level_t tx_power_{}; | ||||||
|  |   esp_ble_adv_params_t ble_adv_params_; | ||||||
|  |   bool advertising_{false}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) |  | ||||||
| extern ESP32BLEBeacon *global_esp32_ble_beacon; |  | ||||||
|  |  | ||||||
| }  // namespace esp32_ble_beacon | }  // namespace esp32_ble_beacon | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,7 +7,6 @@ from esphome.components.esp32 import add_idf_sdkconfig_option | |||||||
|  |  | ||||||
| AUTO_LOAD = ["esp32_ble"] | AUTO_LOAD = ["esp32_ble"] | ||||||
| CODEOWNERS = ["@jesserockz", "@clydebarrow", "@Rapsssito"] | CODEOWNERS = ["@jesserockz", "@clydebarrow", "@Rapsssito"] | ||||||
| CONFLICTS_WITH = ["esp32_ble_beacon"] |  | ||||||
| DEPENDENCIES = ["esp32"] | DEPENDENCIES = ["esp32"] | ||||||
|  |  | ||||||
| CONF_MANUFACTURER = "manufacturer" | CONF_MANUFACTURER = "manufacturer" | ||||||
|   | |||||||
| @@ -6,7 +6,6 @@ from esphome.const import CONF_ID | |||||||
|  |  | ||||||
| AUTO_LOAD = ["esp32_ble_server"] | AUTO_LOAD = ["esp32_ble_server"] | ||||||
| CODEOWNERS = ["@jesserockz"] | CODEOWNERS = ["@jesserockz"] | ||||||
| CONFLICTS_WITH = ["esp32_ble_beacon"] |  | ||||||
| DEPENDENCIES = ["wifi", "esp32"] | DEPENDENCIES = ["wifi", "esp32"] | ||||||
|  |  | ||||||
| CONF_AUTHORIZED_DURATION = "authorized_duration" | CONF_AUTHORIZED_DURATION = "authorized_duration" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user