Skip to content

Commit

Permalink
rework peripheral gatt example for more complex use of gatt interacti…
Browse files Browse the repository at this point in the history
…ons with other tasks
  • Loading branch information
jamessizeland committed Nov 21, 2024
1 parent 58ca515 commit 79a231b
Showing 1 changed file with 98 additions and 69 deletions.
167 changes: 98 additions & 69 deletions examples/apps/src/ble_bas_peripheral.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
use embassy_futures::{
join::join3,
select::{select, Either},
};
use embassy_time::{Duration, Timer};
use embassy_futures::select::{select, Either};
use embassy_time::Timer;
use trouble_host::prelude::*;

/// Size of L2CAP packets (ATT MTU is this - 4)
Expand Down Expand Up @@ -42,6 +39,7 @@ fn battery_level_on_write(_connection: &Connection, data: &[u8]) -> Result<(), (
Ok(())
}

/// Run the BLE stack.
pub async fn run<C>(controller: C)
where
C: Controller,
Expand All @@ -50,10 +48,11 @@ where
info!("Our address = {:?}", address);

let mut resources = Resources::new(PacketQos::None);
let (stack, peripheral, _, runner) = trouble_host::new(controller, &mut resources)
let (stack, mut peripheral, _, mut runner) = trouble_host::new(controller, &mut resources)
.set_random_address(address)
.build();

info!("Starting advertising and GATT service");
let server = Server::new_with_config(
stack,
GapConfig::Peripheral(PeripheralConfig {
Expand All @@ -62,81 +61,111 @@ where
}),
)
.unwrap();

info!("Starting advertising and GATT service");
let _ = join3(
ble_task(runner),
gatt_task(&server),
advertise_task(peripheral, &server),
)
.await;
}

async fn ble_task<C: Controller>(mut runner: Runner<'_, C>) -> Result<(), BleHostError<C::Error>> {
runner.run().await
let ble_task = ble_task(&mut runner);
let app_task = async {
loop {
info!("[adv] advertising");
match advertise("Trouble Example", &mut peripheral).await {
Ok(conn) => {
info!("[adv] connection established");
// set up tasks when the connection is established to a central, so they don't run when no one is connected.
let gatt = gatt_task(&server, &conn);
let counter_task = example_application_task(&server, &conn);
// run until any task ends (usually because the connection has been closed),
// then return to advertising state.
select(gatt, counter_task).await;
}
Err(err) => info!("[adv] error: {:?}", err),
}
}
};
select(ble_task, app_task).await;
}

async fn gatt_task<C: Controller>(server: &Server<'_, '_, C>) -> Result<(), BleHostError<C::Error>> {
server.run().await
async fn ble_task<C: Controller>(runner: &mut Runner<'_, C>) -> Result<(), BleHostError<C::Error>> {
runner.run().await?;
info!("BLE task finished");
Ok(())
}

async fn advertise_task<C: Controller>(
mut peripheral: Peripheral<'_, C>,
/// Stream Events until the connection closes.
async fn gatt_task<C: Controller>(
server: &Server<'_, '_, C>,
conn: &Connection<'_>,
) -> Result<(), BleHostError<C::Error>> {
let mut adv_data = [0; 31];
let level = server.battery_service.level;
loop {
if let Either::First(event) = select(conn.next(), server.run()).await {
match event {
ConnectionEvent::Disconnected { reason } => {
info!("[adv] disconnected: {:?}", reason);
break;
}
ConnectionEvent::Gatt { event, .. } => match event {
GattEvent::Read { value_handle } => {
if value_handle == level.handle {
let value = server.get(&level);
info!("[gatt] Read Event to Level Characteristic: {:?}", value);
}
}
GattEvent::Write { value_handle } => {
if value_handle == level.handle {
let value = server.get(&level);
info!("[gatt] Write Event to Level Characteristic: {:?}", value);
}
}
},
}
}
}
info!("[gatt] task finished");
Ok(())
}

/// Create an advertiser to use to connect to a BLE Central, and wait for it to connect.
async fn advertise<'a, C: Controller>(
name: &'a str,
peripheral: &mut Peripheral<'a, C>,
) -> Result<Connection<'a>, BleHostError<C::Error>> {
let name= if name.len() > 22 {
let truncated_name = &name[..22];
info!("Name truncated to {}", truncated_name);
truncated_name
} else {
name
};
let mut advertiser_data = [0; 31];
AdStructure::encode_slice(
&[
AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED),
AdStructure::ServiceUuids16(&[Uuid::Uuid16([0x0f, 0x18])]),
AdStructure::CompleteLocalName(b"Trouble"),
AdStructure::CompleteLocalName(name.as_bytes()),
],
&mut adv_data[..],
&mut advertiser_data[..],
)?;
let mut advertiser = peripheral
.advertise(
&Default::default(),
Advertisement::ConnectableScannableUndirected {
adv_data: &advertiser_data[..],
scan_data: &[],
},
)
.await?;
Ok(advertiser.accept().await?)
}

/// Example task to use the BLE notifier interface.
async fn example_application_task<C: Controller>(server: &Server<'_, '_, C>, conn: &Connection<'_>) {
let mut tick: u8 = 0;
let level = server.battery_service.level;
loop {
info!("[adv] advertising");
let mut advertiser = peripheral
.advertise(
&Default::default(),
Advertisement::ConnectableScannableUndirected {
adv_data: &adv_data[..],
scan_data: &[],
},
)
.await?;
let conn = advertiser.accept().await?;
info!("[adv] connection established");
let mut tick: u8 = 0;
let level = server.battery_service.level;
loop {
match select(conn.next(), Timer::after(Duration::from_secs(2))).await {
Either::First(event) => match event {
ConnectionEvent::Disconnected { reason } => {
info!("[adv] disconnected: {:?}", reason);
break;
}
ConnectionEvent::Gatt { event, .. } => match event {
GattEvent::Read { value_handle } => {
if value_handle == level.handle {
let value = server.get(&level);
info!("[gatt] Read Event to Level Characteristic: {:?}", value);
}
},
GattEvent::Write { value_handle } => {
if value_handle == level.handle {
let value = server.get(&level);
info!("[gatt] Write Event to Level Characteristic: {:?}", value);
}
},
},

},
Either::Second(_) => {
tick = tick.wrapping_add(1);
info!("[adv] notifying connection of tick {}", tick);
let _ = server.notify(&server.battery_service.level, &conn, &tick).await;
}
}
}
tick = tick.wrapping_add(1);
info!("[adv] notifying connection of tick {}", tick);
if let Err(err) = server.notify(&level, &conn, &tick).await {
info!("[adv] error notifying connection: {:?}", err);
break;
};
Timer::after_secs(2).await;
}
}

0 comments on commit 79a231b

Please sign in to comment.