mirror of
https://github.com/esphome/esphome.git
synced 2026-08-27 00:18:32 +00:00
Return to the configured window when the last connection drops; review fixes
This commit is contained in:
@@ -234,13 +234,13 @@ def validate_scan_parameters(config: ConfigType) -> ConfigType:
|
||||
# unit collapses to the same value — silently programming a 100 % duty cycle
|
||||
# (radio permanently on) from a config that asked for less.
|
||||
interval_units = to_ble_units(interval)
|
||||
window_units = to_ble_units(window)
|
||||
if window_units == interval_units and window < interval:
|
||||
raise cv.Invalid(
|
||||
f"Scan window ({window}) and interval ({interval}) both truncate to "
|
||||
f"{interval_units} x 0.625 ms, which the controller scans at a 100 % duty "
|
||||
f"cycle. Separate them by at least 0.625 ms."
|
||||
)
|
||||
for name, value in windows:
|
||||
if to_ble_units(value) == interval_units and value < interval:
|
||||
raise cv.Invalid(
|
||||
f"Scan {name} ({value}) and interval ({interval}) both truncate to "
|
||||
f"{interval_units} x 0.625 ms, which the controller scans at a 100 % duty "
|
||||
f"cycle. Separate them by at least 0.625 ms."
|
||||
)
|
||||
|
||||
if interval.total_microseconds * 3 > duration.total_microseconds:
|
||||
raise cv.Invalid(
|
||||
|
||||
@@ -189,7 +189,9 @@ def _raise_defaulted_scan_window(config: ConfigType) -> ConfigType:
|
||||
params[CONF_WINDOW] = copy.copy(params[CONF_INTERVAL])
|
||||
# A full-duty scan must not compete with an active GATT connection
|
||||
# for airtime, so arm the connection-time fallback window as well
|
||||
# unless the user picked one themselves.
|
||||
# unless the user picked one themselves. Injected after validation;
|
||||
# safe only because it equals the window default, which already
|
||||
# passed the <= interval and controller range checks.
|
||||
if CONF_CONNECTION_SCAN_WINDOW not in params:
|
||||
params[CONF_CONNECTION_SCAN_WINDOW] = cv.positive_time_period(
|
||||
ble_device_base.DEFAULT_SCAN_WINDOW
|
||||
|
||||
@@ -144,6 +144,16 @@ void ESP32BLETracker::loop() {
|
||||
(this->scan_set_param_failed_ && this->scanner_state_ == ScannerState::RUNNING)) {
|
||||
this->handle_scanner_failure_();
|
||||
}
|
||||
|
||||
// The window is chosen at scan start, so when the last connection drops
|
||||
// mid-scan the reduced connection-time window would persist for the rest
|
||||
// of the scan period (up to scan_duration_); stop the scan so the restart
|
||||
// below returns to the configured window. Only for continuous scanning:
|
||||
// a user-started scan would not be restarted.
|
||||
if (this->scan_window_reduced_ && !counts.active && this->scan_continuous_ &&
|
||||
this->scanner_state_ == ScannerState::RUNNING) {
|
||||
this->stop_scan_();
|
||||
}
|
||||
/*
|
||||
|
||||
Avoid starting the scanner if:
|
||||
@@ -248,11 +258,11 @@ void ESP32BLETracker::start_scan_(bool first) {
|
||||
this->scan_params_.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL;
|
||||
this->scan_params_.scan_interval = this->scan_interval_;
|
||||
uint32_t window = this->scan_window_;
|
||||
if (this->connection_scan_window_ != 0 && this->client_state_counts_.active > 0) {
|
||||
// The defaulted window was raised to full duty for advertisement
|
||||
// throughput; while a GATT connection is active, fall back so the
|
||||
// connection events get guaranteed airtime instead of competing with
|
||||
// a wall-to-wall scan on the shared radio.
|
||||
this->scan_window_reduced_ = this->connection_scan_window_ != 0 && this->client_state_counts_.active > 0;
|
||||
if (this->scan_window_reduced_) {
|
||||
// While a GATT connection is active, fall back to the connection scan
|
||||
// window so the connection events get guaranteed airtime instead of
|
||||
// competing with a wall-to-wall scan on the shared radio.
|
||||
ESP_LOGV(TAG, "Connection active, using %" PRIu32 " unit scan window", this->connection_scan_window_);
|
||||
window = this->connection_scan_window_;
|
||||
}
|
||||
@@ -417,6 +427,9 @@ void ESP32BLETracker::dump_config() {
|
||||
" Continuous Scanning: %s",
|
||||
this->scan_duration_, this->scan_interval_ * 0.625f, this->scan_window_ * 0.625f,
|
||||
this->scan_active_ ? "ACTIVE" : "PASSIVE", YESNO(this->scan_continuous_));
|
||||
if (this->connection_scan_window_ != 0) {
|
||||
ESP_LOGCONFIG(TAG, " Connection Scan Window: %.1f ms", this->connection_scan_window_ * 0.625f);
|
||||
}
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Scanner State: %s\n"
|
||||
" Connecting: %d, discovered: %d, disconnecting: %d, active: %d",
|
||||
|
||||
@@ -314,8 +314,8 @@ class ESP32BLETracker final : public Component,
|
||||
uint32_t scan_duration_;
|
||||
uint32_t scan_interval_;
|
||||
uint32_t scan_window_;
|
||||
/// Window used while a GATT connection is active; only set when the
|
||||
/// defaulted window was raised to full duty (0 = no fallback).
|
||||
/// Window used while a GATT connection is active; set by the user, or
|
||||
/// defaulted when the window was raised to full duty (0 = no fallback).
|
||||
uint32_t connection_scan_window_{0};
|
||||
esp_bt_status_t scan_start_failed_{ESP_BT_STATUS_SUCCESS};
|
||||
esp_bt_status_t scan_set_param_failed_{ESP_BT_STATUS_SUCCESS};
|
||||
@@ -341,6 +341,9 @@ class ESP32BLETracker final : public Component,
|
||||
#endif
|
||||
bool ble_was_disabled_{true};
|
||||
bool parse_advertisements_{false};
|
||||
/// The running scan was started with connection_scan_window_; lets loop()
|
||||
/// restart the scan at the configured window when the last connection drops.
|
||||
bool scan_window_reduced_{false};
|
||||
#ifdef USE_ESP32_BLE_SOFTWARE_COEXISTENCE
|
||||
bool coex_prefer_ble_{false};
|
||||
#endif
|
||||
|
||||
@@ -163,6 +163,23 @@ def test_connection_scan_window_above_interval_rejected(
|
||||
_scan_params({"scan_parameters": {"connection_scan_window": "400ms"}})
|
||||
|
||||
|
||||
def test_connection_scan_window_truncation_collapse_rejected(
|
||||
stage_esp32: Callable[..., None],
|
||||
) -> None:
|
||||
"""A connection window that truncates into the interval's 0.625 ms unit
|
||||
would silently program a full-duty scan during connections."""
|
||||
stage_esp32("5.5.5", wifi=True)
|
||||
with pytest.raises(cv.Invalid, match="connection window .* both truncate"):
|
||||
_scan_params(
|
||||
{
|
||||
"scan_parameters": {
|
||||
"interval": "320.5ms",
|
||||
"connection_scan_window": "320.2ms",
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def test_codegen_connection_window_when_raised(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
|
||||
Reference in New Issue
Block a user