use core::cell::RefCell;
pub use bt_hci;
use bt_hci::param::LeAdvReportsIter;
use embassy_futures::select::{Either, select};
use embassy_time::{Duration, Timer};
use esp_hal::efuse::{self, InterfaceMacAddress};
use esp_hal::peripherals::BT;
use esp_radio::ble::controller::{BleConnector, BleConnectorError};
pub use trouble_host;
use trouble_host::prelude::{
AdStructure, Address, DefaultPacketPool, EventHandler, ExternalController, HostResources,
ScanConfig, Scanner, Stack,
};
pub const COMMAND_SLOTS: usize = 20;
pub type Controller<'d> = ExternalController<BleConnector<'d>, COMMAND_SLOTS>;
pub type InitError = esp_radio::ble::controller::BleInitError;
pub type HostError = trouble_host::BleHostError<BleConnectorError>;
pub type ScanResources<'d> = HostResources<Controller<'d>, DefaultPacketPool, 1, 1>;
pub fn controller<'d>(bt: BT<'d>) -> Result<Controller<'d>, InitError> {
Ok(ExternalController::new(BleConnector::new(
bt,
Default::default(),
)?))
}
pub fn address() -> Address {
let mac = efuse::interface_mac_address(InterfaceMacAddress::Bluetooth);
let mut le = [0u8; 6];
for (dst, src) in le.iter_mut().zip(mac.as_bytes().iter().rev()) {
*dst = *src; }
le[5] |= 0xC0;
Address::random(le)
}
pub fn stack<
'r,
'd,
const CONNS: usize,
const CHANNELS: usize,
const ADV: usize,
const BONDS: usize,
>(
controller: Controller<'d>,
resources: &'r mut HostResources<
Controller<'d>,
DefaultPacketPool,
CONNS,
CHANNELS,
ADV,
BONDS,
>,
) -> Stack<'r, Controller<'d>, DefaultPacketPool> {
trouble_host::new(controller, resources)
.set_random_address(address())
.build()
}
#[derive(Clone, Copy, Debug)]
pub struct Advertisement<'a> {
pub address: Address,
pub rssi: i8,
pub data: &'a [u8],
}
impl<'a> Advertisement<'a> {
pub fn name(&self) -> Option<&'a str> {
let mut short = None;
for ad in AdStructure::decode(self.data).flatten() {
match ad {
AdStructure::CompleteLocalName(n) => return core::str::from_utf8(n).ok(),
AdStructure::ShortenedLocalName(n) => short = core::str::from_utf8(n).ok(),
_ => {}
}
}
short
}
pub fn structures(&self) -> impl Iterator<Item = AdStructure<'a>> {
AdStructure::decode(self.data).flatten()
}
}
struct Reports<F>(RefCell<F>);
impl<F: FnMut(&Advertisement<'_>)> EventHandler for Reports<F> {
fn on_adv_reports(&self, mut reports: LeAdvReportsIter<'_>) {
let mut f = self.0.borrow_mut();
while let Some(Ok(r)) = reports.next() {
f(&Advertisement {
address: Address::new(r.addr_kind, r.addr),
rssi: r.rssi,
data: r.data,
});
}
}
}
pub async fn scan<'d>(
stack: &Stack<'_, Controller<'d>, DefaultPacketPool>,
duration: Duration,
on_advertisement: impl FnMut(&Advertisement<'_>),
) -> Result<(), HostError> {
let mut runner = stack.runner();
let mut scanner = Scanner::new(stack.central());
let reports = Reports(RefCell::new(on_advertisement));
let config = ScanConfig {
active: true,
interval: Duration::from_millis(100),
window: Duration::from_millis(100),
..Default::default()
};
let session = async {
let session = scanner.scan(&config).await?;
Timer::after(duration).await;
drop(session);
Timer::after(Duration::from_millis(100)).await;
Ok(())
};
match select(runner.run_with_handler(&reports), session).await {
Either::First(r) => r,
Either::Second(r) => r,
}
}