m5stack_core/driver/radio/ble.rs
1// SPDX-License-Identifier: MIT OR Apache-2.0
2//! BLE radio driver wrapper around `esp-radio`.
3//!
4//! Initializes the ESP32/ESP32-S3 radio coprocessor and returns a
5//! `BleConnector` for use with the `trouble-host` BLE stack.
6//!
7//! Ref: <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/bluetooth/index.html>
8use esp_hal::peripherals::BT;
9use esp_radio::ble::{Config, InvalidConfigError, controller::{BleConnector, BleInitError}};
10use thiserror_no_std::Error;
11
12#[derive(Debug, Error)]
13pub enum RadioError {
14 #[error("Failed to initialize BLE controller")]
15 BleInitError(#[from] BleInitError),
16
17 #[error("Failed to initialize BLE controller")]
18 BleConfigError(#[from] InvalidConfigError),
19}
20
21pub struct BleRadio {
22 pub ble_connector: BleConnector<'static>,
23}
24
25impl BleRadio {
26 /// Initialize BLE in scan-only mode with minimal buffer footprint.
27 ///
28 /// esp-radio's `Config::default()` sizes the BT-controller blob for
29 /// `max_connections=6` and `scan_duplicate_list_count=100`. On ESP32-S3
30 /// that defaults to ~36 KB of heap, which doesn't fit alongside LVGL on
31 /// cores3 (56 KB heap). Our use case is passive scanning of Victron
32 /// advertisements — we never open a connection, and only a handful of
33 /// distinct advertisers exist on the bus.
34 ///
35 /// `max_connections=1` is the minimum the controller accepts. Setting
36 /// it to 1 shrinks the per-connection static state. Combined with a
37 /// 10-entry duplicate-filter (the spec minimum, range 10–1000), this
38 /// frees ~10 KB on ESP32-S3 and a smaller amount on ESP32.
39 pub fn new(bt: BT<'static>) -> Result<Self, RadioError> {
40 // task_stack_size: BLE controller's own task stack (esp-radio default
41 // is 4096 B). The HIL hit a stack-guard watchpoint in
42 // `ld_sscan_frm_cbk` (controller's scan-frame callback): SP within
43 // 32 B of the per-task guard while a `r_rwbtdm_isr_wrapper` was
44 // nesting on top. Bumping to 8 KB gives the controller's deepest
45 // ISR-nested path room without resizing the main task stack region
46 // (which is pinned at [28, 80] KB by ESP_HAL_CONFIG_MAIN_STACK_*).
47 // Costs 4 KB of heap — well within budget on both targets.
48 let cfg = Config::default()
49 .with_task_priority(10)
50 .with_max_connections(1)
51 .with_scan_duplicate_list_count(10)
52 .with_task_stack_size(8192);
53 let ble_connector = BleConnector::new(bt, cfg)?;
54
55 Ok(Self { ble_connector })
56 }
57}