Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Added extended advertising scan support #141

Merged
merged 2 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions examples/ble5_multi_advertiser.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use esp_idf_svc::sys::{BLE_HCI_LE_PHY_1M, BLE_HCI_LE_PHY_CODED};

use esp32_nimble::{
utilities::BleUuid, BLEAddress, BLEAddressType, BLEDevice, BLEExtAdvertisement, NimbleProperties,
enums::{PrimPhy, SecPhy},
utilities::BleUuid,
BLEAddress, BLEAddressType, BLEDevice, BLEExtAdvertisement, NimbleProperties,
};

const SERVICE_UUID: BleUuid = BleUuid::Uuid16(0xABCD);
Expand All @@ -20,16 +20,14 @@ fn main() -> anyhow::Result<()> {
.create_characteristic(BleUuid::Uuid16(0x1234), NimbleProperties::READ);
characteristic.lock().set_value("Hello, world!".as_bytes());

let mut ext_scannable =
BLEExtAdvertisement::new(BLE_HCI_LE_PHY_CODED as _, BLE_HCI_LE_PHY_1M as _);
let mut ext_scannable = BLEExtAdvertisement::new(PrimPhy::Coded, SecPhy::Phy1M);
ext_scannable.scannable(true);
ext_scannable.connectable(false);

ext_scannable.service_data(SERVICE_UUID, "Scan me!".as_bytes());
ext_scannable.enable_scan_request_callback(true);

let mut legacy_connectable =
BLEExtAdvertisement::new(BLE_HCI_LE_PHY_1M as _, BLE_HCI_LE_PHY_1M as _);
let mut legacy_connectable = BLEExtAdvertisement::new(PrimPhy::Phy1M, SecPhy::Phy1M);
legacy_connectable.address(&BLEAddress::from_be_bytes(
[0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD],
BLEAddressType::Random,
Expand All @@ -41,8 +39,7 @@ fn main() -> anyhow::Result<()> {
legacy_connectable.legacy_advertising(true);
legacy_connectable.connectable(true);

let mut legacy_scan_response =
BLEExtAdvertisement::new(BLE_HCI_LE_PHY_1M as _, BLE_HCI_LE_PHY_1M as _);
let mut legacy_scan_response = BLEExtAdvertisement::new(PrimPhy::Phy1M, SecPhy::Phy1M);
legacy_scan_response.service_data(SERVICE_UUID, "Legacy SR".as_bytes());

{
Expand Down
73 changes: 68 additions & 5 deletions src/client/ble_advertised_device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use alloc::vec::Vec;
use bstr::{BStr, BString};
use esp_idf_svc::sys as esp_idf_sys;

use crate::enums::{AdvFlag, AdvType};
use crate::enums::*;
use crate::utilities::BleUuid;
use crate::BLEAddress;

Expand Down Expand Up @@ -34,17 +34,50 @@ pub struct BLEAdvertisedDevice {
service_data_list: Vec<BLEServiceData>,
tx_power: Option<u8>,
manufacture_data: Option<Vec<u8>>,

#[cfg(esp_idf_bt_nimble_ext_adv)]
is_legacy_adv: bool,
#[cfg(esp_idf_bt_nimble_ext_adv)]
sid: u8,
#[cfg(esp_idf_bt_nimble_ext_adv)]
prim_phy: PrimPhy,
#[cfg(esp_idf_bt_nimble_ext_adv)]
sec_phy: Option<SecPhy>,
#[cfg(esp_idf_bt_nimble_ext_adv)]
periodic_itvl: u16,
}

impl BLEAdvertisedDevice {
pub(crate) fn new(param: &esp_idf_sys::ble_gap_disc_desc) -> Self {
#[cfg(esp_idf_bt_nimble_ext_adv)]
pub(crate) fn new(disc: &esp_idf_sys::ble_gap_ext_disc_desc, event_type: u8) -> Self {
Self {
addr: disc.addr.into(),
adv_type: AdvType::try_from(event_type).unwrap(),
adv_flags: None,
appearance: None,
name: BString::default(),
rssi: disc.rssi as _,
service_uuids: Vec::new(),
service_data_list: Vec::new(),
tx_power: None,
manufacture_data: None,
is_legacy_adv: (disc.props & (esp_idf_sys::BLE_HCI_ADV_LEGACY_MASK as u8)) != 0,
sid: disc.sid,
prim_phy: PrimPhy::try_from(disc.prim_phy).unwrap(),
sec_phy: SecPhy::try_from(disc.sec_phy).ok(),
periodic_itvl: disc.periodic_adv_itvl,
}
}

#[cfg(not(esp_idf_bt_nimble_ext_adv))]
pub(crate) fn new(disc: &esp_idf_sys::ble_gap_disc_desc, _event_type: u8) -> Self {
Self {
addr: param.addr.into(),
adv_type: AdvType::try_from(param.event_type).unwrap(),
addr: disc.addr.into(),
adv_type: AdvType::try_from(disc.event_type).unwrap(),
adv_flags: None,
appearance: None,
name: BString::default(),
rssi: param.rssi as _,
rssi: disc.rssi as _,
service_uuids: Vec::new(),
service_data_list: Vec::new(),
tx_power: None,
Expand Down Expand Up @@ -99,6 +132,36 @@ impl BLEAdvertisedDevice {
self.manufacture_data.as_deref()
}

#[cfg(esp_idf_bt_nimble_ext_adv)]
/// Check if this advertisement is a legacy or extended type
pub fn is_legacy_advertisement(&self) -> bool {
self.is_legacy_adv
}

#[cfg(esp_idf_bt_nimble_ext_adv)]
/// Get the set ID of the extended advertisement.
pub fn sid(&self) -> u8 {
self.sid
}

#[cfg(esp_idf_bt_nimble_ext_adv)]
/// Get the primary PHY used by this advertisement.
pub fn prim_phy(&self) -> PrimPhy {
self.prim_phy
}

#[cfg(esp_idf_bt_nimble_ext_adv)]
/// Get the secondary PHY used by this advertisement.
pub fn sec_phy(&self) -> Option<SecPhy> {
self.sec_phy
}

#[cfg(esp_idf_bt_nimble_ext_adv)]
/// Get the periodic interval of the advertisement.
pub fn periodic_itvl(&self) -> u16 {
self.periodic_itvl
}

pub(crate) fn parse_advertisement(&mut self, payload: &[u8]) {
let mut payload = payload;

Expand Down
55 changes: 48 additions & 7 deletions src/client/ble_scan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ impl BLEScan {
itvl: 0,
window: 0,
filter_policy: esp_idf_sys::BLE_HCI_SCAN_FILT_NO_WL as _,
_bitfield_align_1: [0; 0],
_bitfield_1: esp_idf_sys::__BindgenBitfieldUnit::new([0; 1]),
..Default::default()
},
stopped: true,
scan_results: Vec::new(),
Expand Down Expand Up @@ -136,6 +135,32 @@ impl BLEScan {
callback: Option<&mut (dyn FnMut(&mut Self, &BLEAdvertisedDevice) + Send + Sync)>,
) -> Result<(), BLEError> {
let cb_arg = (self, callback);

#[cfg(esp_idf_bt_nimble_ext_adv)]
{
let mut scan_params = esp_idf_sys::ble_gap_ext_disc_params {
itvl: cb_arg.0.scan_params.itvl,
window: cb_arg.0.scan_params.window,
..Default::default()
};
scan_params.set_passive(cb_arg.0.scan_params.passive());
unsafe {
ble!(esp_idf_sys::ble_gap_ext_disc(
crate::ble_device::OWN_ADDR_TYPE as _,
(duration_ms / 10) as _,
0,
cb_arg.0.scan_params.filter_duplicates(),
cb_arg.0.scan_params.filter_policy,
cb_arg.0.scan_params.limited(),
&scan_params,
&scan_params,
Some(Self::handle_gap_event),
core::ptr::addr_of!(cb_arg) as _,
))?;
}
}

#[cfg(not(esp_idf_bt_nimble_ext_adv))]
unsafe {
ble!(esp_idf_sys::ble_gap_disc(
crate::ble_device::OWN_ADDR_TYPE as _,
Expand All @@ -145,8 +170,8 @@ impl BLEScan {
core::ptr::addr_of!(cb_arg) as _,
))?;
}
cb_arg.0.stopped = false;

cb_arg.0.stopped = false;
cb_arg.0.signal.wait().await;
Ok(())
}
Expand Down Expand Up @@ -185,23 +210,38 @@ impl BLEScan {

match event.type_ as u32 {
esp_idf_sys::BLE_GAP_EVENT_EXT_DISC | esp_idf_sys::BLE_GAP_EVENT_DISC => {
#[cfg(esp_idf_bt_nimble_ext_adv)]
let disc = unsafe { &event.__bindgen_anon_1.ext_disc };
#[cfg(esp_idf_bt_nimble_ext_adv)]
let is_legacy_adv = (disc.props & (esp_idf_sys::BLE_HCI_ADV_LEGACY_MASK as u8)) != 0;
#[cfg(esp_idf_bt_nimble_ext_adv)]
let event_type = if is_legacy_adv {
disc.legacy_event_type
} else {
disc.props
};

#[cfg(not(esp_idf_bt_nimble_ext_adv))]
let disc = unsafe { &event.__bindgen_anon_1.disc };
#[cfg(not(esp_idf_bt_nimble_ext_adv))]
let is_legacy_adv = true;
#[cfg(not(esp_idf_bt_nimble_ext_adv))]
let event_type = disc.event_type;

let mut advertised_device = scan
.scan_results
.iter_mut()
.find(|x| x.addr().value.val == disc.addr.val);

if advertised_device.is_none() {
if disc.event_type != esp_idf_sys::BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP as _ {
let device = BLEAdvertisedDevice::new(disc);
if !is_legacy_adv || event_type != esp_idf_sys::BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP as _ {
let device = BLEAdvertisedDevice::new(disc, event_type);
scan.scan_results.push(device);
advertised_device = scan.scan_results.last_mut();
} else {
return 0;
}
}

let advertised_device = advertised_device.unwrap();

let data = unsafe { core::slice::from_raw_parts(disc.data, disc.length_data as _) };
Expand All @@ -212,9 +252,10 @@ impl BLEScan {

if let Some(callback) = on_result {
if scan.scan_params.passive() != 0
|| !is_legacy_adv
|| (advertised_device.adv_type() != AdvType::Ind
&& advertised_device.adv_type() != AdvType::ScanInd)
|| disc.event_type == esp_idf_sys::BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP as _
|| event_type == esp_idf_sys::BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP as _
{
let (scan, _) = unsafe { voidp_to_ref::<CbArgType>(arg) };
callback(scan, advertised_device);
Expand Down
20 changes: 20 additions & 0 deletions src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,23 @@ pub enum AdvFilterPolicy {
/// only allow scan/connections from those on the white list.
Both = BLE_HCI_ADV_FILT_BOTH as _,
}

#[repr(u8)]
#[derive(Copy, Clone, PartialEq, Debug, TryFromPrimitive, IntoPrimitive)]
pub enum PrimPhy {
/// 1Mbps phy
Phy1M = BLE_HCI_LE_PHY_1M as _,
/// Coded phy
Coded = BLE_HCI_LE_PHY_CODED as _,
}

#[repr(u8)]
#[derive(Copy, Clone, PartialEq, Debug, TryFromPrimitive, IntoPrimitive)]
pub enum SecPhy {
/// 1Mbps phy
Phy1M = BLE_HCI_LE_PHY_1M as _,
/// 2Mbps phy
Phy2M = BLE_HCI_LE_PHY_2M as _,
/// Coded phy
Coded = BLE_HCI_LE_PHY_CODED as _,
}
6 changes: 3 additions & 3 deletions src/server/ble_ext_advertising.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ pub struct BLEExtAdvertisement {
}

impl BLEExtAdvertisement {
pub fn new(primary_phy: u8, secondary_phy: u8) -> Self {
pub fn new(primary_phy: PrimPhy, secondary_phy: SecPhy) -> Self {
Self {
payload: Vec::new(),
params: esp_idf_sys::ble_gap_ext_adv_params {
own_addr_type: unsafe { crate::ble_device::OWN_ADDR_TYPE as _ },
primary_phy,
secondary_phy,
primary_phy: primary_phy.into(),
secondary_phy: secondary_phy.into(),
tx_power: 127,
..Default::default()
},
Expand Down