diff options
author | Dario Nieuwenhuis <dirbaio@dirbaio.net> | 2021-10-15 16:21:07 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-15 16:21:07 +0200 |
commit | a6c6b1f7cb54f412c5865f33b51987e5aa8427a6 (patch) | |
tree | 358d1d5ed6f9aaef0a6b622bb9b36f65b4a115d5 | |
parent | bf4f7a143d38713922d9bd5ff1a983c4c7290c10 (diff) | |
parent | 182b9001189deca80e3840b6c989cbc37623991d (diff) | |
download | nrf-softdevice-a6c6b1f7cb54f412c5865f33b51987e5aa8427a6.zip |
Merge pull request #81 from albertskog/feature/indications
Add support for indications
-rw-r--r-- | examples/src/bin/ble_bas_peripheral.rs | 28 | ||||
-rw-r--r-- | examples/src/bin/ble_peripheral_onoff.rs | 7 | ||||
-rw-r--r-- | nrf-softdevice-macro/src/lib.rs | 72 | ||||
-rw-r--r-- | nrf-softdevice/src/ble/gatt_server.rs | 42 |
4 files changed, 122 insertions, 27 deletions
diff --git a/examples/src/bin/ble_bas_peripheral.rs b/examples/src/bin/ble_bas_peripheral.rs index 511513b..e3e6233 100644 --- a/examples/src/bin/ble_bas_peripheral.rs +++ b/examples/src/bin/ble_bas_peripheral.rs @@ -32,7 +32,13 @@ struct BatteryService { #[nrf_softdevice::gatt_service(uuid = "9e7312e0-2354-11eb-9f10-fbc30a62cf38")] struct FooService { - #[characteristic(uuid = "9e7312e0-2354-11eb-9f10-fbc30a63cf38", read, write, notify)] + #[characteristic( + uuid = "9e7312e0-2354-11eb-9f10-fbc30a63cf38", + read, + write, + notify, + indicate + )] foo: u16, } @@ -70,11 +76,8 @@ async fn bluetooth_task(sd: &'static Softdevice) { // Run the GATT server on the connection. This returns when the connection gets disconnected. let res = gatt_server::run(&conn, &server, |e| match e { ServerEvent::Bas(e) => match e { - BatteryServiceEvent::BatteryLevelNotificationsEnabled => { - info!("battery notifications enabled") - } - BatteryServiceEvent::BatteryLevelNotificationsDisabled => { - info!("battery notifications disabled") + BatteryServiceEvent::BatteryLevelCccdWrite { notifications } => { + info!("battery notifications: {}", notifications) } }, ServerEvent::Foo(e) => match e { @@ -84,11 +87,14 @@ async fn bluetooth_task(sd: &'static Softdevice) { info!("send notification error: {:?}", e); } } - FooServiceEvent::FooNotificationsEnabled => { - info!("foo notifications enabled") - } - FooServiceEvent::FooNotificationsDisabled => { - info!("foo notifications disabled") + FooServiceEvent::FooCccdWrite { + indications, + notifications, + } => { + info!( + "foo indications: {}, notifications: {}", + indications, notifications + ) } }, }) diff --git a/examples/src/bin/ble_peripheral_onoff.rs b/examples/src/bin/ble_peripheral_onoff.rs index 067f1d4..32eac93 100644 --- a/examples/src/bin/ble_peripheral_onoff.rs +++ b/examples/src/bin/ble_peripheral_onoff.rs @@ -68,11 +68,8 @@ async fn run_bluetooth(sd: &'static Softdevice, server: &Server) { info!("send notification error: {:?}", e); } } - ServerEvent::Foo(FooServiceEvent::FooNotificationsEnabled) => { - info!("notifications enabled") - } - ServerEvent::Foo(FooServiceEvent::FooNotificationsDisabled) => { - info!("notifications disabled") + ServerEvent::Foo(FooServiceEvent::FooCccdWrite { notifications }) => { + info!("foo notifications: {}", notifications) } }) .await; diff --git a/nrf-softdevice-macro/src/lib.rs b/nrf-softdevice-macro/src/lib.rs index f78e0e6..c564d0d 100644 --- a/nrf-softdevice-macro/src/lib.rs +++ b/nrf-softdevice-macro/src/lib.rs @@ -215,6 +215,7 @@ pub fn gatt_service(args: TokenStream, item: TokenStream) -> TokenStream { let get_fn = format_ident!("{}_get", ch.name); let set_fn = format_ident!("{}_set", ch.name); let notify_fn = format_ident!("{}_notify", ch.name); + let indicate_fn = format_ident!("{}_indicate", ch.name); let fn_vis = ch.vis.clone(); let uuid = ch.args.uuid; @@ -291,10 +292,8 @@ pub fn gatt_service(args: TokenStream, item: TokenStream) -> TokenStream { } )); } - if notify { - let case_enabled = format_ident!("{}NotificationsEnabled", name_pascal); - let case_disabled = format_ident!("{}NotificationsDisabled", name_pascal); + if notify { code_impl.extend(quote_spanned!(ch.span=> #fn_vis fn #notify_fn( &self, @@ -306,17 +305,68 @@ pub fn gatt_service(args: TokenStream, item: TokenStream) -> TokenStream { } )); + if !indicate { + let case_cccd_write = format_ident!("{}CccdWrite", name_pascal); + + code_event_enum.extend(quote_spanned!(ch.span=> + #case_cccd_write{notifications: bool}, + )); + code_on_write.extend(quote_spanned!(ch.span=> + if handle == self.#cccd_handle && data.len() != 0 { + match data[0] & 0x01 { + 0x00 => return Some(#event_enum_name::#case_cccd_write{notifications: false}), + 0x01 => return Some(#event_enum_name::#case_cccd_write{notifications: true}), + _ => {}, + } + } + )); + } + } + + if indicate { + code_impl.extend(quote_spanned!(ch.span=> + fn #indicate_fn( + &self, + conn: &#ble::Connection, + val: #ty, + ) -> Result<(), #ble::gatt_server::IndicateValueError> { + let buf = #ty_as_val::to_gatt(&val); + #ble::gatt_server::indicate_value(conn, self.#value_handle, buf) + } + )); + + if !notify { + let case_cccd_write = format_ident!("{}CccdWrite", name_pascal); + + code_event_enum.extend(quote_spanned!(ch.span=> + #case_cccd_write{indications: bool}, + )); + code_on_write.extend(quote_spanned!(ch.span=> + if handle == self.#cccd_handle && data.len() != 0 { + match data[0] & 0x02 { + 0x00 => return Some(#event_enum_name::#case_cccd_write{indications: false}), + 0x02 => return Some(#event_enum_name::#case_cccd_write{indications: true}), + _ => {}, + } + } + )); + } + } + + if indicate && notify { + let case_cccd_write = format_ident!("{}CccdWrite", name_pascal); + code_event_enum.extend(quote_spanned!(ch.span=> - #case_enabled, - #case_disabled, + #case_cccd_write{indications: bool, notifications: bool}, )); code_on_write.extend(quote_spanned!(ch.span=> - if handle == self.#cccd_handle { - let data = data; - if data.len() != 0 && data[0] & 0x01 != 0 { - return Some(#event_enum_name::#case_enabled); - } else { - return Some(#event_enum_name::#case_disabled); + if handle == self.#cccd_handle && data.len() != 0 { + match data[0] & 0x03 { + 0x00 => return Some(#event_enum_name::#case_cccd_write{indications: false, notifications: false}), + 0x01 => return Some(#event_enum_name::#case_cccd_write{indications: false, notifications: true}), + 0x02 => return Some(#event_enum_name::#case_cccd_write{indications: true, notifications: false}), + 0x03 => return Some(#event_enum_name::#case_cccd_write{indications: true, notifications: true}), + _ => {}, } } )); diff --git a/nrf-softdevice/src/ble/gatt_server.rs b/nrf-softdevice/src/ble/gatt_server.rs index b744d9b..6c7eb8c 100644 --- a/nrf-softdevice/src/ble/gatt_server.rs +++ b/nrf-softdevice/src/ble/gatt_server.rs @@ -262,6 +262,7 @@ impl From<DisconnectedError> for NotifyValueError { } } +/// Multiple notifications can be queued. Will fail when the queue is full. pub fn notify_value(conn: &Connection, handle: u16, val: &[u8]) -> Result<(), NotifyValueError> { let conn_handle = conn.with_state(|state| state.check_connected())?; @@ -279,6 +280,47 @@ pub fn notify_value(conn: &Connection, handle: u16, val: &[u8]) -> Result<(), No Ok(()) } +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum IndicateValueError { + Disconnected, + Raw(RawError), +} + +impl From<RawError> for IndicateValueError { + fn from(err: RawError) -> Self { + Self::Raw(err) + } +} + +impl From<DisconnectedError> for IndicateValueError { + fn from(_: DisconnectedError) -> Self { + Self::Disconnected + } +} + +/// This will fail if an indication is already in progress +pub fn indicate_value( + conn: &Connection, + handle: u16, + val: &[u8], +) -> Result<(), IndicateValueError> { + let conn_handle = conn.with_state(|state| state.check_connected())?; + + let mut len: u16 = val.len() as _; + let params = raw::ble_gatts_hvx_params_t { + handle, + type_: raw::BLE_GATT_HVX_INDICATION as u8, + offset: 0, + p_data: val.as_ptr() as _, + p_len: &mut len, + }; + let ret = unsafe { raw::sd_ble_gatts_hvx(conn_handle, ¶ms) }; + RawError::convert(ret)?; + + Ok(()) +} + pub(crate) unsafe fn on_evt(ble_evt: *const raw::ble_evt_t) { let gatts_evt = get_union_field(ble_evt, &(*ble_evt).evt.gatts_evt); match (*ble_evt).header.evt_id as u32 { |