Skip to content

Commit

Permalink
rework gatt support
Browse files Browse the repository at this point in the history
* New interface for configuring attributes: AttributeTable.
* Track MTU per connections.
* Track notifications per connection per characteristic internally.
* Ability to read/write/notify values in gatt table.
  • Loading branch information
lulf committed Mar 14, 2024
1 parent 5f3359f commit c81717c
Show file tree
Hide file tree
Showing 8 changed files with 814 additions and 648 deletions.
58 changes: 42 additions & 16 deletions examples/nrf-sdc/src/bin/ble_bas_peripheral.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use bt_hci::cmd::SyncCmd;
use bt_hci::param::BdAddr;
use defmt::{info, unwrap};
use defmt::{error, info, unwrap};
use embassy_executor::Spawner;
use embassy_futures::join::join3;
use embassy_nrf::{bind_interrupts, pac};
Expand All @@ -17,8 +17,7 @@ use static_cell::StaticCell;
use trouble_host::{
adapter::{Adapter, HostResources},
advertise::{AdStructure, AdvertiseConfig, BR_EDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE},
attribute::{AttributesBuilder, CharacteristicProp, ServiceBuilder, Uuid},
gatt::{GattEvent, GattServer},
attribute::{AttributeTable, Characteristic, CharacteristicProp, Service, Uuid},
PacketQos,
};

Expand Down Expand Up @@ -94,7 +93,7 @@ async fn main(spawner: Spawner) {
let mut pool = [0; 256];
let rng = sdc::rng_pool::RngPool::new(p.RNG, Irqs, &mut pool, 64);

let mut sdc_mem = sdc::Mem::<1672>::new();
let mut sdc_mem = sdc::Mem::<3312>::new();
let sdc = unwrap!(build_sdc(sdc_p, &rng, mpsl, &mut sdc_mem));

info!("Advertising as {:02x}", bd_addr());
Expand All @@ -114,33 +113,60 @@ async fn main(spawner: Spawner) {
],
};

let mut attributes: AttributesBuilder<'_, 10> = AttributesBuilder::new();
static DATA: StaticCell<[u8; 1]> = StaticCell::new();
let data = DATA.init([32; 1]);
ServiceBuilder::new(&mut attributes, 0x180f_u16.into())
.add_characteristic(0x2a19.into(), &[CharacteristicProp::Read], data)
.done();
let mut attributes = attributes.build();
let mut table: AttributeTable<'_, NoopRawMutex, 10> = AttributeTable::new();

// Generic Access Service (mandatory)
let mut id = [b'T', b'r', b'o', b'u', b'b', b'l', b'e'];
let mut appearance = [0x80, 0x07];
let mut bat_level = [0; 1];
let handle = {
let mut svc = table.add_service(Service::new(0x1800));
let _ = svc.add_characteristic(Characteristic::new(0x2a00, &[CharacteristicProp::Read], &mut id[..]));
let _ = svc.add_characteristic(Characteristic::new(
0x2a01,
&[CharacteristicProp::Read],
&mut appearance[..],
));
drop(svc);

// Generic attribute service (mandatory)
table.add_service(Service::new(0x1801));

// Battery service
let mut svc = table.add_service(Service::new(0x180f));

svc.add_characteristic(Characteristic::new(
0x2a19,
&[CharacteristicProp::Read, CharacteristicProp::Notify],
&mut bat_level,
))
};

let mut server = GattServer::new(&adapter, &mut attributes[..]);
let server = adapter.gatt_server(&table);

info!("Starting advertising and GATT service");
let _ = join3(
adapter.run(),
async {
loop {
match server.next().await {
GattEvent::Write(_conn, attribute) => {
info!("Attribute was written: {:?}", attribute);
Ok(event) => {
info!("Gatt event: {:?}", event);
}
Err(e) => {
error!("Error processing GATT events: {:?}", e);
}
}
}
},
async {
let _conn = unwrap!(adapter.advertise(&config).await);
let conn = unwrap!(adapter.advertise(&config).await);
// Keep connection alive
let mut tick: u8 = 0;
loop {
Timer::after(Duration::from_secs(60)).await
Timer::after(Duration::from_secs(10)).await;
tick += 1;
unwrap!(server.notify(handle, &conn, &[tick]).await);
}
},
)
Expand Down
21 changes: 20 additions & 1 deletion host/src/adapter.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use crate::advertise::AdvertiseConfig;
use crate::attribute::AttributeTable;
use crate::attribute_server::AttributeServer;
use crate::channel_manager::ChannelManager;
use crate::connection::Connection;
use crate::connection_manager::{ConnectionInfo, ConnectionManager};
use crate::cursor::{ReadCursor, WriteCursor};
use crate::gatt::GattServer;
use crate::l2cap::{L2capPacket, L2CAP_CID_ATT, L2CAP_CID_DYN_START, L2CAP_CID_LE_U_SIGNAL};
use crate::packet_pool::{DynamicPacketPool, PacketPool, Qos, ATT_ID};
use crate::packet_pool::{self, DynamicPacketPool, PacketPool, Qos, ATT_ID};
use crate::pdu::Pdu;
use crate::scan::{ScanConfig, ScanReport};
use crate::types::l2cap::L2capLeSignal;
Expand Down Expand Up @@ -159,6 +162,21 @@ where
Ok(conn)
}

/// Creates a GATT server capable of processing the GATT protocol using the provided table of attributes.
pub fn gatt_server<'reference, 'values, const MAX: usize>(
&'reference self,
table: &'reference AttributeTable<'values, M, MAX>,
) -> GattServer<'reference, 'values, 'd, M, MAX> {
GattServer {
server: AttributeServer::new(table),
pool: self.pool,
pool_id: packet_pool::ATT_ID,
rx: self.att_inbound.receiver().into(),
tx: self.outbound.sender().into(),
connections: &self.connections,
}
}

async fn handle_acl(&self, acl: AclPacket<'_>) -> Result<(), HandleError> {
let (conn, packet) = L2capPacket::decode(acl)?;
match packet.channel {
Expand Down Expand Up @@ -256,6 +274,7 @@ where
interval: e.conn_interval.as_u16(),
latency: e.peripheral_latency,
timeout: e.supervision_timeout.as_u16(),
att_mtu: 23,
},
) {
warn!("Error establishing connection: {:?}", err);
Expand Down
Loading

0 comments on commit c81717c

Please sign in to comment.