Skip to content

update trouble api #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 9 additions & 14 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,33 @@ lsm303agr = "1.1.0"
heapless = "0.8.0"

defmt-rtt = "0.4"
defmt = "0.3"
defmt = "0.3.10"
panic-probe = { version = "0.3", features = ["print-defmt"] }

# embassy dependencies
embassy-sync = { version = "^0.6.0", default-features = false, features = [
"defmt",
] }
embassy-futures = { version = "^0.1.0", default-features = false }
embassy-executor = { version = "^0.6.1", default-features = false, features = [
"integrated-timers",
"defmt",
embassy-sync = { version = "0.6.2", features = ["defmt"] }
embassy-futures = { version = "0.1.1" }
embassy-executor = { version = "0.7.0", default-features = false, features = [
"arch-cortex-m",
"defmt",
"executor-interrupt",
"executor-thread",
"task-arena-size-32768",
] }
embassy-time = { version = "^0.3.0", default-features = false, features = [
embassy-time = { version = "0.4.0", features = [
"defmt",
"defmt-timestamp-uptime",
] }

# nrf52833 dependencies
microbit-bsp = { git = "https://github.com/jamessizeland/microbit-bsp.git", branch = "trouble", features = [
microbit-bsp = { git = "https://github.com/lulf/microbit-bsp.git", features = [
"trouble",
] }
# microbit-bsp = { path = "../microbit-bsp", features = ["trouble"] }
], branch = "main" }

# BLE dependencies
bt-hci = { version = "0.1.1", default-features = false, features = ["defmt"] }
trouble-host = { git = "https://github.com/embassy-rs/trouble.git", features = [
"defmt",
], branch = "main" }
], rev = "a1daee269deee25fbb6236cb599a8fb5919c02b7" }
static_cell = "2.1.0"

[profile.release]
Expand Down
92 changes: 27 additions & 65 deletions src/ble/advertiser.rs
Original file line number Diff line number Diff line change
@@ -1,69 +1,31 @@
use defmt::info;
use trouble_host::prelude::*;

/// BLE advertiser
pub struct AdvertiserBuilder<'d, C: Controller> {
/// Name of the device
name: &'d str,
peripheral: Peripheral<'d, C>,
}

pub struct Advertiser<'d, C: Controller> {
advertiser_data: [u8; 31],
scan_data: [u8; 4],
peripheral: Peripheral<'d, C>,
}

/// A BLE advertiser
impl<'d, C: Controller> AdvertiserBuilder<'d, C> {
/// Create a new advertiser builder
pub fn new(name: &'d str, peripheral: Peripheral<'d, C>) -> Self {
Self { name, peripheral }
}
/// Build the advertiser
pub fn build(self) -> Result<Advertiser<'d, C>, Error> {
let name: &str;
if self.name.len() > 22 {
name = &self.name[..22];
info!("Name truncated to {}", name);
} else {
name = self.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(name.as_bytes()),
],
&mut advertiser_data[..],
)?;
#[rustfmt::skip]
let scan_data: [u8;4] = [0; 4];
Ok(Advertiser {
advertiser_data,
scan_data,
peripheral: self.peripheral,
})
}
}

impl<'d, C: Controller> Advertiser<'d, C> {
/// Advertise and connect to a device with the given name
pub async fn advertise(&mut self) -> Result<Connection<'d>, BleHostError<C::Error>> {
let mut advertiser = self
.peripheral
.advertise(
&Default::default(),
Advertisement::ConnectableScannableUndirected {
adv_data: &self.advertiser_data[..],
scan_data: &self.scan_data[..],
},
)
.await?;
info!("advertising");
let conn = advertiser.accept().await?;
info!("connection established");
Ok(conn)
}
/// Create an advertiser to use to connect to a BLE Central, and wait for it to connect.
pub async fn advertise<'a, C: Controller>(
name: &'a str,
peripheral: &mut Peripheral<'a, C>,
) -> Result<Connection<'a>, BleHostError<C::Error>> {
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(name.as_bytes()),
],
&mut advertiser_data[..],
)?;
let advertiser = peripheral
.advertise(
&Default::default(),
Advertisement::ConnectableScannableUndirected {
adv_data: &advertiser_data[..],
scan_data: &[],
},
)
.await?;
info!("[adv] advertising");
let conn = advertiser.accept().await?;
info!("[adv] connection established");
Ok(conn)
}
107 changes: 50 additions & 57 deletions src/ble/gatt.rs
Original file line number Diff line number Diff line change
@@ -1,107 +1,100 @@
use super::advertiser::{Advertiser, AdvertiserBuilder};
use super::hid::*;
use super::{ble_task, mpsl_task, BleResources};
use super::{hid::*, BleServer};
use super::{stick::*, BleController};
use defmt::info;
use defmt::{info, warn};
use embassy_executor::Spawner;
use embassy_futures::select::select;
use embassy_futures::select::Either;
use microbit_bsp::ble::{MultiprotocolServiceLayer, SoftdeviceError};
use microbit_bsp::ble::{MultiprotocolServiceLayer, SoftdeviceController, SoftdeviceError};
use static_cell::StaticCell;
use trouble_host::prelude::*;
use trouble_host::types::gatt_traits::GattValue;

/// Allow a central to decide which player this controller belongs to
#[gatt_service(uuid = "8f701cf1-b1df-42a1-bb5f-6a1028c793b0")]
pub struct Player {
#[characteristic(uuid = "e3d1afe4-b414-44e3-be54-0ea26c394eba", read, write, on_write = on_write)]
#[characteristic(uuid = "e3d1afe4-b414-44e3-be54-0ea26c377eba", read, write)]
index: u8,
}

fn on_write(_: &Connection<'_>, value: &[u8]) -> Result<(), ()> {
if let Ok(index) = u8::from_gatt(value) {
info!("Player index set to {:?}", index);
};
Ok(())
}

#[gatt_server(attribute_data_size = 100)]
pub struct Server {
#[gatt_server]
pub struct BleServer {
// pub bas: BatteryService,
pub hid: ButtonService,
pub stick: StickService,
pub player: Player,
}

impl Server<'static, 'static, BleController> {
impl<'d> BleServer<'d> {
/// Build the stack for the GATT server and start background tasks required for the
/// Softdevice (Noridc's BLE stack) to run.
pub fn start_gatt(
name: &'static str,
name: &'d str,
spawner: Spawner,
controller: BleController,
mpsl: &'static MultiprotocolServiceLayer<'static>,
) -> Result<(&'static Self, Advertiser<'static, BleController>), BleHostError<SoftdeviceError>>
{
mpsl: &'static MultiprotocolServiceLayer<'_>,
) -> Result<(&'static Self, Peripheral<'d, BleController>), BleHostError<SoftdeviceError>> {
spawner.must_spawn(mpsl_task(mpsl));

let address = Address::random([0x42, 0x5A, 0xE3, 0x1E, 0x83, 0xE7]);
info!("Our address = {:?}", address);

let resources = {
static RESOURCES: StaticCell<BleResources> = StaticCell::new();
RESOURCES.init(BleResources::new(PacketQos::None))
RESOURCES.init(BleResources::new())
};
let stack = {
static STACK: StaticCell<Stack<'_, SoftdeviceController<'_>>> = StaticCell::new();
STACK.init(trouble_host::new(controller, resources).set_random_address(address))
};
let (stack, peripheral, _, runner) = trouble_host::new(controller, resources)
.set_random_address(address)
.build();
let host = stack.build();
let server = {
static SERVER: StaticCell<BleServer<'_>> = StaticCell::new();
SERVER.init(
Server::new_with_config(
stack,
GapConfig::Peripheral(PeripheralConfig {
name,
appearance: &appearance::GAMEPAD,
}),
)
BleServer::new_with_config(GapConfig::Peripheral(PeripheralConfig {
name,
appearance: &appearance::human_interface_device::GAMEPAD,
}))
.expect("Error creating Gatt Server"),
)
};
info!("Starting Gatt Server");
spawner.must_spawn(ble_task(runner));
let advertiser = AdvertiserBuilder::new(name, peripheral).build()?;
Ok((server, advertiser))
spawner.must_spawn(ble_task(host.runner));
Ok((server, host.peripheral))
}
}

/// A BLE GATT server
/// A BLE GATT server.
///
/// This is where we can interact with events from the GATT server.
/// This task will run until the connection is disconnected.
pub async fn gatt_server_task(server: &BleServer<'_>, conn: &Connection<'_>) {
let index = server.player.index;
loop {
if let Either::First(event) = select(conn.next(), server.run()).await {
match event {
ConnectionEvent::Disconnected { reason } => {
info!("[gatt] Disconnected: {:?}", reason);
break;
}
ConnectionEvent::Gatt { event, .. } => match event {
GattEvent::Read { value_handle } => {
if value_handle == server.player.index.handle {
let value = server.get(&server.player.index);
info!(
"[gatt] Read Event to Player Index Characteristic: {:?}",
value
);
match conn.next().await {
ConnectionEvent::Disconnected { reason } => {
info!("[gatt] Disconnected: {:?}", reason);
break;
}
ConnectionEvent::Gatt { data, .. } => {
match data.process(server).await {
// Server processing emits
Ok(Some(GattEvent::Read(event))) => {
if event.handle() == index.handle {
let value = server.get(&index);
info!("[gatt] Read Event to index Characteristic: {:?}", value);
}
}
GattEvent::Write { value_handle } => {
if value_handle == server.player.index.handle {
let value = server.get(&server.player.index);
Ok(Some(GattEvent::Write(event))) => {
if event.handle() == index.handle {
info!(
"[gatt] Write Event to Player Index Characteristic: {:?}",
value
"[gatt] Write Event to index Characteristic: {:?}",
event.data()
);
}
}
},
Ok(_) => {}
Err(e) => {
warn!("[gatt] error processing event: {:?}", e);
}
}
}
}
}
Expand Down
6 changes: 2 additions & 4 deletions src/ble/hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub async fn notify_button_state(
loop {
button.input.wait_for_low().await;
info!("button {} pressed", button.name);
server.notify(&button.ble_handle, connection, &true).await?;
button.ble_handle.notify(server, connection, &true).await?;
display
.display(
DisplayFrame::Letter(button.name),
Expand All @@ -55,9 +55,7 @@ pub async fn notify_button_state(
Timer::after(debounce).await;
button.input.wait_for_high().await;
info!("button {} released", button.name);
server
.notify(&button.ble_handle, connection, &false)
.await?;
button.ble_handle.notify(server, connection, &false).await?;
Timer::after(debounce).await;
}
}
Expand Down
6 changes: 2 additions & 4 deletions src/ble/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod gatt;
pub mod hid;
pub mod stick;

pub use gatt::BleServer;
use microbit_bsp::ble::{MultiprotocolServiceLayer, SoftdeviceController};
use trouble_host::prelude::*;

Expand All @@ -15,12 +16,9 @@ const CONNECTIONS_MAX: usize = 1;
/// Max number of L2CAP channels.
const L2CAP_CHANNELS_MAX: usize = 2; // Signal + att

pub type BleServer<'d> = gatt::Server<'d, 'd, SoftdeviceController<'d>>;

pub type BleController = SoftdeviceController<'static>;

pub type BleResources =
HostResources<BleController, CONNECTIONS_MAX, L2CAP_CHANNELS_MAX, L2CAP_MTU>;
pub type BleResources = HostResources<CONNECTIONS_MAX, L2CAP_CHANNELS_MAX, L2CAP_MTU>;

#[embassy_executor::task]
pub async fn mpsl_task(mpsl: &'static MultiprotocolServiceLayer<'static>) -> ! {
Expand Down
7 changes: 4 additions & 3 deletions src/ble/stick.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ impl Axis {
let new = -((new_raw - self.offset) / self.divider) as i8; // invert the value
if new != self.old {
self.old = new;
Some(new as i8)
Some(new)
} else {
None
}
Expand All @@ -73,16 +73,17 @@ pub async fn analog_stick_task(
let divider = 623;
let mut x_axis = Axis::new(offset, divider);
let mut y_axis = Axis::new(offset, divider);
let stick = &server.stick;
loop {
// read adc values for x and y, and if they have changed by a certain amount, notify
// we are reducing the number of analogue stick levels to a range of -2 to 2
saadc.sample(&mut buf).await;
// display the x and y values on the led matrix
if let Some(x) = x_axis.changed(buf[0]) {
server.notify(&server.stick.x, conn, &x).await?;
stick.x.notify(server, conn, &x).await?;
}
if let Some(y) = y_axis.changed(buf[1]) {
server.notify(&server.stick.y, conn, &y).await?;
stick.y.notify(server, conn, &y).await?;
}
if !(x_axis.old == 0 && y_axis.old == 0) {
// only display if the stick is not centered
Expand Down
4 changes: 2 additions & 2 deletions src/io/display/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ impl AsyncDisplay {

#[allow(unused)]
pub enum DisplayFrame {
DisplayFrame(Frame<5, 5>),
Custom(Frame<5, 5>),
/// Display a single pixel at the given coordinates, where (0,0) is the center of the display.
Coord {
x: i8,
Expand All @@ -89,7 +89,7 @@ pub enum DisplayFrame {
impl DisplayFrame {
fn to_frame(&self) -> Frame<5, 5> {
match self {
DisplayFrame::DisplayFrame(frame) => frame.clone(),
DisplayFrame::Custom(frame) => *frame,
DisplayFrame::Heart => bitmap::HEART,
DisplayFrame::Smile => bitmap::SMILE,
DisplayFrame::Sad => bitmap::SAD,
Expand Down
Loading