diff options
author | Albert Skog <mail@albertskog.se> | 2021-10-15 12:34:47 +0200 |
---|---|---|
committer | Albert Skog <mail@albertskog.se> | 2021-10-15 12:34:47 +0200 |
commit | 182b9001189deca80e3840b6c989cbc37623991d (patch) | |
tree | 358d1d5ed6f9aaef0a6b622bb9b36f65b4a115d5 | |
parent | fd48489c0ffc58c8ae02d32d9904162a2777df3c (diff) | |
parent | bf4f7a143d38713922d9bd5ff1a983c4c7290c10 (diff) | |
download | nrf-softdevice-182b9001189deca80e3840b6c989cbc37623991d.zip |
Merge branch 'master' into feature/indications
-rw-r--r-- | Cargo.lock | 2 | ||||
-rw-r--r-- | examples/src/bin/ble_bas_peripheral.rs | 61 | ||||
-rw-r--r-- | examples/src/bin/ble_peripheral_onoff.rs | 17 | ||||
-rw-r--r-- | nrf-softdevice-macro/src/lib.rs | 118 | ||||
-rw-r--r-- | nrf-softdevice/Cargo.toml | 2 | ||||
-rw-r--r-- | nrf-softdevice/src/ble/gatt_server.rs | 21 |
6 files changed, 172 insertions, 49 deletions
@@ -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)); } _ => {} } |