Skip to content

Add Appearance Capability to Advertisement Builder (and refactor) #289

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion examples/src/bin/ble_advertise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ async fn main(spawner: Spawner) {
config.interval = 50;

static ADV_DATA: LegacyAdvertisementPayload = LegacyAdvertisementBuilder::new()
.flags(&[Flag::GeneralDiscovery, Flag::LE_Only])
.flags(&[Flag::GENERAL_DISCOVERY, Flag::LE_ONLY])
.services_16(ServiceList::Complete, &[ServiceUuid16::HEALTH_THERMOMETER]) // if there were a lot of these there may not be room for the full name
.short_name("Hello")
.build();
Expand Down
2 changes: 1 addition & 1 deletion examples/src/bin/ble_bas_peripheral.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ async fn main(spawner: Spawner) {
unwrap!(spawner.spawn(softdevice_task(sd)));

static ADV_DATA: LegacyAdvertisementPayload = LegacyAdvertisementBuilder::new()
.flags(&[Flag::GeneralDiscovery, Flag::LE_Only])
.flags(&[Flag::GENERAL_DISCOVERY, Flag::LE_ONLY])
.services_16(ServiceList::Complete, &[ServiceUuid16::BATTERY])
.full_name("HelloRust")
.build();
Expand Down
2 changes: 1 addition & 1 deletion examples/src/bin/ble_bas_peripheral_notify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ async fn main(spawner: Spawner) {
unwrap!(spawner.spawn(softdevice_task(sd)));

static ADV_DATA: LegacyAdvertisementPayload = LegacyAdvertisementBuilder::new()
.flags(&[Flag::GeneralDiscovery, Flag::LE_Only])
.flags(&[Flag::GENERAL_DISCOVERY, Flag::LE_ONLY])
.services_16(ServiceList::Complete, &[ServiceUuid16::BATTERY])
.full_name("HelloRust")
.build();
Expand Down
2 changes: 1 addition & 1 deletion examples/src/bin/ble_bond_peripheral.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ async fn main(spawner: Spawner) -> ! {
unwrap!(spawner.spawn(softdevice_task(sd)));

static ADV_DATA: LegacyAdvertisementPayload = LegacyAdvertisementBuilder::new()
.flags(&[Flag::GeneralDiscovery, Flag::LE_Only])
.flags(&[Flag::GENERAL_DISCOVERY, Flag::LE_ONLY])
.services_16(ServiceList::Complete, &[ServiceUuid16::BATTERY])
.full_name("HelloRust")
.build();
Expand Down
2 changes: 1 addition & 1 deletion examples/src/bin/ble_dis_bas_peripheral_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ async fn main(spawner: Spawner) {
unwrap!(spawner.spawn(softdevice_task(sd)));

static ADV_DATA: LegacyAdvertisementPayload = LegacyAdvertisementBuilder::new()
.flags(&[Flag::GeneralDiscovery, Flag::LE_Only])
.flags(&[Flag::GENERAL_DISCOVERY, Flag::LE_ONLY])
.services_16(ServiceList::Incomplete, &[ServiceUuid16::BATTERY])
.full_name("HelloRust")
.build();
Expand Down
2 changes: 1 addition & 1 deletion examples/src/bin/ble_keyboard_peripheral_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ async fn main(spawner: Spawner) {
unwrap!(spawner.spawn(softdevice_task(sd)));

static ADV_DATA: LegacyAdvertisementPayload = LegacyAdvertisementBuilder::new()
.flags(&[Flag::GeneralDiscovery, Flag::LE_Only])
.flags(&[Flag::GENERAL_DISCOVERY, Flag::LE_ONLY])
.services_16(
ServiceList::Incomplete,
&[ServiceUuid16::BATTERY, ServiceUuid16::HUMAN_INTERFACE_DEVICE],
Expand Down
2 changes: 1 addition & 1 deletion examples/src/bin/ble_keyboard_peripheral_builder_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ async fn main(spawner: Spawner) {
unwrap!(spawner.spawn(softdevice_task(sd)));

static ADV_DATA: LegacyAdvertisementPayload = LegacyAdvertisementBuilder::new()
.flags(&[Flag::GeneralDiscovery, Flag::LE_Only])
.flags(&[Flag::GENERAL_DISCOVERY, Flag::LE_ONLY])
.services_16(
ServiceList::Incomplete,
&[ServiceUuid16::BATTERY, ServiceUuid16::HUMAN_INTERFACE_DEVICE],
Expand Down
2 changes: 1 addition & 1 deletion examples/src/bin/ble_l2cap_peripheral.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ async fn main(spawner: Spawner) {
info!("My address: {:?}", ble::get_address(sd));

static ADV_DATA: LegacyAdvertisementPayload = LegacyAdvertisementBuilder::new()
.flags(&[Flag::GeneralDiscovery, Flag::LE_Only])
.flags(&[Flag::GENERAL_DISCOVERY, Flag::LE_ONLY])
.services_128(
ServiceList::Complete,
&[[
Expand Down
2 changes: 1 addition & 1 deletion examples/src/bin/ble_peripheral_onoff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ struct Server {

async fn run_bluetooth(sd: &'static Softdevice, server: &Server) {
static ADV_DATA: LegacyAdvertisementPayload = LegacyAdvertisementBuilder::new()
.flags(&[Flag::GeneralDiscovery, Flag::LE_Only])
.flags(&[Flag::GENERAL_DISCOVERY, Flag::LE_ONLY])
.full_name("HelloRust")
.build();

Expand Down
184 changes: 17 additions & 167 deletions nrf-softdevice/src/ble/advertisement_builder.rs
Original file line number Diff line number Diff line change
@@ -1,184 +1,29 @@
#[cfg(feature = "defmt")]
use defmt::Format;
pub mod ad;
pub mod appearance;
pub mod flag;
pub mod service_uuid16;

pub use ad::AdvertisementDataType;
pub use appearance::Appearance;
pub use flag::Flag;
pub use service_uuid16::ServiceUuid16;

const LEGACY_PAYLOAD_LEN: usize = 31;
const EXTENDED_PAYLOAD_LEN: usize = 254;

#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(Format))]
#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
pub enum Error {
Oversize { expected: usize },
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(Format))]
pub struct AdvertisementDataType(u8);

impl AdvertisementDataType {
pub const FLAGS: AdvertisementDataType = AdvertisementDataType(0x01);
pub const INCOMPLETE_16_SERVICE_LIST: AdvertisementDataType = AdvertisementDataType(0x02);
pub const COMPLETE_16_SERVICE_LIST: AdvertisementDataType = AdvertisementDataType(0x03);
pub const INCOMPLETE_32_SERVICE_LIST: AdvertisementDataType = AdvertisementDataType(0x04);
pub const COMPLETE_32_SERVICE_LIST: AdvertisementDataType = AdvertisementDataType(0x05);
pub const INCOMPLETE_128_SERVICE_LIST: AdvertisementDataType = AdvertisementDataType(0x06);
pub const COMPLETE_128_SERVICE_LIST: AdvertisementDataType = AdvertisementDataType(0x07);
pub const SHORT_NAME: AdvertisementDataType = AdvertisementDataType(0x08);
pub const FULL_NAME: AdvertisementDataType = AdvertisementDataType(0x09);
pub const TXPOWER_LEVEL: AdvertisementDataType = AdvertisementDataType(0x0a);
pub const PERIPHERAL_CONNECTION_INTERVAL_RANGE: AdvertisementDataType = AdvertisementDataType(0x12);
pub const SERVICE_SOLICITATION_16: AdvertisementDataType = AdvertisementDataType(0x14);
pub const SERVICE_SOLICITATION_128: AdvertisementDataType = AdvertisementDataType(0x15);
pub const SERVICE_SOLICITATION_32: AdvertisementDataType = AdvertisementDataType(0x1f);
pub const SERVICE_DATA_16: AdvertisementDataType = AdvertisementDataType(0x16);
pub const SERVICE_DATA_32: AdvertisementDataType = AdvertisementDataType(0x20);
pub const SERVICE_DATA_128: AdvertisementDataType = AdvertisementDataType(0x21);
pub const APPEARANCE: AdvertisementDataType = AdvertisementDataType(0x19);
pub const PUBLIC_TARGET_ADDRESS: AdvertisementDataType = AdvertisementDataType(0x17);
pub const RANDOM_TARGET_ADDRESS: AdvertisementDataType = AdvertisementDataType(0x18);
pub const ADVERTISING_INTERVAL: AdvertisementDataType = AdvertisementDataType(0x1a);
pub const URI: AdvertisementDataType = AdvertisementDataType(0x24);
pub const LE_SUPPORTED_FEATURES: AdvertisementDataType = AdvertisementDataType(0x27);
pub const MANUFACTURER_SPECIFIC_DATA: AdvertisementDataType = AdvertisementDataType(0xff);

pub const fn from_u8(value: u8) -> Self {
AdvertisementDataType(value)
}

pub const fn to_u8(self) -> u8 {
self.0
}
}

impl From<u8> for AdvertisementDataType {
fn from(value: u8) -> Self {
AdvertisementDataType(value)
}
}

impl From<AdvertisementDataType> for u8 {
fn from(value: AdvertisementDataType) -> Self {
value.0
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(Format))]
#[repr(u8)]
pub enum Flag {
LimitedDiscovery = 0b1,
GeneralDiscovery = 0b10,
#[allow(non_camel_case_types)]
LE_Only = 0b100,

// i don't understand these but in case people want them
Bit3 = 0b1000,
Bit4 = 0b10000,
// the rest are "reserved for future use"
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(Format))]
#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
pub enum ServiceList {
Incomplete,
Complete,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(Format))]
pub struct ServiceUuid16(u16);

impl ServiceUuid16 {
pub const GENERIC_ACCESS: ServiceUuid16 = ServiceUuid16(0x1800);
pub const GENERIC_ATTRIBUTE: ServiceUuid16 = ServiceUuid16(0x1801);
pub const IMMEDIATE_ALERT: ServiceUuid16 = ServiceUuid16(0x1802);
pub const LINK_LOSS: ServiceUuid16 = ServiceUuid16(0x1803);
pub const TX_POWER: ServiceUuid16 = ServiceUuid16(0x1804);
pub const CURRENT_TIME: ServiceUuid16 = ServiceUuid16(0x1805);
pub const REFERENCE_TIME_UPDATE: ServiceUuid16 = ServiceUuid16(0x1806);
pub const NEXT_DST_CHANGE: ServiceUuid16 = ServiceUuid16(0x1807);
pub const GLUCOSE: ServiceUuid16 = ServiceUuid16(0x1808);
pub const HEALTH_THERMOMETER: ServiceUuid16 = ServiceUuid16(0x1809);
pub const DEVICE_INFORMATION: ServiceUuid16 = ServiceUuid16(0x180A);
pub const HEART_RATE: ServiceUuid16 = ServiceUuid16(0x180D);
pub const PHONE_ALERT_STATUS: ServiceUuid16 = ServiceUuid16(0x180E);
pub const BATTERY: ServiceUuid16 = ServiceUuid16(0x180F);
pub const BLOOD_PRESSURE: ServiceUuid16 = ServiceUuid16(0x1810);
pub const ALERT_NOTIFICATION: ServiceUuid16 = ServiceUuid16(0x1811);
pub const HUMAN_INTERFACE_DEVICE: ServiceUuid16 = ServiceUuid16(0x1812);
pub const SCAN_PARAMETERS: ServiceUuid16 = ServiceUuid16(0x1813);
pub const RUNNNIG_SPEED_AND_CADENCE: ServiceUuid16 = ServiceUuid16(0x1814);
pub const AUTOMATION_IO: ServiceUuid16 = ServiceUuid16(0x1815);
pub const CYCLING_SPEED_AND_CADENCE: ServiceUuid16 = ServiceUuid16(0x1816);
pub const CYCLING_POWER: ServiceUuid16 = ServiceUuid16(0x1818);
pub const LOCATION_AND_NAVIGATION: ServiceUuid16 = ServiceUuid16(0x1819);
pub const ENVIRONMENTAL_SENSING: ServiceUuid16 = ServiceUuid16(0x181A);
pub const BODY_COMPOSITION: ServiceUuid16 = ServiceUuid16(0x181B);
pub const USER_DATA: ServiceUuid16 = ServiceUuid16(0x181C);
pub const WEIGHT_SCALE: ServiceUuid16 = ServiceUuid16(0x181D);
pub const BOND_MANAGEMENT: ServiceUuid16 = ServiceUuid16(0x181E);
pub const CONTINOUS_GLUCOSE_MONITORING: ServiceUuid16 = ServiceUuid16(0x181F);
pub const INTERNET_PROTOCOL_SUPPORT: ServiceUuid16 = ServiceUuid16(0x1820);
pub const INDOOR_POSITIONING: ServiceUuid16 = ServiceUuid16(0x1821);
pub const PULSE_OXIMETER: ServiceUuid16 = ServiceUuid16(0x1822);
pub const HTTP_PROXY: ServiceUuid16 = ServiceUuid16(0x1823);
pub const TRANSPORT_DISCOVERY: ServiceUuid16 = ServiceUuid16(0x1824);
pub const OBJECT_TRANSFER: ServiceUuid16 = ServiceUuid16(0x1825);
pub const FITNESS_MACHINE: ServiceUuid16 = ServiceUuid16(0x1826);
pub const MESH_PROVISIONING: ServiceUuid16 = ServiceUuid16(0x1827);
pub const MESH_PROXY: ServiceUuid16 = ServiceUuid16(0x1828);
pub const RECONNECTION_CONFIGURATION: ServiceUuid16 = ServiceUuid16(0x1829);
pub const INSULIN_DELIVERY: ServiceUuid16 = ServiceUuid16(0x183A);
pub const BINARY_SENSOR: ServiceUuid16 = ServiceUuid16(0x183B);
pub const EMERGENCY_CONFIGURATION: ServiceUuid16 = ServiceUuid16(0x183C);
pub const AUTHORIZATION_CONTROL: ServiceUuid16 = ServiceUuid16(0x183D);
pub const PHYSICAL_ACTIVITY_MONITOR: ServiceUuid16 = ServiceUuid16(0x183E);
pub const ELAPSED_TIME: ServiceUuid16 = ServiceUuid16(0x183F);
pub const GENERIC_HEALTH_SENSOR: ServiceUuid16 = ServiceUuid16(0x1840);
pub const AUDIO_INPUT_CONTROL: ServiceUuid16 = ServiceUuid16(0x1843);
pub const VOLUME_CONTROL: ServiceUuid16 = ServiceUuid16(0x1844);
pub const VOLUME_OFFSET_CONTROL: ServiceUuid16 = ServiceUuid16(0x1845);
pub const COORDINATED_SET_IDENTIFICATION: ServiceUuid16 = ServiceUuid16(0x1846);
pub const DEVICE_TIME: ServiceUuid16 = ServiceUuid16(0x1847);
pub const MEDIA_CONTROL: ServiceUuid16 = ServiceUuid16(0x1848);
pub const GENERIC_MEDIA_CONTROL: ServiceUuid16 = ServiceUuid16(0x1849);
pub const CONSTANT_TONE_EXTENSION: ServiceUuid16 = ServiceUuid16(0x184A);
pub const TELEPHONE_BEARER: ServiceUuid16 = ServiceUuid16(0x184B);
pub const GENERIC_TELEPHONE_BEARER: ServiceUuid16 = ServiceUuid16(0x184C);
pub const MICROPHONE_CONTROL: ServiceUuid16 = ServiceUuid16(0x184D);
pub const AUDIO_STREAM_CONTROL: ServiceUuid16 = ServiceUuid16(0x184E);
pub const BROADCAST_AUDIO_SCAN: ServiceUuid16 = ServiceUuid16(0x184F);
pub const PUBLISHED_AUDIO_SCAN: ServiceUuid16 = ServiceUuid16(0x1850);
pub const BASIC_AUDIO_CAPABILITIES: ServiceUuid16 = ServiceUuid16(0x1851);
pub const BROADCAST_AUDIO_ANNOUNCEMENT: ServiceUuid16 = ServiceUuid16(0x1852);
pub const COMMON_AUDIO: ServiceUuid16 = ServiceUuid16(0x1853);
pub const HEARING_ACCESS: ServiceUuid16 = ServiceUuid16(0x1854);
pub const TELEPHONY_AND_MEDIA_AUDIO: ServiceUuid16 = ServiceUuid16(0x1855);
pub const PUBLIC_BROADCAST_ANNOUNCEMENT: ServiceUuid16 = ServiceUuid16(0x1856);
pub const ELECTRONIC_SHELF_LABEL: ServiceUuid16 = ServiceUuid16(0x1847);
pub const GAMING_AUDIO: ServiceUuid16 = ServiceUuid16(0x1858);
pub const MESH_PROXY_SOLICITATION: ServiceUuid16 = ServiceUuid16(0x1859);

pub const fn from_u16(value: u16) -> Self {
ServiceUuid16(value)
}

pub const fn to_u16(self) -> u16 {
self.0
}
}

impl From<u16> for ServiceUuid16 {
fn from(value: u16) -> Self {
ServiceUuid16(value)
}
}

impl From<ServiceUuid16> for u16 {
fn from(value: ServiceUuid16) -> Self {
value.0
}
}

pub struct AdvertisementBuilder<const N: usize> {
buf: [u8; N],
ptr: usize,
Expand Down Expand Up @@ -271,7 +116,7 @@ impl<const K: usize> AdvertisementBuilder<K> {
let mut i = 0;
let mut bits = 0;
while i < flags.len() {
bits |= flags[i] as u8;
bits |= flags[i].to_u8();
i += 1;
}

Expand Down Expand Up @@ -342,6 +187,11 @@ impl<const K: usize> AdvertisementBuilder<K> {
res
}
}

/// Add an appearance to the advertisement data.
pub const fn appearance(self, appearance: Appearance) -> Self {
self.raw(AdvertisementDataType::APPEARANCE, &appearance.to_u16().to_le_bytes())
}
}

pub type LegacyAdvertisementBuilder = AdvertisementBuilder<LEGACY_PAYLOAD_LEN>;
Expand Down
50 changes: 50 additions & 0 deletions nrf-softdevice/src/ble/advertisement_builder/ad.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
pub struct AdvertisementDataType(u8);

impl AdvertisementDataType {
pub const FLAGS: Self = Self(0x01);
pub const INCOMPLETE_16_SERVICE_LIST: Self = Self(0x02);
pub const COMPLETE_16_SERVICE_LIST: Self = Self(0x03);
pub const INCOMPLETE_32_SERVICE_LIST: Self = Self(0x04);
pub const COMPLETE_32_SERVICE_LIST: Self = Self(0x05);
pub const INCOMPLETE_128_SERVICE_LIST: Self = Self(0x06);
pub const COMPLETE_128_SERVICE_LIST: Self = Self(0x07);
pub const SHORT_NAME: Self = Self(0x08);
pub const FULL_NAME: Self = Self(0x09);
pub const TXPOWER_LEVEL: Self = Self(0x0a);
pub const PERIPHERAL_CONNECTION_INTERVAL_RANGE: Self = Self(0x12);
pub const SERVICE_SOLICITATION_16: Self = Self(0x14);
pub const SERVICE_SOLICITATION_128: Self = Self(0x15);
pub const SERVICE_SOLICITATION_32: Self = Self(0x1f);
pub const SERVICE_DATA_16: Self = Self(0x16);
pub const SERVICE_DATA_32: Self = Self(0x20);
pub const SERVICE_DATA_128: Self = Self(0x21);
pub const APPEARANCE: Self = Self(0x19);
pub const PUBLIC_TARGET_ADDRESS: Self = Self(0x17);
pub const RANDOM_TARGET_ADDRESS: Self = Self(0x18);
pub const ADVERTISING_INTERVAL: Self = Self(0x1a);
pub const URI: Self = Self(0x24);
pub const LE_SUPPORTED_FEATURES: Self = Self(0x27);
pub const MANUFACTURER_SPECIFIC_DATA: Self = Self(0xff);

pub const fn from_u8(value: u8) -> Self {
Self(value)
}

pub const fn to_u8(self) -> u8 {
self.0
}
}

impl From<u8> for AdvertisementDataType {
fn from(value: u8) -> Self {
Self(value)
}
}

impl From<AdvertisementDataType> for u8 {
fn from(value: AdvertisementDataType) -> Self {
value.0
}
}
Loading
Loading