Skip to content

Commit

Permalink
Merge pull request #142 from taks/scan_api
Browse files Browse the repository at this point in the history
Changed scan API (breaking change)
  • Loading branch information
taks authored Sep 13, 2024
2 parents b88fb5d + e7ed6f1 commit 977a2f6
Show file tree
Hide file tree
Showing 10 changed files with 373 additions and 433 deletions.
15 changes: 11 additions & 4 deletions examples/ble_client.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use bstr::ByteSlice;
use esp32_nimble::{uuid128, BLEClient, BLEDevice};
use esp32_nimble::{uuid128, BLEClient, BLEDevice, BLEScan};
use esp_idf_svc::hal::{
prelude::Peripherals,
task::block_on,
Expand All @@ -15,20 +15,27 @@ fn main() -> anyhow::Result<()> {

block_on(async {
let ble_device = BLEDevice::take();
let ble_scan = ble_device.get_scan();
let mut ble_scan = BLEScan::new();
let device = ble_scan
.active_scan(true)
.interval(100)
.window(99)
.find_device(10000, |device| device.name().contains_str("ESP32"))
.start(ble_device, 10000, |device, data| {
if let Some(name) = data.name() {
if name.contains_str("ESP32") {
return Some(*device);
}
}
None
})
.await?;

if let Some(device) = device {
let mut client = BLEClient::new();
client.on_connect(|client| {
client.update_conn_params(120, 120, 0, 60).unwrap();
});
client.connect(device.addr()).await?;
client.connect(&device.addr()).await?;

let service = client
.get_service(uuid128!("fafafafa-fafa-fafa-fafa-fafafafafafa"))
Expand Down
19 changes: 10 additions & 9 deletions examples/ble_scan.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use esp32_nimble::BLEDevice;
use esp32_nimble::{BLEDevice, BLEScan};
use esp_idf_svc::hal::task::block_on;
use log::*;

Expand All @@ -10,15 +10,16 @@ fn main() -> anyhow::Result<()> {

block_on(async {
let ble_device = BLEDevice::take();
let ble_scan = ble_device.get_scan();
let mut ble_scan = BLEScan::new();
ble_scan.active_scan(true).interval(100).window(99);

ble_scan
.active_scan(true)
.interval(100)
.window(99)
.on_result(|_scan, param| {
info!("Advertised Device: {:?}", param);
});
ble_scan.start(5000).await?;
.start(ble_device, 5000, |device, data| {
info!("Advertised Device: ({:?}, {:?})", device, data);
None::<()>
})
.await?;

info!("Scan end");

Ok(())
Expand Down
13 changes: 8 additions & 5 deletions examples/ble_secure_client.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use esp32_nimble::{enums::*, utilities::BleUuid, BLEClient, BLEDevice};
use esp32_nimble::{enums::*, utilities::BleUuid, BLEClient, BLEDevice, BLEScan};
use esp_idf_svc::hal::task::block_on;
use log::*;

Expand All @@ -16,14 +16,17 @@ fn main() -> anyhow::Result<()> {
.set_auth(AuthReq::all())
.set_io_cap(SecurityIOCap::KeyboardOnly);

let ble_scan = device.get_scan();
let mut ble_scan = BLEScan::new();

let device = ble_scan
.active_scan(true)
.interval(100)
.window(99)
.find_device(10000, move |device| {
device.is_advertising_service(&SERVICE_UUID)
.start(device, 10000, |device, data| {
if data.is_advertising_service(&SERVICE_UUID) {
return Some(*device);
}
None
})
.await?;

Expand All @@ -35,7 +38,7 @@ fn main() -> anyhow::Result<()> {
info!("Advertised Device: {:?}", device);

let mut client = BLEClient::new();
client.connect(device.addr()).await?;
client.connect(&device.addr()).await?;
client.on_passkey_request(|| 123456);
client.secure_connection().await?;

Expand Down
14 changes: 1 addition & 13 deletions src/ble_device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ use esp_idf_svc::sys as esp_idf_sys;
use esp_idf_sys::{esp, esp_nofail, EspError};
use once_cell::sync::Lazy;

use crate::{
ble, client::BLEScan, enums::*, utilities::mutex::Mutex, BLEAddress, BLEError, BLESecurity,
BLEServer,
};
use crate::{ble, enums::*, utilities::mutex::Mutex, BLEAddress, BLEError, BLESecurity, BLEServer};

#[cfg(not(esp_idf_bt_nimble_ext_adv))]
type BLEAdvertising = crate::BLEAdvertising;
Expand Down Expand Up @@ -38,7 +35,6 @@ static mut BLE_DEVICE: Lazy<BLEDevice> = Lazy::new(|| {
security: BLESecurity::new(),
}
});
static mut BLE_SCAN: Lazy<BLEScan> = Lazy::new(BLEScan::new);
pub static mut BLE_SERVER: Lazy<BLEServer> = Lazy::new(BLEServer::new);
static BLE_ADVERTISING: Lazy<Mutex<BLEAdvertising>> =
Lazy::new(|| Mutex::new(BLEAdvertising::new()));
Expand Down Expand Up @@ -146,18 +142,10 @@ impl BLEDevice {
if let Some(server) = Lazy::get_mut(&mut BLE_SERVER) {
server.reset();
}

if let Some(scan) = Lazy::get_mut(&mut BLE_SCAN) {
scan.reset();
}
}
Ok(())
}

pub fn get_scan(&self) -> &'static mut BLEScan {
unsafe { Lazy::force_mut(&mut BLE_SCAN) }
}

pub fn get_server(&self) -> &'static mut BLEServer {
unsafe { Lazy::force_mut(&mut BLE_SERVER) }
}
Expand Down
211 changes: 211 additions & 0 deletions src/client/ble_advertised_data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
use alloc::vec::Vec;
use bstr::BStr;
use esp_idf_svc::sys;

use crate::{enums::AdvFlag, utilities::BleUuid};

pub struct BLEAdvertisedData<T>
where
T: AsRef<[u8]>,
{
payload: T,
}

impl<T: AsRef<[u8]>> BLEAdvertisedData<T> {
pub(crate) fn new(payload: T) -> Self {
Self { payload }
}

pub fn payload(&self) -> &[u8] {
self.payload.as_ref()
}

#[allow(clippy::should_implement_trait)]
pub fn clone(&self) -> BLEAdvertisedData<Vec<u8>> {
BLEAdvertisedData::<Vec<u8>>::new(self.payload().to_vec())
}

/// Get the advertisement flags.
pub fn adv_flags(&self) -> Option<AdvFlag> {
let data = self
.decode()
.find(|x| x.ty == (sys::BLE_HS_ADV_TYPE_FLAGS as _))?;

let ad_flag = data.data.first()?;
AdvFlag::from_bits(*ad_flag)
}

#[allow(unused_variables)]
pub fn is_advertising_service(&self, uuid: &BleUuid) -> bool {
todo!()
}

pub fn service_uuids(&self) -> Vec<BleUuid> {
let mut ret = Vec::new();
self.decode().for_each(|x| match x.ty as u32 {
sys::BLE_HS_ADV_TYPE_INCOMP_UUIDS16 | sys::BLE_HS_ADV_TYPE_COMP_UUIDS16 => {
let mut data = x.data;
while data.len() >= 2 {
let (uuid, data_) = data.split_at(2);
ret.push(BleUuid::from_uuid16(u16::from_le_bytes(
uuid.try_into().unwrap(),
)));
data = data_;
}
}
sys::BLE_HS_ADV_TYPE_INCOMP_UUIDS32 | sys::BLE_HS_ADV_TYPE_COMP_UUIDS32 => {
let mut data = x.data;
while data.len() >= 4 {
let (uuid, data_) = data.split_at(4);
ret.push(BleUuid::from_uuid32(u32::from_le_bytes(
uuid.try_into().unwrap(),
)));
data = data_;
}
}
sys::BLE_HS_ADV_TYPE_INCOMP_UUIDS128 | sys::BLE_HS_ADV_TYPE_COMP_UUIDS128 => {
if let Ok(data) = x.data.try_into() {
ret.push(BleUuid::Uuid128(data));
}
}
_ => {}
});
ret
}

pub fn name(&self) -> Option<&BStr> {
let data = self.decode().find(|x| {
x.ty == (sys::BLE_HS_ADV_TYPE_COMP_NAME as _)
|| x.ty == (sys::BLE_HS_ADV_TYPE_INCOMP_NAME as _)
})?;
Some(BStr::new(data.data))
}

pub fn tx_power(&self) -> Option<u8> {
let data = self
.decode()
.find(|x| x.ty == (sys::BLE_HS_ADV_TYPE_TX_PWR_LVL as _))?;

data.data.first().copied()
}

pub fn service_data(&self) -> Option<BLEServiceData> {
for x in self.decode() {
match x.ty as u32 {
sys::BLE_HS_ADV_TYPE_SVC_DATA_UUID16 => {
if let Some((uuid, service_data)) = x.data.split_at_checked(2) {
let uuid = BleUuid::from_uuid16(u16::from_le_bytes(uuid.try_into().unwrap()));
return Some(BLEServiceData { uuid, service_data });
} else {
::log::error!("Length too small for BLE_HS_ADV_TYPE_SVC_DATA_UUID16");
}
}
sys::BLE_HS_ADV_TYPE_SVC_DATA_UUID32 => {
if let Some((uuid, service_data)) = x.data.split_at_checked(4) {
let uuid = BleUuid::from_uuid32(u32::from_le_bytes(uuid.try_into().unwrap()));
return Some(BLEServiceData { uuid, service_data });
} else {
::log::error!("Length too small for BLE_HS_ADV_TYPE_SVC_DATA_UUID32");
}
}
sys::BLE_HS_ADV_TYPE_SVC_DATA_UUID128 => {
if let Some((uuid, service_data)) = x.data.split_at_checked(16) {
let uuid = BleUuid::from_uuid128(uuid.try_into().unwrap());
return Some(BLEServiceData { uuid, service_data });
} else {
::log::error!("Length too small for BLE_HS_ADV_TYPE_SVC_DATA_UUID128");
}
}
_ => {}
}
}
None
}

pub fn manufacture_data(&self) -> Option<ManufactureData> {
let data = self
.decode()
.find(|x| x.ty == (sys::BLE_HS_ADV_TYPE_MFG_DATA as _))?;

let (id, payload) = data.data.split_at_checked(2)?;
Some(ManufactureData {
company_identifier: u16::from_le_bytes(id.try_into().unwrap()),
payload,
})
}

pub fn test(&self) -> impl Iterator<Item = &u8> {
self.payload().iter().filter(|x| (**x) != 2)
}

fn decode(&self) -> AdStructureIter<'_> {
AdStructureIter {
payload: self.payload(),
}
}
}

impl<T: AsRef<[u8]>> core::fmt::Debug for BLEAdvertisedData<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut f = f.debug_struct("BLEAdvertisedData");
// f.field("types", &self.decode().map(|x| x.ty).collect::<Vec<_>>());
if let Some(adv_flags) = self.adv_flags() {
f.field("adv_flags", &adv_flags);
}
if let Some(name) = self.name() {
f.field("name", &name);
}
if let Some(tx_power) = self.tx_power() {
f.field("tx_power", &tx_power);
}
if let Some(manufacture_data) = self.manufacture_data() {
f.field("manufacture_data", &manufacture_data);
}
let service_uuids = self.service_uuids();
if !service_uuids.is_empty() {
f.field("service_uuids", &service_uuids);
}
if let Some(service_data) = self.service_data() {
f.field("service_data", &service_data);
}
f.finish()
}
}

#[derive(Debug, Copy, Clone)]
pub struct BLEServiceData<'a> {
pub uuid: BleUuid,
pub service_data: &'a [u8],
}

#[derive(Debug, Copy, Clone)]
pub struct ManufactureData<'a> {
pub company_identifier: u16,
pub payload: &'a [u8],
}

struct AdData<'d> {
ty: u8,
data: &'d [u8],
}

struct AdStructureIter<'d> {
payload: &'d [u8],
}

impl<'d> Iterator for AdStructureIter<'d> {
type Item = AdData<'d>;

fn next(&mut self) -> Option<Self::Item> {
let length = (*self.payload.first()?) as usize;
let (data, next_payload) = self.payload.split_at_checked(1 + length)?;
self.payload = next_payload;
if length == 0 {
return None;
}
return Some(AdData {
ty: unsafe { *data.get_unchecked(1) },
data: data.get(2..(length + 1)).unwrap(),
});
}
}
Loading

0 comments on commit 977a2f6

Please sign in to comment.