[rp2040_ble] Add scan arbitration and GATT client hooks (#18155)

This commit is contained in:
J. Nick Koston
2026-08-07 11:50:18 -05:00
committed by GitHub
parent c5e165d062
commit c0e70d9beb
2 changed files with 77 additions and 3 deletions
@@ -57,6 +57,17 @@ void RP2040BLE::enable() {
l2cap_init();
sm_init();
#ifdef USE_BLE_GATT_CLIENT
gatt_client_init();
// The GATT engine kicks the MTU exchange explicitly right after a
// connection completes (auto negotiation would only run on the first
// query, which a with-cache connection never issues).
gatt_client_mtu_enable_auto_negotiation(0);
// Just-works security for peripheral-initiated pairing.
sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT);
sm_set_authentication_requirements(SM_AUTHREQ_BONDING);
#endif
this->hci_event_callback_registration_.callback = &RP2040BLE::packet_handler;
hci_add_event_handler(&this->hci_event_callback_registration_);
@@ -215,6 +226,18 @@ bool RP2040BLE::scan_start(uint16_t interval, uint16_t window, bool active) {
// the moment a tracker retries. Callers retry until the stack is up.
return false;
}
#ifdef USE_BLE_GATT_CLIENT
this->scan_interval_ = interval;
this->scan_window_ = window;
this->scan_active_mode_ = active;
this->scan_desired_ = true;
if (this->scan_inhibit_count_ > 0) {
// A connect attempt owns the radio; the scan starts physically when the
// inhibit is released. Report success — the controller will run it.
ESP_LOGV(TAG, "Scan start deferred (connect in progress)");
return true;
}
#endif
// Serialize with the BTstack background worker (arduino-pico's BluetoothHCI
// takes the same lock around its gap_* calls).
BluetoothLock lock;
@@ -224,6 +247,9 @@ bool RP2040BLE::scan_start(uint16_t interval, uint16_t window, bool active) {
}
void RP2040BLE::scan_stop() {
#ifdef USE_BLE_GATT_CLIENT
this->scan_desired_ = false;
#endif
if (!this->is_active()) {
return; // nothing can be scanning on a stack that is not up
}
@@ -231,6 +257,32 @@ void RP2040BLE::scan_stop() {
gap_stop_scan();
}
#ifdef USE_BLE_GATT_CLIENT
void RP2040BLE::inhibit_scan() {
if (this->scan_inhibit_count_++ != 0) {
return; // another connect attempt already owns the radio
}
if (this->scan_desired_ && this->is_active()) {
BluetoothLock lock;
gap_stop_scan();
}
}
void RP2040BLE::release_scan_inhibit() {
if (this->scan_inhibit_count_ == 0) {
return;
}
this->scan_inhibit_count_--;
if (this->scan_inhibit_count_ != 0) {
return;
}
if (this->scan_desired_) {
// One physical-start path: scan_start re-applies the remembered params.
this->scan_start(this->scan_interval_, this->scan_window_, this->scan_active_mode_);
}
}
#endif // USE_BLE_GATT_CLIENT
} // namespace esphome::rp2040_ble
#endif // USE_RP2040_BLE
+25 -3
View File
@@ -91,13 +91,25 @@ class RP2040BLE final : public Component {
/// (0.625 ms). Returns false until the stack is ACTIVE (callers retry — the
/// tracker's rate-limited retry loop); powering the stack on stays with the
/// user (enable_on_boot or an explicit enable() call). The controller keeps
/// no scan state: a disable()/enable() power cycle ends the scan, and the
/// caller must call scan_start() again once the stack is back to ACTIVE
/// (the tracker's loop() reconciliation does exactly that).
/// no scan state across power cycles: a disable()/enable() cycle ends the
/// scan, and the caller must call scan_start() again once the stack is back
/// to ACTIVE (the tracker's loop() reconciliation does exactly that).
/// While a GATT connect attempt has the scan inhibited, the desired scan is
/// remembered and started physically when the inhibit is released.
bool scan_start(uint16_t interval, uint16_t window, bool active);
/// Stop the controller scan (no-op when not scanning).
void scan_stop();
#ifdef USE_BLE_GATT_CLIENT
/// Pause the physical scan for the duration of a GATT connect attempt
/// (initiating and scanning contend for the radio). The desired scan state
/// set through scan_start()/scan_stop() is remembered and reconciled by
/// release_scan_inhibit(). Holders must guarantee the release on every
/// abort path (the GATT engine reclaims via its connect timeout).
void inhibit_scan();
void release_scan_inhibit();
#endif
protected:
static void packet_handler(uint8_t type, uint16_t channel, uint8_t *packet, uint16_t size);
@@ -128,6 +140,16 @@ class RP2040BLE final : public Component {
bool enable_on_boot_{true};
bool btstack_initialized_{false};
bool active_logged_{false};
#ifdef USE_BLE_GATT_CLIENT
// Remembered scan intent, so connect attempts can pause the physical scan
// and restore it afterwards without involving the tracker. Counted so
// overlapping connect attempts compose once multiple slots exist.
uint16_t scan_interval_{0};
uint16_t scan_window_{0};
uint8_t scan_inhibit_count_{0};
bool scan_active_mode_{false};
bool scan_desired_{false};
#endif
};
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)