diff options
author | Dario Nieuwenhuis <dirbaio@dirbaio.net> | 2021-10-11 13:15:34 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-11 13:15:34 +0200 |
commit | 0fcb3135b22ec045ff65e83b74d5241534141a6c (patch) | |
tree | 4f9c6f790ac0a713ee8019e0548ae84dcdd206e4 | |
parent | 71b9e4b3c20ac4fab0d14c4541b365ed48aab58b (diff) | |
parent | 37585e1ffafbfa5b32c9136472ddb3f4ae136856 (diff) | |
download | nrf-softdevice-0fcb3135b22ec045ff65e83b74d5241534141a6c.zip |
Merge pull request #82 from lulf/gatt-multiple-services-1
Gatt multiple services 1
-rw-r--r-- | examples/src/bin/ble_bas_peripheral.rs | 57 | ||||
-rw-r--r-- | examples/src/bin/ble_peripheral_onoff.rs | 23 | ||||
-rw-r--r-- | nrf-softdevice-macro/src/lib.rs | 119 | ||||
-rw-r--r-- | nrf-softdevice/src/ble/gatt_server.rs | 21 |
4 files changed, 176 insertions, 44 deletions
diff --git a/examples/src/bin/ble_bas_peripheral.rs b/examples/src/bin/ble_bas_peripheral.rs index 8793c82..511513b 100644 --- a/examples/src/bin/ble_bas_peripheral.rs +++ b/examples/src/bin/ble_bas_peripheral.rs @@ -24,17 +24,28 @@ 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, - #[characteristic(uuid = "3a4a1f7e-22d8-11eb-a3aa-1b3b1d4e4a0d", read, write, notify)] +} + +#[nrf_softdevice::gatt_service(uuid = "9e7312e0-2354-11eb-9f10-fbc30a62cf38")] +struct FooService { + #[characteristic(uuid = "9e7312e0-2354-11eb-9f10-fbc30a63cf38", read, write, notify)] 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, @@ -58,26 +69,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::BatteryLevelNotificationsEnabled => { + info!("battery notifications enabled") + } + BatteryServiceEvent::BatteryLevelNotificationsDisabled => { + info!("battery notifications disabled") + } + }, + 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); + } + } + FooServiceEvent::FooNotificationsEnabled => { + info!("foo notifications enabled") } - } - BatteryServiceEvent::FooWrite(val) => { - info!("wrote battery level: {}", val); - if let Err(e) = server.foo_notify(&conn, val + 1) { - info!("send notification error: {:?}", e); + FooServiceEvent::FooNotificationsDisabled => { + info!("foo notifications disabled") } - } - BatteryServiceEvent::BatteryLevelNotificationsEnabled => { - info!("battery notifications enabled") - } - BatteryServiceEvent::BatteryLevelNotificationsDisabled => { - info!("battery notifications disabled") - } - BatteryServiceEvent::FooNotificationsEnabled => info!("foo notifications enabled"), - BatteryServiceEvent::FooNotificationsDisabled => info!("foo notifications disabled"), + }, }) .await; diff --git a/examples/src/bin/ble_peripheral_onoff.rs b/examples/src/bin/ble_peripheral_onoff.rs index e3f3943..067f1d4 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,14 +62,18 @@ 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::FooNotificationsEnabled => info!("notifications enabled"), - FooServiceEvent::FooNotificationsDisabled => info!("notifications disabled"), + ServerEvent::Foo(FooServiceEvent::FooNotificationsEnabled) => { + info!("notifications enabled") + } + ServerEvent::Foo(FooServiceEvent::FooNotificationsDisabled) => { + info!("notifications disabled") + } }) .await; @@ -76,7 +85,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 d2e4789..f78e0e6 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 @@ -122,6 +215,7 @@ pub fn gatt_server(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 fn_vis = ch.vis.clone(); let uuid = ch.args.uuid; let read = ch.args.read; @@ -159,14 +253,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) @@ -193,7 +287,7 @@ 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))); } )); } @@ -202,7 +296,7 @@ pub fn gatt_server(args: TokenStream, item: TokenStream) -> TokenStream { let case_disabled = format_ident!("{}NotificationsDisabled", name_pascal); code_impl.extend(quote_spanned!(ch.span=> - fn #notify_fn( + #fn_vis fn #notify_fn( &self, conn: &#ble::Connection, val: #ty, @@ -218,6 +312,7 @@ pub fn gatt_server(args: TokenStream, item: TokenStream) -> TokenStream { )); 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 { @@ -230,6 +325,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 @@ -238,7 +334,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 { @@ -262,7 +358,7 @@ pub fn gatt_server(args: TokenStream, item: TokenStream) -> TokenStream { } } - enum #event_enum_name { + #struc_vis enum #event_enum_name { #code_event_enum } }; @@ -274,7 +370,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(); @@ -321,6 +417,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/src/ble/gatt_server.rs b/nrf-softdevice/src/ble/gatt_server.rs index 58663cc..b744d9b 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)); } _ => {} } |