mirror of
https://github.com/esphome/esphome.git
synced 2026-08-22 22:26:21 +00:00
[rp2040_ble] Codegen sized StaticVector for scan listeners (#18058)
This commit is contained in:
@@ -31,6 +31,11 @@ def _validate_board(config: ConfigType) -> ConfigType:
|
||||
FINAL_VALIDATE_SCHEMA = _validate_board
|
||||
|
||||
|
||||
# Once per registered scan listener; sizes the controller's StaticVector
|
||||
# listener storage.
|
||||
request_scan_listener_slot = cg.slot_counter("RP2040_BLE_SCAN_LISTENER_COUNT")
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
@@ -117,8 +117,10 @@ void RP2040BLE::loop() {
|
||||
if (report == nullptr)
|
||||
return;
|
||||
do {
|
||||
#ifdef RP2040_BLE_SCAN_LISTENER_COUNT
|
||||
for (auto *listener : this->scan_listeners_)
|
||||
listener->on_scan_report(*report);
|
||||
#endif
|
||||
this->report_pool_.release(report);
|
||||
} while ((report = this->report_queue_.pop()) != nullptr);
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/event_pool.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/lock_free_queue.h"
|
||||
|
||||
#include <btstack.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace esphome::rp2040_ble {
|
||||
|
||||
@@ -79,8 +79,12 @@ class RP2040BLE final : public Component {
|
||||
/// power-up).
|
||||
void get_mac_msb_first(uint8_t out[6]) const;
|
||||
|
||||
#ifdef RP2040_BLE_SCAN_LISTENER_COUNT
|
||||
/// Register a consumer for scan reports (delivered on the main loop via loop()).
|
||||
/// Storage is codegen-sized: the consumer's codegen requests a slot via
|
||||
/// request_scan_listener_slot(), which emits RP2040_BLE_SCAN_LISTENER_COUNT.
|
||||
void register_scan_listener(BLEScanListener *listener) { this->scan_listeners_.push_back(listener); }
|
||||
#endif
|
||||
|
||||
/// Start a controller scan; active sends scan requests and receives scan
|
||||
/// responses as separate reports. Interval/window are in BLE units
|
||||
@@ -102,7 +106,11 @@ class RP2040BLE final : public Component {
|
||||
void enqueue_scan_report_(const uint8_t *mac_lsb_first, int8_t rssi, uint8_t addr_type, uint8_t adv_event_type,
|
||||
const uint8_t *data, uint16_t data_len);
|
||||
|
||||
std::vector<BLEScanListener *> scan_listeners_;
|
||||
#ifdef RP2040_BLE_SCAN_LISTENER_COUNT
|
||||
// Codegen-sized: no heap allocation, no std::vector template instantiation —
|
||||
// the same StaticVector pattern as the tracker's ble_device_base listeners.
|
||||
StaticVector<BLEScanListener *, RP2040_BLE_SCAN_LISTENER_COUNT> scan_listeners_;
|
||||
#endif
|
||||
// Report ring: the BTstack packet handler (async-context IRQ) allocates a
|
||||
// report from the pool, fills it and pushes the pointer; loop() pops,
|
||||
// dispatches and releases. Lock-free SPSC — the esp32_ble/bk72xx_ble pattern.
|
||||
|
||||
@@ -59,6 +59,9 @@ async def to_code(config: ConfigType) -> None:
|
||||
|
||||
parent = await cg.get_variable(config[CONF_RP2040_BLE_ID])
|
||||
cg.add(var.set_parent(parent))
|
||||
# The tracker registers itself as a controller scan listener in setup();
|
||||
# request the codegen-sized StaticVector slot for it.
|
||||
rp2040_ble.request_scan_listener_slot()
|
||||
|
||||
# Get notified when an OTA update starts, to pause scanning (esp32_ble_tracker parity)
|
||||
ota.request_ota_state_listeners()
|
||||
|
||||
@@ -436,6 +436,7 @@
|
||||
#define USE_LOGGER_USB_CDC
|
||||
#define USE_SOCKET_IMPL_LWIP_TCP
|
||||
#define USE_RP2040_BLE
|
||||
#define RP2040_BLE_SCAN_LISTENER_COUNT 1
|
||||
#define ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT 1
|
||||
#define USE_RP2040_VARIANT_RP2040
|
||||
#define USE_SPI
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
esphome:
|
||||
name: slotcount-rp2-controller
|
||||
|
||||
rp2:
|
||||
board: rpipicow
|
||||
|
||||
rp2040_ble:
|
||||
@@ -0,0 +1,7 @@
|
||||
esphome:
|
||||
name: slotcount-rp2-tracker
|
||||
|
||||
rp2:
|
||||
board: rpipicow
|
||||
|
||||
rp2_ble_tracker:
|
||||
@@ -11,12 +11,23 @@ from __future__ import annotations
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome.core import CORE
|
||||
|
||||
from ..helpers import get_define_value
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("config", "define"),
|
||||
[
|
||||
("bk72xx_tracker.yaml", "BK72XX_BLE_SCAN_LISTENER_COUNT"),
|
||||
("rp2_tracker.yaml", "RP2040_BLE_SCAN_LISTENER_COUNT"),
|
||||
],
|
||||
)
|
||||
def test_tracker_requests_one_slot(
|
||||
config: str,
|
||||
define: str,
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
) -> None:
|
||||
@@ -25,18 +36,27 @@ def test_tracker_requests_one_slot(
|
||||
The neutral listener count must stay absent from the same build: no BLE
|
||||
consumer registered through register_ble_device().
|
||||
"""
|
||||
generate_main(component_config_path("bk72xx_tracker.yaml"))
|
||||
assert get_define_value("BK72XX_BLE_SCAN_LISTENER_COUNT") == "1"
|
||||
generate_main(component_config_path(config))
|
||||
assert get_define_value(define) == "1"
|
||||
assert get_define_value("ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT") is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("config", "define"),
|
||||
[
|
||||
("bk72xx_controller_only.yaml", "BK72XX_BLE_SCAN_LISTENER_COUNT"),
|
||||
("rp2_controller_only.yaml", "RP2040_BLE_SCAN_LISTENER_COUNT"),
|
||||
],
|
||||
)
|
||||
def test_controller_only_emits_no_count(
|
||||
config: str,
|
||||
define: str,
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
) -> None:
|
||||
"""No consumer, no define — the guarded listener storage compiles out."""
|
||||
generate_main(component_config_path("bk72xx_controller_only.yaml"))
|
||||
assert get_define_value("BK72XX_BLE_SCAN_LISTENER_COUNT") is None
|
||||
generate_main(component_config_path(config))
|
||||
assert get_define_value(define) is None
|
||||
|
||||
|
||||
def test_neutral_listener_count_emitted_when_requested() -> None:
|
||||
|
||||
Reference in New Issue
Block a user