summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <dirbaio@dirbaio.net>2021-10-15 16:21:07 +0200
committerGitHub <noreply@github.com>2021-10-15 16:21:07 +0200
commita6c6b1f7cb54f412c5865f33b51987e5aa8427a6 (patch)
tree358d1d5ed6f9aaef0a6b622bb9b36f65b4a115d5
parentbf4f7a143d38713922d9bd5ff1a983c4c7290c10 (diff)
parent182b9001189deca80e3840b6c989cbc37623991d (diff)
downloadnrf-softdevice-a6c6b1f7cb54f412c5865f33b51987e5aa8427a6.zip
Merge pull request #81 from albertskog/feature/indications
Add support for indications
-rw-r--r--examples/src/bin/ble_bas_peripheral.rs28
-rw-r--r--examples/src/bin/ble_peripheral_onoff.rs7
-rw-r--r--nrf-softdevice-macro/src/lib.rs72
-rw-r--r--nrf-softdevice/src/ble/gatt_server.rs42
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, &params) };
+ 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 {