Skip to content

Commit

Permalink
Merge pull request #261 from dysonltd/allow-getting-and-setting-all-a…
Browse files Browse the repository at this point in the history
…ttributes

Allow getting and setting all attributes
  • Loading branch information
petekubiak authored Feb 27, 2025
2 parents 1b5b70b + e4c7f42 commit 92841fc
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 51 deletions.
2 changes: 1 addition & 1 deletion examples/apps/src/ble_bas_peripheral.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ struct Server {
struct BatteryService {
/// Battery Level
#[descriptor(uuid = descriptors::VALID_RANGE, read, value = [0, 100])]
#[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, read, value = "Battery Level")]
#[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "hello", read, value = "Battery Level")]
#[characteristic(uuid = characteristic::BATTERY_LEVEL, read, notify, value = 10)]
level: u8,
#[characteristic(uuid = "408813df-5dd4-1f87-ec11-cdb001100000", write, read, notify)]
Expand Down
12 changes: 11 additions & 1 deletion host-macros/src/characteristic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ pub struct AccessArgs {
pub struct DescriptorArgs {
/// The UUID of the descriptor.
pub uuid: TokenStream,
/// The name which will be used to identify the descriptor when accessing its attribute handle
pub name: Option<LitStr>,
/// The initial value of the descriptor (&str).
/// This is optional and can be used to set the initial value of the descriptor.
pub default_value: Option<syn::Expr>,
Expand Down Expand Up @@ -166,6 +168,7 @@ impl CharacteristicArgs {
impl DescriptorArgs {
pub fn parse(attribute: &syn::Attribute) -> Result<Self> {
let mut uuid: Option<_> = None;
let mut name: Option<LitStr> = None;
let mut read: Option<bool> = None;
// let mut write: Option<bool> = None;
// let mut capacity: Option<syn::Expr> = None;
Expand All @@ -180,6 +183,12 @@ impl DescriptorArgs {
.as_str()
{
"uuid" => check_multi(&mut uuid, "uuid", &meta, parse_uuid(&meta)?)?,
"name" => {
let value = meta
.value()
.map_err(|_| meta.error("'name' must be followed by '= [name]'. i.e. name = \"units\""))?;
check_multi(&mut name, "name", &meta, value.parse()?)?
}
"read" => check_multi(&mut read, "read", &meta, true)?,
// "write" => check_multi(&mut write, "write", &meta, true)?,
// "write_without_response" => check_multi(&mut write_without_response, "write_without_response", &meta, true)?,
Expand All @@ -196,7 +205,7 @@ impl DescriptorArgs {
"default_value" => return Err(meta.error("use 'value' for default value")),
other => {
return Err(meta.error(format!(
"Unsupported descriptor property: '{other}'.\nSupported properties are: uuid, read, value"
"Unsupported descriptor property: '{other}'.\nSupported properties are: uuid, name, read, value"
)));
}
};
Expand All @@ -205,6 +214,7 @@ impl DescriptorArgs {

Ok(Self {
uuid: uuid.ok_or(Error::custom("Descriptor must have a UUID"))?,
name,
default_value,
capacity: None,
access: AccessArgs {
Expand Down
9 changes: 5 additions & 4 deletions host-macros/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,13 @@ impl ServerBuilder {
})
}

#visibility fn get<T: trouble_host::types::gatt_traits::FromGatt>(&self, characteristic: &trouble_host::attribute::Characteristic<T>) -> Result<T, trouble_host::Error> {
self.server.table().get(characteristic)
#visibility fn get<T: trouble_host::attribute::AttributeHandle<Value = V>, V: FromGatt>(&self, attribute_handle: &T) -> Result<T::Value, Error> {
self.server.table().get(attribute_handle)
}

#visibility fn set<T: trouble_host::types::gatt_traits::AsGatt>(&self, characteristic: &trouble_host::attribute::Characteristic<T>, input: &T) -> Result<(), trouble_host::Error> {
self.server.table().set(characteristic, input)
#visibility fn set<T: trouble_host::attribute::AttributeHandle>(&self, attribute_handle: &T, input: &T::Value) -> Result<(), Error> {
self.server.table().set(attribute_handle, input)

}
}

Expand Down
47 changes: 31 additions & 16 deletions host-macros/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use darling::{Error, FromMeta};
use inflector::cases::screamingsnakecase::to_screaming_snake_case;
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote, quote_spanned};
use quote::{format_ident, quote, quote_spanned, ToTokens};
use syn::parse::Result;
use syn::spanned::Spanned;
use syn::{Expr, Meta, Token};
Expand Down Expand Up @@ -153,7 +153,7 @@ impl ServiceBuilder {
quote! {
#visibility struct #struct_name {
#fields
handle: trouble_host::attribute::AttributeHandle,
handle: u16,
}

#[allow(unused)]
Expand All @@ -179,7 +179,7 @@ impl ServiceBuilder {

/// Construct instructions for adding a characteristic to the service, with static storage.
fn construct_characteristic_static(&mut self, characteristic: Characteristic) {
let descriptors = self.build_descriptors(&characteristic);
let (code_descriptors, named_descriptors) = self.build_descriptors(&characteristic);
let name_screaming = format_ident!("{}", to_screaming_snake_case(characteristic.name.as_str()));
let char_name = format_ident!("{}", characteristic.name);
let ty = characteristic.ty;
Expand All @@ -192,16 +192,16 @@ impl ServiceBuilder {
};

self.code_build_chars.extend(quote_spanned! {characteristic.span=>
let #char_name = {
let (#char_name, #(#named_descriptors),*) = {
static #name_screaming: static_cell::StaticCell<[u8; <#ty as trouble_host::types::gatt_traits::AsGatt>::MAX_SIZE]> = static_cell::StaticCell::new();
let mut val = <#ty>::default(); // constrain the type of the value here
val = #default_value; // update the temporary value with our new default
let store = #name_screaming.init([0; <#ty as trouble_host::types::gatt_traits::AsGatt>::MAX_SIZE]);
let mut builder = service
.add_characteristic(#uuid, &[#(#properties),*], val, store);
#descriptors
#code_descriptors

builder.build()
(builder.build(), #(#named_descriptors),*)
};
});

Expand Down Expand Up @@ -271,15 +271,17 @@ impl ServiceBuilder {
}

/// Generate token stream for any descriptors tagged against this characteristic.
fn build_descriptors(&mut self, characteristic: &Characteristic) -> TokenStream2 {
characteristic
fn build_descriptors(&mut self, characteristic: &Characteristic) -> (TokenStream2, Vec<TokenStream2>) {
let mut named_descriptors = Vec::<TokenStream2>::new();
(characteristic
.args
.descriptors
.iter()
.enumerate()
.map(|(index, args)| {
let name_screaming =
format_ident!("DESC_{index}_{}", to_screaming_snake_case(characteristic.name.as_str()));
let identifier = args.name.as_ref().map(|name| format_ident!("{}_{}_descriptor", characteristic.name.as_str(), name.value()));
let access = &args.access;
let properties = set_access_properties(access);
let uuid = &args.uuid;
Expand All @@ -289,28 +291,41 @@ impl ServiceBuilder {
};
let capacity = match &args.capacity {
Some(cap) => quote!(#cap),
None => quote!(#default_value.len() as u8)
None => quote!(#default_value.len() as usize)
};

let mut identifier_assignment = None;
if let Some(name) = &identifier {
self.code_fields.extend(quote_spanned!{ identifier.span() =>
#name: trouble_host::attribute::Descriptor<&'static [u8]>,
});
self.code_struct_init.extend(quote_spanned! { identifier.span() =>
#name,
});
named_descriptors.push(name.to_token_stream());
identifier_assignment = Some(quote! { let #name = });
};

self.attribute_count += 1; // descriptors should always only be one attribute.

quote_spanned! {characteristic.span=>
{
#identifier_assignment {
let value = #default_value;
const CAPACITY: u8 = if (#capacity) < 16 { 16 } else { #capacity }; // minimum capacity is 16 bytes
static #name_screaming: static_cell::StaticCell<[u8; CAPACITY as usize]> = static_cell::StaticCell::new();
let store = #name_screaming.init([0; CAPACITY as usize]);
const CAPACITY: usize = if (#capacity) < 16 { 16 } else { #capacity }; // minimum capacity is 16 bytes
static #name_screaming: static_cell::StaticCell<[u8; CAPACITY]> = static_cell::StaticCell::new();
let store = #name_screaming.init([0; CAPACITY]);
let value = trouble_host::types::gatt_traits::AsGatt::as_gatt(&value);
store[..value.len()].copy_from_slice(value);
builder.add_descriptor(
builder.add_descriptor::<&[u8], _>(
#uuid,
&[#(#properties),*],
store,
);
)
};
}
})
.collect()
.collect(),
named_descriptors)
}
}

Expand Down
Loading

0 comments on commit 92841fc

Please sign in to comment.