1use core::cell::RefCell;
29
30pub use bt_hci;
31use bt_hci::param::LeAdvReportsIter;
32use embassy_futures::select::{Either, select};
33use embassy_time::{Duration, Timer};
34use esp_hal::efuse::{self, InterfaceMacAddress};
35use esp_hal::peripherals::BT;
36use esp_radio::ble::controller::{BleConnector, BleConnectorError};
37pub use trouble_host;
38use trouble_host::prelude::{
39 AdStructure, Address, DefaultPacketPool, EventHandler, ExternalController, HostResources,
40 ScanConfig, Scanner, Stack,
41};
42
43pub const COMMAND_SLOTS: usize = 20;
46
47pub type Controller<'d> = ExternalController<BleConnector<'d>, COMMAND_SLOTS>;
49
50pub type InitError = esp_radio::ble::controller::BleInitError;
52
53pub type HostError = trouble_host::BleHostError<BleConnectorError>;
55
56pub type ScanResources<'d> = HostResources<Controller<'d>, DefaultPacketPool, 1, 1>;
59
60pub fn controller<'d>(bt: BT<'d>) -> Result<Controller<'d>, InitError> {
62 Ok(ExternalController::new(BleConnector::new(
63 bt,
64 Default::default(),
65 )?))
66}
67
68pub fn address() -> Address {
72 let mac = efuse::interface_mac_address(InterfaceMacAddress::Bluetooth);
73 let mut le = [0u8; 6];
74 for (dst, src) in le.iter_mut().zip(mac.as_bytes().iter().rev()) {
75 *dst = *src; }
77 le[5] |= 0xC0;
78 Address::random(le)
79}
80
81pub fn stack<
83 'r,
84 'd,
85 const CONNS: usize,
86 const CHANNELS: usize,
87 const ADV: usize,
88 const BONDS: usize,
89>(
90 controller: Controller<'d>,
91 resources: &'r mut HostResources<
92 Controller<'d>,
93 DefaultPacketPool,
94 CONNS,
95 CHANNELS,
96 ADV,
97 BONDS,
98 >,
99) -> Stack<'r, Controller<'d>, DefaultPacketPool> {
100 trouble_host::new(controller, resources)
101 .set_random_address(address())
102 .build()
103}
104
105#[derive(Clone, Copy, Debug)]
107pub struct Advertisement<'a> {
108 pub address: Address,
110 pub rssi: i8,
112 pub data: &'a [u8],
114}
115
116impl<'a> Advertisement<'a> {
117 pub fn name(&self) -> Option<&'a str> {
121 let mut short = None;
122 for ad in AdStructure::decode(self.data).flatten() {
123 match ad {
124 AdStructure::CompleteLocalName(n) => return core::str::from_utf8(n).ok(),
125 AdStructure::ShortenedLocalName(n) => short = core::str::from_utf8(n).ok(),
126 _ => {}
127 }
128 }
129 short
130 }
131
132 pub fn structures(&self) -> impl Iterator<Item = AdStructure<'a>> {
134 AdStructure::decode(self.data).flatten()
135 }
136}
137
138struct Reports<F>(RefCell<F>);
139
140impl<F: FnMut(&Advertisement<'_>)> EventHandler for Reports<F> {
141 fn on_adv_reports(&self, mut reports: LeAdvReportsIter<'_>) {
142 let mut f = self.0.borrow_mut();
143 while let Some(Ok(r)) = reports.next() {
144 f(&Advertisement {
145 address: Address::new(r.addr_kind, r.addr),
146 rssi: r.rssi,
147 data: r.data,
148 });
149 }
150 }
151}
152
153pub async fn scan<'d>(
159 stack: &Stack<'_, Controller<'d>, DefaultPacketPool>,
160 duration: Duration,
161 on_advertisement: impl FnMut(&Advertisement<'_>),
162) -> Result<(), HostError> {
163 let mut runner = stack.runner();
164 let mut scanner = Scanner::new(stack.central());
165 let reports = Reports(RefCell::new(on_advertisement));
166 let config = ScanConfig {
167 active: true,
168 interval: Duration::from_millis(100),
169 window: Duration::from_millis(100),
170 ..Default::default()
171 };
172 let session = async {
173 let session = scanner.scan(&config).await?;
174 Timer::after(duration).await;
175 drop(session);
176 Timer::after(Duration::from_millis(100)).await;
179 Ok(())
180 };
181 match select(runner.run_with_handler(&reports), session).await {
182 Either::First(r) => r,
183 Either::Second(r) => r,
184 }
185}