Inherit esp32_ble_beacon from esp32_ble (#6908)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Jan-Philipp Benecke 2024-07-22 06:20:09 +02:00 committed by GitHub
parent 172a358d01
commit 5d5f3276e9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 176 additions and 151 deletions

View file

@ -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:

View file

@ -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) {

View file

@ -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_;
}; };

View file

@ -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

View file

@ -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

View file

@ -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]))

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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"