Skip to content

Commit

Permalink
Refactor host API
Browse files Browse the repository at this point in the history
The refactoring ensures that API is less likely to be misused, and moves
logic associated with a BLE role to the appropriate place.

- Peripheral type for advertising
- Central type for connecting and scanning
- Runner type for running the stack
- Stack type for passing a copyable handle to the stack
  • Loading branch information
lulf committed Sep 23, 2024
1 parent 1776fbf commit ea859c8
Show file tree
Hide file tree
Showing 22 changed files with 1,092 additions and 866 deletions.
2 changes: 1 addition & 1 deletion examples/apps/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = "2021"
license = "MIT OR Apache-2.0"

[dependencies]
trouble-host = { version = "0.1.0", path = "../../host", features = ["gatt"] }
trouble-host = { version = "0.1.0", path = "../../host" } #, features = ["gatt"] }
bt-hci = { version = "0.1.1" }
embassy-executor = { version = "0.6.0" }
embassy-futures = "0.1.1"
Expand Down
25 changes: 10 additions & 15 deletions examples/apps/src/ble_advertise_multiple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@ use bt_hci::cmd::le::*;
use bt_hci::controller::ControllerCmdSync;
use embassy_futures::join::join;
use embassy_time::{Duration, Instant, Timer};
use static_cell::StaticCell;
use trouble_host::advertise::{
AdStructure, AdvFilterPolicy, Advertisement, AdvertisementParameters, AdvertisementSet, PhyKind, TxPower,
BR_EDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE,
};
use trouble_host::{Address, BleHost, BleHostResources, Controller, PacketQos};
use trouble_host::prelude::*;

/// Size of L2CAP packets (ATT MTU is this - 4)
const L2CAP_MTU: usize = 27;
Expand All @@ -18,6 +13,8 @@ const CONNECTIONS_MAX: usize = 1;
/// Max number of L2CAP channels.
const L2CAP_CHANNELS_MAX: usize = 2; // Signal + att

type Resources<C> = HostResources<C, CONNECTIONS_MAX, L2CAP_CHANNELS_MAX, L2CAP_MTU>;

pub async fn run<C>(controller: C)
where
C: Controller
Expand All @@ -29,15 +26,13 @@ where
+ for<'t> ControllerCmdSync<LeSetExtAdvEnable<'t>>
+ for<'t> ControllerCmdSync<LeSetExtScanResponseData<'t>>,
{
static HOST_RESOURCES: StaticCell<BleHostResources<CONNECTIONS_MAX, L2CAP_CHANNELS_MAX, L2CAP_MTU>> =
StaticCell::new();
let host_resources = HOST_RESOURCES.init(BleHostResources::new(PacketQos::None));

let mut ble: BleHost<'_, _> = BleHost::new(controller, host_resources);

let address: Address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]);
info!("Our address = {:?}", address);
ble.set_random_address(address);

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

let mut adv_data = [0; 31];
let len = AdStructure::encode_slice(
Expand Down Expand Up @@ -88,10 +83,10 @@ where
let mut handles = AdvertisementSet::handles(&sets);

info!("Starting advertising");
let _ = join(ble.run(), async {
let _ = join(runner.run(), async {
loop {
let start = Instant::now();
let mut advertiser = ble.advertise_ext(&sets, &mut handles).await.unwrap();
let mut advertiser = peripheral.advertise_ext(&sets, &mut handles).await.unwrap();
match advertiser.accept().await {
Ok(_) => {}
Err(trouble_host::Error::Timeout) => {
Expand Down
23 changes: 10 additions & 13 deletions examples/apps/src/ble_bas_central.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ use embassy_futures::join::join;
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use embassy_time::{Duration, Timer};
use static_cell::StaticCell;
use trouble_host::attribute::Uuid;
use trouble_host::connection::ConnectConfig;
use trouble_host::packet_pool::PacketPool;
use trouble_host::scan::ScanConfig;
use trouble_host::{Address, BleHost, BleHostResources, Controller, PacketQos};
use trouble_host::prelude::*;

/// Size of L2CAP packets
const L2CAP_MTU: usize = 128;
Expand All @@ -17,15 +13,14 @@ const CONNECTIONS_MAX: usize = 1;
/// Max number of L2CAP channels.
const L2CAP_CHANNELS_MAX: usize = 3; // Signal + att + CoC

type Resources<C> = HostResources<C, CONNECTIONS_MAX, L2CAP_CHANNELS_MAX, L2CAP_MTU>;

pub async fn run<C>(controller: C)
where
C: Controller,
{
static HOST_RESOURCES: StaticCell<BleHostResources<CONNECTIONS_MAX, L2CAP_CHANNELS_MAX, L2CAP_MTU>> =
StaticCell::new();
let host_resources = HOST_RESOURCES.init(BleHostResources::new(PacketQos::None));

let ble: BleHost<'_, _> = BleHost::new(controller, host_resources);
let mut resources = Resources::new(PacketQos::None);
let (stack, _, mut central, mut runner) = trouble_host::new(controller, &mut resources).build();

// NOTE: Modify this to match the address of the peripheral you want to connect to.
// Currently it matches the address used by the peripheral examples
Expand All @@ -40,16 +35,18 @@ where
};

info!("Scanning for peripheral...");
let _ = join(ble.run(), async {
let _ = join(runner.run(), async {
static PACKET_POOL: StaticCell<PacketPool<NoopRawMutex, 24, 64, 1>> = StaticCell::new();
let packet_pool = PACKET_POOL.init(PacketPool::new(PacketQos::None));

info!("Connecting");

let conn = ble.connect(&config).await.unwrap();
let conn = central.connect(&config).await.unwrap();
info!("Connected, creating gatt client");

let client = ble.gatt_client::<10, 64, 16, 24>(&conn, packet_pool).await.unwrap();
let client = GattClient::<C, 10, 64, 16, 24>::new(stack, &conn, packet_pool)
.await
.unwrap();

let _ = join(client.task(), async {
info!("Looking for battery service");
Expand Down
38 changes: 17 additions & 21 deletions examples/apps/src/ble_bas_peripheral.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
use embassy_futures::join::join3;
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use embassy_time::{Duration, Timer};
use static_cell::StaticCell;
use trouble_host::advertise::{AdStructure, Advertisement, BR_EDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE};
use trouble_host::attribute::{AttributeTable, Characteristic, CharacteristicProp, Service, Uuid};
use trouble_host::gatt::{GattEvent, GattServer};
use trouble_host::{Address, BleHost, BleHostError, BleHostResources, Controller, PacketQos};
use trouble_host::prelude::*;

/// Size of L2CAP packets (ATT MTU is this - 4)
const L2CAP_MTU: usize = 251;
Expand All @@ -18,20 +14,19 @@ const L2CAP_CHANNELS_MAX: usize = 2; // Signal + att

const MAX_ATTRIBUTES: usize = 10;

type Resources<C> = HostResources<C, CONNECTIONS_MAX, L2CAP_CHANNELS_MAX, L2CAP_MTU>;

pub async fn run<C>(controller: C)
where
C: Controller,
{
static HOST_RESOURCES: StaticCell<BleHostResources<CONNECTIONS_MAX, L2CAP_CHANNELS_MAX, L2CAP_MTU>> =
StaticCell::new();
let resources = HOST_RESOURCES.init(BleHostResources::new(PacketQos::None));

let mut ble: BleHost<'_, _> = BleHost::new(controller, resources);

//let address: Address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]);
let address = Address::random([0x41, 0x5A, 0xE3, 0x1E, 0x83, 0xE7]);
info!("Our address = {:?}", address);
ble.set_random_address(address);

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

let mut table: AttributeTable<'_, NoopRawMutex, MAX_ATTRIBUTES> = AttributeTable::new();

Expand All @@ -57,19 +52,19 @@ where
)
.build();

let server = ble.gatt_server::<NoopRawMutex, MAX_ATTRIBUTES, L2CAP_MTU>(&table);
let server = GattServer::<NoopRawMutex, MAX_ATTRIBUTES, L2CAP_MTU>::new(stack, &table);

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

async fn ble_task<C: Controller>(ble: &BleHost<'_, C>) -> Result<(), BleHostError<C::Error>> {
ble.run().await
async fn ble_task<C: Controller>(mut runner: Runner<'_, C>) -> Result<(), BleHostError<C::Error>> {
runner.run().await
}

async fn gatt_task(
Expand All @@ -94,7 +89,8 @@ async fn gatt_task(
}

async fn advertise_task<C: Controller>(
ble: &BleHost<'_, C>,
stack: Stack<'_, C>,
mut peripheral: Peripheral<'_, C>,
server: &GattServer<'_, '_, NoopRawMutex, MAX_ATTRIBUTES, L2CAP_MTU>,
handle: Characteristic,
) -> Result<(), BleHostError<C::Error>> {
Expand All @@ -109,7 +105,7 @@ async fn advertise_task<C: Controller>(
)?;
loop {
info!("[adv] advertising");
let mut advertiser = ble
let mut advertiser = peripheral
.advertise(
&Default::default(),
Advertisement::ConnectableScannableUndirected {
Expand All @@ -126,7 +122,7 @@ async fn advertise_task<C: Controller>(
Timer::after(Duration::from_secs(2)).await;
tick = tick.wrapping_add(1);
info!("[adv] notifying connection of tick {}", tick);
let _ = server.notify(ble, handle, &conn, &[tick]).await;
let _ = server.notify(stack, handle, &conn, &[tick]).await;
}
}
}
24 changes: 10 additions & 14 deletions examples/apps/src/ble_l2cap_central.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
use embassy_futures::join::join;
use embassy_time::{Duration, Timer};
use static_cell::StaticCell;
use trouble_host::connection::ConnectConfig;
use trouble_host::l2cap::L2capChannel;
use trouble_host::scan::ScanConfig;
use trouble_host::{Address, BleHost, BleHostResources, Controller, PacketQos};
use trouble_host::prelude::*;

/// How many outgoing L2CAP buffers per link
const L2CAP_TXQ: u8 = 20;
Expand All @@ -21,15 +17,15 @@ const CONNECTIONS_MAX: usize = 1;
/// Max number of L2CAP channels.
const L2CAP_CHANNELS_MAX: usize = 3; // Signal + att + CoC

type Resources<C> = HostResources<C, CONNECTIONS_MAX, L2CAP_CHANNELS_MAX, L2CAP_MTU>;

pub async fn run<C>(controller: C)
where
C: Controller,
{
static HOST_RESOURCES: StaticCell<BleHostResources<CONNECTIONS_MAX, L2CAP_CHANNELS_MAX, L2CAP_MTU>> =
StaticCell::new();
let host_resources = HOST_RESOURCES.init(BleHostResources::new(PacketQos::None));
let mut resources = Resources::new(PacketQos::None);

let ble: BleHost<'_, _> = BleHost::new(controller, host_resources);
let (stack, _, mut central, mut runner) = trouble_host::new(controller, &mut resources).build();

// NOTE: Modify this to match the address of the peripheral you want to connect to.
// Currently it matches the address used by the peripheral examples
Expand All @@ -44,23 +40,23 @@ where
};

info!("Scanning for peripheral...");
let _ = join(ble.run(), async {
let _ = join(runner.run(), async {
loop {
let conn = ble.connect(&config).await.unwrap();
let conn = central.connect(&config).await.unwrap();
info!("Connected, creating l2cap channel");
const PAYLOAD_LEN: usize = 27;
let mut ch1 = L2capChannel::create(&ble, &conn, 0x2349, &Default::default())
let mut ch1 = L2capChannel::create(stack, &conn, 0x2349, &Default::default())
.await
.unwrap();
info!("New l2cap channel created, sending some data!");
for i in 0..10 {
let tx = [i; PAYLOAD_LEN];
ch1.send::<_, PAYLOAD_LEN>(&ble, &tx).await.unwrap();
ch1.send::<_, PAYLOAD_LEN>(stack, &tx).await.unwrap();
}
info!("Sent data, waiting for them to be sent back");
let mut rx = [0; PAYLOAD_LEN];
for i in 0..10 {
let len = ch1.receive(&ble, &mut rx).await.unwrap();
let len = ch1.receive(stack, &mut rx).await.unwrap();
assert_eq!(len, rx.len());
assert_eq!(rx, [i; PAYLOAD_LEN]);
}
Expand Down
27 changes: 13 additions & 14 deletions examples/apps/src/ble_l2cap_peripheral.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
use embassy_futures::join::join;
use embassy_time::{Duration, Timer};
use static_cell::StaticCell;
use trouble_host::advertise::{AdStructure, Advertisement, BR_EDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE};
use trouble_host::l2cap::L2capChannel;
use trouble_host::{Address, BleHost, BleHostResources, Controller, PacketQos};
use trouble_host::prelude::*;

/// How many outgoing L2CAP buffers per link
const L2CAP_TXQ: u8 = 20;
Expand All @@ -20,19 +17,21 @@ const CONNECTIONS_MAX: usize = 1;
/// Max number of L2CAP channels.
const L2CAP_CHANNELS_MAX: usize = 3; // Signal + att + CoC

type Resources<C> = HostResources<C, CONNECTIONS_MAX, L2CAP_CHANNELS_MAX, L2CAP_MTU>;

pub async fn run<C>(controller: C)
where
C: Controller,
{
static HOST_RESOURCES: StaticCell<BleHostResources<CONNECTIONS_MAX, L2CAP_CHANNELS_MAX, L2CAP_MTU>> =
StaticCell::new();
let host_resources = HOST_RESOURCES.init(BleHostResources::new(PacketQos::None));
let mut resources = Resources::new(PacketQos::None);

let address: Address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]);
info!("Our address = {:?}", address);

let mut ble: BleHost<'_, _> = BleHost::new(controller, host_resources);
ble.set_random_address(address);
let (stack, mut peripheral, _, mut runner) = trouble_host::new(controller, &mut resources)
.set_random_address(address)
.build();

let mut adv_data = [0; 31];
AdStructure::encode_slice(
&[AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED)],
Expand All @@ -43,10 +42,10 @@ where
let mut scan_data = [0; 31];
AdStructure::encode_slice(&[AdStructure::CompleteLocalName(b"Trouble")], &mut scan_data[..]).unwrap();

let _ = join(ble.run(), async {
let _ = join(runner.run(), async {
loop {
info!("Advertising, waiting for connection...");
let mut advertiser = ble
let mut advertiser = peripheral
.advertise(
&Default::default(),
Advertisement::ConnectableScannableUndirected {
Expand All @@ -60,7 +59,7 @@ where

info!("Connection established");

let mut ch1 = L2capChannel::accept(&ble, &conn, &[0x2349], &Default::default())
let mut ch1 = L2capChannel::accept(stack, &conn, &[0x2349], &Default::default())
.await
.unwrap();

Expand All @@ -70,7 +69,7 @@ where
const PAYLOAD_LEN: usize = 27;
let mut rx = [0; PAYLOAD_LEN];
for i in 0..10 {
let len = ch1.receive(&ble, &mut rx).await.unwrap();
let len = ch1.receive(stack, &mut rx).await.unwrap();
assert_eq!(len, rx.len());
assert_eq!(rx, [i; PAYLOAD_LEN]);
}
Expand All @@ -79,7 +78,7 @@ where
Timer::after(Duration::from_secs(1)).await;
for i in 0..10 {
let tx = [i; PAYLOAD_LEN];
ch1.send::<_, PAYLOAD_LEN>(&ble, &tx).await.unwrap();
ch1.send::<_, PAYLOAD_LEN>(stack, &tx).await.unwrap();
}
info!("L2CAP data echoed");

Expand Down
2 changes: 1 addition & 1 deletion examples/nrf-sdc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ futures = { version = "0.3", default-features = false, features = ["async-await"
nrf-sdc = { version = "0.1.0", default-features = false, features = ["defmt", "nrf52840", "peripheral", "central"] }
nrf-mpsl = { version = "0.1.0", default-features = false, features = ["defmt", "critical-section-impl"] }
bt-hci = { version = "0.1.1", default-features = false, features = ["defmt"] }
trouble-host = { version = "0.1.0", path = "../../host", features = ["defmt", "gatt"] }
trouble-host = { version = "0.1.0", path = "../../host", features = ["defmt"] }
trouble-example-apps = { version = "0.1.0", path = "../apps", features = ["defmt"] }

defmt = "0.3"
Expand Down
4 changes: 3 additions & 1 deletion host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@ rand = "0.8.5"
[features]
defmt = [ "dep:defmt", "embassy-time/defmt", "bt-hci/defmt" ]
log = [ "dep:log" ]
peripheral = []
central = []
gatt = []
scan = []
default = [ "gatt", "scan" ]
default = [ "peripheral", "central", "gatt" ]
controller-host-flow-control = []

# BEGIN AUTOGENERATED CONFIG FEATURES
Expand Down
Loading

0 comments on commit ea859c8

Please sign in to comment.