summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlbert Skog <mail@albertskog.se>2021-10-15 12:34:47 +0200
committerAlbert Skog <mail@albertskog.se>2021-10-15 12:34:47 +0200
commit182b9001189deca80e3840b6c989cbc37623991d (patch)
tree358d1d5ed6f9aaef0a6b622bb9b36f65b4a115d5
parentfd48489c0ffc58c8ae02d32d9904162a2777df3c (diff)
parentbf4f7a143d38713922d9bd5ff1a983c4c7290c10 (diff)
downloadnrf-softdevice-182b9001189deca80e3840b6c989cbc37623991d.zip
Merge branch 'master' into feature/indications
-rw-r--r--Cargo.lock2
-rw-r--r--examples/src/bin/ble_bas_peripheral.rs61
-rw-r--r--examples/src/bin/ble_peripheral_onoff.rs17
-rw-r--r--nrf-softdevice-macro/src/lib.rs118
-rw-r--r--nrf-softdevice/Cargo.toml2
-rw-r--r--nrf-softdevice/src/ble/gatt_server.rs21
6 files changed, 172 insertions, 49 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 83526b3..47f9d92 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -534,7 +534,7 @@ name = "nrf-softdevice"
version = "0.1.0"
dependencies = [
"cortex-m 0.7.3",
- "cortex-m-rt 0.6.14",
+ "cortex-m-rt 0.7.0",
"critical-section",
"defmt",
"embassy",
diff --git a/examples/src/bin/ble_bas_peripheral.rs b/examples/src/bin/ble_bas_peripheral.rs
index 8d76ec3..e3e6233 100644
--- a/examples/src/bin/ble_bas_peripheral.rs
+++ b/examples/src/bin/ble_bas_peripheral.rs
@@ -24,12 +24,16 @@ async fn softdevice_task(sd: &'static Softdevice) {
sd.run().await;
}
-#[nrf_softdevice::gatt_server(uuid = "180f")]
+#[nrf_softdevice::gatt_service(uuid = "180f")]
struct BatteryService {
- #[characteristic(uuid = "2a19", read, write, notify)]
+ #[characteristic(uuid = "2a19", read, notify)]
battery_level: u8,
+}
+
+#[nrf_softdevice::gatt_service(uuid = "9e7312e0-2354-11eb-9f10-fbc30a62cf38")]
+struct FooService {
#[characteristic(
- uuid = "3a4a1f7e-22d8-11eb-a3aa-1b3b1d4e4a0d",
+ uuid = "9e7312e0-2354-11eb-9f10-fbc30a63cf38",
read,
write,
notify,
@@ -38,9 +42,16 @@ struct BatteryService {
foo: u16,
}
+#[nrf_softdevice::gatt_server]
+struct Server {
+ bas: BatteryService,
+ foo: FooService,
+}
+
#[embassy::task]
async fn bluetooth_task(sd: &'static Softdevice) {
- let server: BatteryService = unwrap!(gatt_server::register(sd));
+ let server: Server = unwrap!(gatt_server::register(sd));
+
#[rustfmt::skip]
let adv_data = &[
0x02, 0x01, raw::BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE as u8,
@@ -64,30 +75,28 @@ 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 {
- BatteryServiceEvent::BatteryLevelWrite(val) => {
- info!("wrote battery level: {}", val);
- if let Err(e) = server.battery_level_notify(&conn, val + 1) {
- info!("send notification error: {:?}", e);
+ ServerEvent::Bas(e) => match e {
+ BatteryServiceEvent::BatteryLevelCccdWrite { notifications } => {
+ info!("battery notifications: {}", notifications)
+ }
+ },
+ ServerEvent::Foo(e) => match e {
+ FooServiceEvent::FooWrite(val) => {
+ info!("wrote foo: {}", val);
+ if let Err(e) = server.foo.foo_notify(&conn, val + 1) {
+ info!("send notification error: {:?}", e);
+ }
}
- }
- BatteryServiceEvent::FooWrite(val) => {
- info!("wrote battery level: {}", val);
- if let Err(e) = server.foo_notify(&conn, val + 1) {
- info!("send notification error: {:?}", e);
+ FooServiceEvent::FooCccdWrite {
+ indications,
+ notifications,
+ } => {
+ info!(
+ "foo indications: {}, notifications: {}",
+ indications, notifications
+ )
}
- }
- BatteryServiceEvent::BatteryLevelCccdWrite { notifications } => {
- info!("battery notifications: {}", notifications)
- }
- BatteryServiceEvent::FooCccdWrite {
- indications,
- notifications,
- } => {
- info!(
- "foo indications: {}, notifications: {}",
- indications, notifications
- )
- }
+ },
})
.await;
diff --git a/examples/src/bin/ble_peripheral_onoff.rs b/examples/src/bin/ble_peripheral_onoff.rs
index 47b9dfd..32eac93 100644
--- a/examples/src/bin/ble_peripheral_onoff.rs
+++ b/examples/src/bin/ble_peripheral_onoff.rs
@@ -28,13 +28,18 @@ async fn softdevice_task(sd: &'static Softdevice) {
sd.run().await;
}
-#[nrf_softdevice::gatt_server(uuid = "9e7312e0-2354-11eb-9f10-fbc30a62cf38")]
+#[nrf_softdevice::gatt_service(uuid = "9e7312e0-2354-11eb-9f10-fbc30a62cf38")]
struct FooService {
#[characteristic(uuid = "9e7312e0-2354-11eb-9f10-fbc30a63cf38", read, write, notify)]
foo: u16,
}
-async fn run_bluetooth(sd: &'static Softdevice, server: &FooService) {
+#[nrf_softdevice::gatt_server]
+struct Server {
+ foo: FooService,
+}
+
+async fn run_bluetooth(sd: &'static Softdevice, server: &Server) {
#[rustfmt::skip]
let adv_data = &[
0x02, 0x01, raw::BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE as u8,
@@ -57,13 +62,13 @@ async fn run_bluetooth(sd: &'static Softdevice, server: &FooService) {
info!("advertising done!");
let res = gatt_server::run(&conn, server, |e| match e {
- FooServiceEvent::FooWrite(val) => {
+ ServerEvent::Foo(FooServiceEvent::FooWrite(val)) => {
info!("wrote foo level: {}", val);
- if let Err(e) = server.foo_notify(&conn, val + 1) {
+ if let Err(e) = server.foo.foo_notify(&conn, val + 1) {
info!("send notification error: {:?}", e);
}
}
- FooServiceEvent::FooCccdWrite { notifications } => {
+ ServerEvent::Foo(FooServiceEvent::FooCccdWrite { notifications }) => {
info!("foo notifications: {}", notifications)
}
})
@@ -77,7 +82,7 @@ async fn run_bluetooth(sd: &'static Softdevice, server: &FooService) {
#[embassy::task]
async fn bluetooth_task(sd: &'static Softdevice, button1: AnyPin, button2: AnyPin) {
- let server: FooService = unwrap!(gatt_server::register(sd));
+ let server: Server = unwrap!(gatt_server::register(sd));
info!("Bluetooth is OFF");
info!("Press nrf52840-dk button 1 to enable, button 2 to disable");
diff --git a/nrf-softdevice-macro/src/lib.rs b/nrf-softdevice-macro/src/lib.rs
index e1cec67..c564d0d 100644
--- a/nrf-softdevice-macro/src/lib.rs
+++ b/nrf-softdevice-macro/src/lib.rs
@@ -14,7 +14,7 @@ mod uuid;
use crate::uuid::Uuid;
#[derive(Debug, FromMeta)]
-struct ServerArgs {
+struct ServiceArgs {
uuid: Uuid,
}
#[derive(Debug, FromMeta)]
@@ -36,14 +36,106 @@ struct Characteristic {
ty: syn::Type,
args: CharacteristicArgs,
span: Span,
+ vis: syn::Visibility,
}
#[proc_macro_attribute]
-pub fn gatt_server(args: TokenStream, item: TokenStream) -> TokenStream {
+pub fn gatt_server(_args: TokenStream, item: TokenStream) -> TokenStream {
+ let mut struc = syn::parse_macro_input!(item as syn::ItemStruct);
+
+ let struct_fields = match &mut struc.fields {
+ syn::Fields::Named(n) => n,
+ _ => {
+ struc
+ .ident
+ .span()
+ .unwrap()
+ .error("gatt_server structs must have named fields, not tuples.")
+ .emit();
+ return TokenStream::new();
+ }
+ };
+ let fields = struct_fields
+ .named
+ .iter()
+ .cloned()
+ .collect::<Vec<syn::Field>>();
+
+ let struct_name = struc.ident.clone();
+ let event_enum_name = format_ident!("{}Event", struct_name);
+
+ let mut code_register_init = TokenStream2::new();
+ let mut code_on_write = TokenStream2::new();
+ let mut code_event_enum = TokenStream2::new();
+
+ let ble = quote!(::nrf_softdevice::ble);
+
+ for field in fields.iter() {
+ let name = field.ident.as_ref().unwrap();
+ let span = field.ty.span();
+ code_register_init.extend(quote_spanned!(span=>
+ #name: #ble::gatt_server::register_service(sd)?,
+ ));
+
+ if let syn::Type::Path(p) = &field.ty {
+ let name_pascal = format_ident!(
+ "{}",
+ inflector::cases::pascalcase::to_pascal_case(&name.to_string())
+ );
+ let event_enum_ty = p.path.get_ident().unwrap();
+ let event_enum_variant = format_ident!("{}Event", event_enum_ty);
+ code_event_enum.extend(quote_spanned!(span=>
+ #name_pascal(#event_enum_variant),
+ ));
+
+ code_on_write.extend(quote_spanned!(span=>
+ if let Some(e) = self.#name.on_write(handle, data) {
+ return Some(#event_enum_name::#name_pascal(e));
+ }
+ ));
+ }
+ }
+
+ struct_fields.named = syn::punctuated::Punctuated::from_iter(fields);
+ let struc_vis = struc.vis.clone();
+
+ let result = quote! {
+ #struc
+
+ impl #struct_name {
+ }
+
+ #struc_vis enum #event_enum_name {
+ #code_event_enum
+ }
+
+ impl #ble::gatt_server::Server for #struct_name {
+ type Event = #event_enum_name;
+
+ fn register(sd: &::nrf_softdevice::Softdevice) -> Result<Self, #ble::gatt_server::RegisterError>
+ {
+ Ok(Self {
+ #code_register_init
+ })
+ }
+
+ fn on_write(&self, handle: u16, data: &[u8]) -> Option<Self::Event> {
+ use #ble::gatt_server::Service;
+
+ #code_on_write
+ None
+ }
+ }
+ };
+ result.into()
+}
+
+#[proc_macro_attribute]
+pub fn gatt_service(args: TokenStream, item: TokenStream) -> TokenStream {
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
let mut struc = syn::parse_macro_input!(item as syn::ItemStruct);
- let args = match ServerArgs::from_list(&args) {
+ let args = match ServiceArgs::from_list(&args) {
Ok(v) => v,
Err(e) => {
return e.write_errors().into();
@@ -59,7 +151,7 @@ pub fn gatt_server(args: TokenStream, item: TokenStream) -> TokenStream {
.ident
.span()
.unwrap()
- .error("gatt_server structs must have named fields, not tuples.")
+ .error("gatt_service structs must have named fields, not tuples.")
.emit();
return TokenStream::new();
}
@@ -90,6 +182,7 @@ pub fn gatt_server(args: TokenStream, item: TokenStream) -> TokenStream {
ty: field.ty.clone(),
args,
span: field.ty.span(),
+ vis: field.vis.clone(),
});
false
@@ -123,6 +216,7 @@ pub fn gatt_server(args: TokenStream, item: TokenStream) -> TokenStream {
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;
let read = ch.args.read;
@@ -160,14 +254,14 @@ pub fn gatt_server(args: TokenStream, item: TokenStream) -> TokenStream {
));
code_impl.extend(quote_spanned!(ch.span=>
- fn #get_fn(&self) -> Result<#ty, #ble::gatt_server::GetValueError> {
+ #fn_vis fn #get_fn(&self) -> Result<#ty, #ble::gatt_server::GetValueError> {
let sd = unsafe { ::nrf_softdevice::Softdevice::steal() };
let buf = &mut [0u8; #ty_as_val::MAX_SIZE];
let size = #ble::gatt_server::get_value(sd, self.#value_handle, buf)?;
Ok(#ty_as_val::from_gatt(&buf[..size]))
}
- fn #set_fn(&self, val: #ty) -> Result<(), #ble::gatt_server::SetValueError> {
+ #fn_vis fn #set_fn(&self, val: #ty) -> Result<(), #ble::gatt_server::SetValueError> {
let sd = unsafe { ::nrf_softdevice::Softdevice::steal() };
let buf = #ty_as_val::to_gatt(&val);
#ble::gatt_server::set_value(sd, self.#value_handle, buf)
@@ -194,14 +288,14 @@ pub fn gatt_server(args: TokenStream, item: TokenStream) -> TokenStream {
));
code_on_write.extend(quote_spanned!(ch.span=>
if handle == self.#value_handle {
- return Some(#event_enum_name::#case_write(#ty_as_val::from_gatt(&data)));
+ return Some(#event_enum_name::#case_write(#ty_as_val::from_gatt(data)));
}
));
}
if notify {
code_impl.extend(quote_spanned!(ch.span=>
- fn #notify_fn(
+ #fn_vis fn #notify_fn(
&self,
conn: &#ble::Connection,
val: #ty,
@@ -281,6 +375,7 @@ pub fn gatt_server(args: TokenStream, item: TokenStream) -> TokenStream {
let uuid = args.uuid;
struct_fields.named = syn::punctuated::Punctuated::from_iter(fields);
+ let struc_vis = struc.vis.clone();
let result = quote! {
#struc
@@ -289,7 +384,7 @@ pub fn gatt_server(args: TokenStream, item: TokenStream) -> TokenStream {
#code_impl
}
- impl #ble::gatt_server::Server for #struct_name {
+ impl #ble::gatt_server::Service for #struct_name {
type Event = #event_enum_name;
fn uuid() -> #ble::Uuid {
@@ -313,7 +408,7 @@ pub fn gatt_server(args: TokenStream, item: TokenStream) -> TokenStream {
}
}
- enum #event_enum_name {
+ #struc_vis enum #event_enum_name {
#code_event_enum
}
};
@@ -325,7 +420,7 @@ pub fn gatt_client(args: TokenStream, item: TokenStream) -> TokenStream {
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
let mut struc = syn::parse_macro_input!(item as syn::ItemStruct);
- let args = match ServerArgs::from_list(&args) {
+ let args = match ServiceArgs::from_list(&args) {
Ok(v) => v,
Err(e) => {
return e.write_errors().into();
@@ -372,6 +467,7 @@ pub fn gatt_client(args: TokenStream, item: TokenStream) -> TokenStream {
ty: field.ty.clone(),
args,
span: field.ty.span(),
+ vis: field.vis.clone(),
});
false
diff --git a/nrf-softdevice/Cargo.toml b/nrf-softdevice/Cargo.toml
index 1034aad..143e1d0 100644
--- a/nrf-softdevice/Cargo.toml
+++ b/nrf-softdevice/Cargo.toml
@@ -43,7 +43,7 @@ critical-section = { version = "0.2.1" }
num_enum = { version = "0.5.1", default-features = false }
embassy = { version = "0.1.0" }
cortex-m = "0.7.2"
-cortex-m-rt = "0.6.13"
+cortex-m-rt = ">=0.6.15,<0.8"
heapless = "0.7.1"
fixed = "1.5.0"
diff --git a/nrf-softdevice/src/ble/gatt_server.rs b/nrf-softdevice/src/ble/gatt_server.rs
index 63c65a6..6c7eb8c 100644
--- a/nrf-softdevice/src/ble/gatt_server.rs
+++ b/nrf-softdevice/src/ble/gatt_server.rs
@@ -30,8 +30,15 @@ pub struct CharacteristicHandles {
pub trait Server: Sized {
type Event;
+ fn register(sd: &Softdevice) -> Result<Self, RegisterError>;
+ fn on_write(&self, handle: u16, data: &[u8]) -> Option<Self::Event>;
+}
+
+pub trait Service: Sized {
+ type Event;
fn uuid() -> Uuid;
+
fn register<F>(service_handle: u16, register_char: F) -> Result<Self, RegisterError>
where
F: FnMut(Characteristic, &[u8]) -> Result<CharacteristicHandles, RegisterError>;
@@ -51,12 +58,17 @@ impl From<RawError> for RegisterError {
}
}
-pub fn register<S: Server>(_sd: &Softdevice) -> Result<S, RegisterError> {
+pub fn register<S: Server>(sd: &Softdevice) -> Result<S, RegisterError> {
+ S::register(sd)
+}
+
+pub fn register_service<S: Service>(_sd: &Softdevice) -> Result<S, RegisterError> {
+ let uuid = S::uuid();
let mut service_handle: u16 = 0;
let ret = unsafe {
raw::sd_ble_gatts_service_add(
raw::BLE_GATTS_SRVC_TYPE_PRIMARY as u8,
- S::uuid().as_raw_ptr(),
+ uuid.as_raw_ptr(),
&mut service_handle as _,
)
};
@@ -139,9 +151,10 @@ impl From<DisconnectedError> for RunError {
}
}
-pub async fn run<S: Server, F>(conn: &Connection, server: &S, mut f: F) -> Result<(), RunError>
+pub async fn run<'m, F, S>(conn: &Connection, server: &S, mut f: F) -> Result<(), RunError>
where
F: FnMut(S::Event),
+ S: Server,
{
let conn_handle = conn.with_state(|state| state.check_connected())?;
portal(conn_handle)
@@ -163,7 +176,7 @@ where
panic!("gatt_server auth_required not yet supported");
}
- server.on_write(params.handle, v).map(|e| f(e));
+ server.on_write(params.handle, &v).map(|e| f(e));
}
_ => {}
}