diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index 5a4862f733..7e998e77b1 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -1,19 +1,48 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.const import CONF_ID, CONF_SCAN_INTERVAL, ESP_PLATFORM_ESP32 +from esphome.const import CONF_ID, ESP_PLATFORM_ESP32, CONF_INTERVAL, \ + CONF_DURATION from esphome.core import coroutine ESP_PLATFORMS = [ESP_PLATFORM_ESP32] AUTO_LOAD = ['xiaomi_ble'] CONF_ESP32_BLE_ID = 'esp32_ble_id' +CONF_SCAN_PARAMETERS = 'scan_parameters' +CONF_WINDOW = 'window' +CONF_ACTIVE = 'active' esp32_ble_tracker_ns = cg.esphome_ns.namespace('esp32_ble_tracker') ESP32BLETracker = esp32_ble_tracker_ns.class_('ESP32BLETracker', cg.Component) ESPBTDeviceListener = esp32_ble_tracker_ns.class_('ESPBTDeviceListener') + +def validate_scan_parameters(config): + duration = config[CONF_DURATION] + interval = config[CONF_INTERVAL] + window = config[CONF_WINDOW] + + if window > interval: + raise cv.Invalid("Scan window ({}) needs to be smaller than scan interval ({})" + "".format(window, interval)) + + if interval.total_milliseconds * 3 > duration.total_milliseconds: + raise cv.Invalid("Scan duration needs to be at least three times the scan interval to" + "cover all BLE channels.") + + return config + + CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(ESP32BLETracker), - cv.Optional(CONF_SCAN_INTERVAL, default='300s'): cv.positive_time_period_seconds, + cv.Optional(CONF_SCAN_PARAMETERS, default={}): cv.All(cv.Schema({ + cv.Optional(CONF_DURATION, default='5min'): cv.positive_time_period_seconds, + cv.Optional(CONF_INTERVAL, default='320ms'): cv.positive_time_period_milliseconds, + cv.Optional(CONF_WINDOW, default='200ms'): cv.positive_time_period_milliseconds, + cv.Optional(CONF_ACTIVE, default=True): cv.boolean, + }), validate_scan_parameters), + + cv.Optional('scan_interval'): cv.invalid("This option has been removed in 1.14 (Reason: " + "it never had an effect)"), }).extend(cv.COMPONENT_SCHEMA) ESP_BLE_DEVICE_SCHEMA = cv.Schema({ @@ -24,7 +53,11 @@ ESP_BLE_DEVICE_SCHEMA = cv.Schema({ def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) - cg.add(var.set_scan_interval(config[CONF_SCAN_INTERVAL])) + params = config[CONF_SCAN_PARAMETERS] + cg.add(var.set_scan_duration(params[CONF_DURATION])) + cg.add(var.set_scan_interval(int(params[CONF_INTERVAL].total_milliseconds / 0.625))) + cg.add(var.set_scan_window(int(params[CONF_WINDOW].total_milliseconds / 0.625))) + cg.add(var.set_scan_active(params[CONF_ACTIVE])) @coroutine diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 9e6cb1cbce..ff1fe59668 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -150,21 +150,16 @@ void ESP32BLETracker::start_scan(bool first) { listener->on_scan_end(); } this->already_discovered_.clear(); - this->scan_params_.scan_type = BLE_SCAN_TYPE_ACTIVE; + this->scan_params_.scan_type = this->scan_active_ ? BLE_SCAN_TYPE_ACTIVE : BLE_SCAN_TYPE_PASSIVE; this->scan_params_.own_addr_type = BLE_ADDR_TYPE_PUBLIC; this->scan_params_.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL; - // Values determined empirically, higher scan intervals and lower scan windows make the ESP more stable - // Ideally, these values should both be quite low, especially scan window. 0x10/0x10 is the esp-idf - // default and works quite well. 0x100/0x50 discovers a few less BLE broadcast packets but is a lot - // more stable (order of several hours). The old ESPHome default (1600/1600) was terrible with - // crashes every few minutes - this->scan_params_.scan_interval = 0x200; - this->scan_params_.scan_window = 0x30; + this->scan_params_.scan_interval = this->scan_interval_; + this->scan_params_.scan_window = this->scan_window_; esp_ble_gap_set_scan_params(&this->scan_params_); - esp_ble_gap_start_scanning(this->scan_interval_); + esp_ble_gap_start_scanning(this->scan_duration_); - this->set_timeout("scan", this->scan_interval_ * 2000, []() { + this->set_timeout("scan", this->scan_duration_ * 2000, []() { ESP_LOGW(TAG, "ESP-IDF BLE scan never terminated, rebooting to restore BLE stack..."); App.reboot(); }); @@ -454,10 +449,12 @@ const std::string &ESPBTDevice::get_manufacturer_data() const { return this->man const std::string &ESPBTDevice::get_service_data() const { return this->service_data_; } const optional &ESPBTDevice::get_service_data_uuid() const { return this->service_data_uuid_; } -void ESP32BLETracker::set_scan_interval(uint32_t scan_interval) { this->scan_interval_ = scan_interval; } void ESP32BLETracker::dump_config() { ESP_LOGCONFIG(TAG, "BLE Tracker:"); - ESP_LOGCONFIG(TAG, " Scan Interval: %u s", this->scan_interval_); + ESP_LOGCONFIG(TAG, " Scan Duration: %u s", this->scan_duration_); + ESP_LOGCONFIG(TAG, " Scan Interval: %u ms", this->scan_interval_); + ESP_LOGCONFIG(TAG, " Scan Window: %u ms", this->scan_window_); + ESP_LOGCONFIG(TAG, " Scan Type: %s", this->scan_active_ ? "ACTIVE" : "PASSIVE"); } void ESP32BLETracker::print_bt_device_info(const ESPBTDevice &device) { const uint64_t address = device.address_uint64(); diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index f1bcada621..82e8e553fc 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -107,7 +107,10 @@ class ESPBTDeviceListener { class ESP32BLETracker : public Component { public: - void set_scan_interval(uint32_t scan_interval); + void set_scan_duration(uint32_t scan_duration) { scan_duration_ = scan_duration; } + void set_scan_interval(uint32_t scan_interval) { scan_interval_ = scan_interval; } + void set_scan_window(uint32_t scan_window) { scan_window_ = scan_window; } + void set_scan_active(bool scan_active) { scan_active_ = scan_active; } /// Setup the FreeRTOS task and the Bluetooth stack. void setup() override; @@ -142,7 +145,10 @@ class ESP32BLETracker : public Component { /// A structure holding the ESP BLE scan parameters. esp_ble_scan_params_t scan_params_; /// The interval in seconds to perform scans. - uint32_t scan_interval_{300}; + uint32_t scan_duration_; + uint32_t scan_interval_; + uint32_t scan_window_; + bool scan_active_; SemaphoreHandle_t scan_result_lock_; SemaphoreHandle_t scan_end_lock_; size_t scan_result_index_{0}; diff --git a/esphome/const.py b/esphome/const.py index fda57060f1..523b9b019d 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -364,7 +364,6 @@ CONF_RX_PIN = 'rx_pin' CONF_SAFE_MODE = 'safe_mode' CONF_SAMSUNG = 'samsung' CONF_SCAN = 'scan' -CONF_SCAN_INTERVAL = 'scan_interval' CONF_SCL = 'scl' CONF_SCL_PIN = 'scl_pin' CONF_SDA = 'sda' diff --git a/tests/test2.yaml b/tests/test2.yaml index 337deece91..1f4245d81d 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -197,7 +197,6 @@ remote_receiver: dump: [] esp32_ble_tracker: - scan_interval: 300s #esp32_ble_beacon: # type: iBeacon