Skip to content

Commit

Permalink
Add ability to act on notification enables/disables.
Browse files Browse the repository at this point in the history
Being able to take action when a characteristic's notifications
are enabled are disabled is useful when the data source needs
some action to be taken to start or stop the data source like
enabling interrupts or powering on a sensor.
  • Loading branch information
konkers committed Mar 12, 2024
1 parent 92d5213 commit 125cbaa
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 29 deletions.
25 changes: 22 additions & 3 deletions bleps-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,14 @@ pub fn gatt(input: TokenStream) -> TokenStream {
return quote!{ compile_error!("Unexpected"); }.into();
}
}
"notify_cb" => {
if let Expr::Path(p) = field.expr {
let name = path_to_string(p.path);
charact.notify_cb = Some(name);
} else {
return quote!{ compile_error!("Unexpected"); }.into();
}
}
"name" => {
if let Expr::Lit(value) = field.expr {
if let Lit::Str(s) = value.lit {
Expand Down Expand Up @@ -344,7 +352,14 @@ pub fn gatt(input: TokenStream) -> TokenStream {
quote!(())
};

quote!(let mut #gen_attr_att_data_ident = (#rfunction, #wfunction);)
let nfunction = if let Some(name) = &characteristic.notify_cb {
let fname = format_ident!("{}", name);
quote!(&mut #fname)
} else {
quote!(())
};

quote!(let mut #gen_attr_att_data_ident = (#rfunction, #wfunction, #nfunction);)
} else if let Some(name) = &characteristic.value {
let vname = format_ident!("{}", name);
quote!(let mut #gen_attr_att_data_ident = #vname;)
Expand Down Expand Up @@ -391,10 +406,11 @@ pub fn gatt(input: TokenStream) -> TokenStream {
let rfunction = format_ident!("_attr_read{}", current_handle);
let wfunction = format_ident!("_attr_write{}", current_handle);
decls.push(
quote!(let mut #char_ccd_data_attr = (&mut #rfunction, &mut #wfunction);),
quote!(let mut #char_ccd_data_attr = (&mut #rfunction, &mut #wfunction, ());),
);

let backing_data = format_ident!("_attr_data{}", current_handle);

pre.push(quote!(
#[allow(non_upper_case_globals)]
static mut #backing_data: [u8; 2] = [0u8; 2];
Expand Down Expand Up @@ -497,7 +513,9 @@ pub fn gatt(input: TokenStream) -> TokenStream {
quote!(())
};

decls.push(quote!(let mut #char_desc_data_ident = (#rfunction, #wfunction);));
decls.push(
quote!(let mut #char_desc_data_ident = (#rfunction, #wfunction, ());),
);
} else if let Some(name) = &descriptor.value {
let vname = format_ident!("{}", name);
decls.push(quote!(let mut #char_desc_data_ident = #vname;));
Expand Down Expand Up @@ -572,6 +590,7 @@ struct Characteristic {
write: Option<String>,
description: Option<String>,
notify: bool,
notify_cb: Option<String>,
name: Option<String>,
descriptors: Vec<Descriptor>,
}
Expand Down
27 changes: 27 additions & 0 deletions bleps-macros/tests/macro_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,30 @@ fn test5() {

println!("{:x?}", gatt_attributes);
}

#[test]
fn test6() {
let mut my_read_function = |_offset: usize, data: &mut [u8]| {
data[..5].copy_from_slice(&b"Ciao!"[..]);
5
};
let mut my_write_function = |_offset, data: &[u8]| {
println!("{:?}", data);
};
let mut my_notify = |enabled: bool| {
println!("enabled = {enabled}");
};

gatt!([service {
uuid: "9e7312e0-2354-11eb-9f10-fbc30a62cf38",
characteristics: [characteristic {
uuid: "9e7312e0-2354-11eb-9f10-fbc30a62cf38",
notify: true,
notify_cb: my_notify,
read: my_read_function,
write: my_write_function,
},],
},]);

println!("{:x?}", gatt_attributes);
}
78 changes: 75 additions & 3 deletions bleps/src/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ pub trait AttData {
fn write(&mut self, _offset: usize, _data: &[u8]) -> Result<(), AttErrorCode> {
Ok(())
}

fn enable_notification(&mut self, _enabled: bool) -> Result<(), AttErrorCode> {
Ok(())
}
}

impl<'a, const N: usize> AttData for &'a [u8; N] {
Expand Down Expand Up @@ -193,7 +197,7 @@ impl<T> IntoResult<T> for Result<T, AttErrorCode> {
}
}

impl<T, R> AttData for (R, ())
impl<T, R> AttData for (R, (), ())
where
T: IntoResult<usize>,
R: FnMut(usize, &mut [u8]) -> T,
Expand All @@ -207,7 +211,7 @@ where
}
}

impl<U, W> AttData for ((), W)
impl<U, W> AttData for ((), W, ())
where
U: IntoResult<()>,
W: FnMut(usize, &[u8]) -> U,
Expand All @@ -221,7 +225,7 @@ where
}
}

impl<T, U, R, W> AttData for (R, W)
impl<T, U, R, W> AttData for (R, W, ())
where
T: IntoResult<usize>,
U: IntoResult<()>,
Expand All @@ -245,6 +249,74 @@ where
}
}

impl<T, U, R, N> AttData for (R, (), N)
where
T: IntoResult<usize>,
U: IntoResult<()>,
R: FnMut(usize, &mut [u8]) -> T,
N: FnMut(bool) -> U,
{
fn readable(&self) -> bool {
true
}

fn read(&mut self, offset: usize, data: &mut [u8]) -> Result<usize, AttErrorCode> {
self.0(offset, data).into_result()
}

fn enable_notification(&mut self, enabled: bool) -> Result<(), AttErrorCode> {
self.2(enabled).into_result()
}
}

impl<T, U, R, W, N> AttData for (R, W, N)
where
T: IntoResult<usize>,
U: IntoResult<()>,
R: FnMut(usize, &mut [u8]) -> T,
W: FnMut(usize, &[u8]) -> U,
N: FnMut(bool) -> U,
{
fn readable(&self) -> bool {
true
}

fn read(&mut self, offset: usize, data: &mut [u8]) -> Result<usize, AttErrorCode> {
self.0(offset, data).into_result()
}

fn writable(&self) -> bool {
true
}

fn write(&mut self, offset: usize, data: &[u8]) -> Result<(), AttErrorCode> {
self.1(offset, data).into_result()
}

fn enable_notification(&mut self, enabled: bool) -> Result<(), AttErrorCode> {
self.2(enabled).into_result()
}
}

impl<U, W, N> AttData for ((), W, N)
where
U: IntoResult<()>,
W: FnMut(usize, &[u8]) -> U,
N: FnMut(bool) -> U,
{
fn writable(&self) -> bool {
true
}

fn write(&mut self, offset: usize, data: &[u8]) -> Result<(), AttErrorCode> {
self.1(offset, data).into_result()
}

fn enable_notification(&mut self, enabled: bool) -> Result<(), AttErrorCode> {
self.2(enabled).into_result()
}
}

pub const ATT_READABLE: u8 = 0x02;
pub const ATT_WRITEABLE: u8 = 0x08;

Expand Down
50 changes: 29 additions & 21 deletions bleps/src/attribute_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,31 +396,39 @@ bleps_dedup::dedup! {
self.write_att(src_handle, response).await;
}

async fn handle_write_cmd(&mut self, _src_handle: u16, handle: u16, data: Data) {
for att in self.attributes.iter_mut() {
if att.handle == handle {
if att.data.writable() {
// Write commands can't respond with an error.
let err = att.data.write(0, data.as_slice());
if let Err(e) = err {
log::debug!("write error: {e:?}");
}
}
break;
}
fn handle_write(&mut self, handle: u16, data: Data) -> Result<(), AttErrorCode> {
let Some((index, att)) = self.attributes.iter_mut().enumerate().find(|(_, att)| att.handle == handle) else {
return Err(AttErrorCode::InvalidHandle);
};
if !att.data.writable() {
return Err(AttErrorCode::WriteNotPermitted);
}

let err = att.data.write(0, data.as_slice());
if let Err(e) = err {
log::debug!("write error: {e:?}");
return Err(e);
}

// If this is a Client Characteristic Configuration descriptor, notify the parent of a change
// otherwise return immediatly.
if att.uuid != Uuid::Uuid16(0x2902) {
return Ok(());
}

// Here we make the same assumption made in async_atribute_server that the CCCD directly follows
// the charactaristic attribute.
let parrent_att = &mut self.attributes[index-1];
parrent_att.data.enable_notification(data.as_slice()[0] & 0x1 == 0x1)
}

async fn handle_write_cmd(&mut self, _src_handle: u16, handle: u16, data: Data) {
// Write commands can't respond with an error.
let _ = self.handle_write(handle, data);
}

async fn handle_write_req(&mut self, src_handle: u16, handle: u16, data: Data) {
let mut err = Err(AttErrorCode::AttributeNotFound);
for att in self.attributes.iter_mut() {
if att.handle == handle {
if att.data.writable() {
err = att.data.write(0, data.as_slice());
}
break;
}
}
let err = self.handle_write(handle, data);

let response = match err {
Ok(()) => Data::new_att_write_response(),
Expand Down
4 changes: 2 additions & 2 deletions bleps/tests/test_ble.rs
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ fn attribute_server_discover_two_services() {
let mut char_att_data = &char_data;
let char = Attribute::new(CHARACTERISTIC_UUID16, &mut char_att_data);

let mut custom_char_att_data = (&mut rf1, &mut wf1);
let mut custom_char_att_data = (&mut rf1, &mut wf1, ());
let custom_char_att_data_attr = Attribute::new(
Uuid::Uuid128([
0xC9, 0x15, 0x15, 0x96, 0x54, 0x56, 0x64, 0xB3, 0x38, 0x45, 0x26, 0x5D, 0xF1, 0x62,
Expand Down Expand Up @@ -681,7 +681,7 @@ fn attribute_server_discover_two_services() {
let mut char_att_data2 = &char_data2;
let char2 = Attribute::new(CHARACTERISTIC_UUID16, &mut char_att_data2);

let mut custom_char_att_data2 = (&mut rf2, &mut wf2);
let mut custom_char_att_data2 = (&mut rf2, &mut wf2, ());
let custom_char_att_data_attr2 = Attribute::new(
Uuid::Uuid128([
0xC9, 0x15, 0x15, 0x96, 0x54, 0x56, 0x64, 0xB3, 0x38, 0x45, 0x26, 0x5D, 0xF1, 0x62,
Expand Down

0 comments on commit 125cbaa

Please sign in to comment.