Skip to content

Commit fc7a43c

Browse files
authored
Merge pull request #18 from jamessizeland/trouble
Trouble
2 parents 4d918d7 + c349cbe commit fc7a43c

File tree

9 files changed

+434
-3
lines changed

9 files changed

+434
-3
lines changed

Cargo.toml

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "microbit-bsp"
3-
version = "0.4.0"
3+
version = "0.4.1"
44
edition = "2021"
55
description = "An embassy-based boards support package (BSP) for BBC Micro:bit v2"
66
license = "MIT OR Apache-2.0"
@@ -20,6 +20,21 @@ futures = { version = "0.3", default-features = false }
2020
defmt = { version = "0.3", optional = true }
2121
heapless = "0.8.0"
2222

23+
# trouble bluetooth dependencies
24+
nrf-sdc = { git = "https://github.com/alexmoon/nrf-sdc.git", default-features = false, features = [
25+
"defmt",
26+
"peripheral",
27+
"central",
28+
"nrf52833"
29+
], rev = "3702af909d31cd81c62f15e1aa9d5f637ec935fa", optional = true}
30+
nrf-mpsl = { git = "https://github.com/alexmoon/nrf-sdc.git", default-features = false, features = [
31+
"defmt",
32+
"critical-section-impl"
33+
], rev = "3702af909d31cd81c62f15e1aa9d5f637ec935fa", optional = true }
34+
static_cell = { version = "2", optional = true }
35+
36+
2337
[features]
2438
default = ["defmt"]
2539
defmt = ["dep:defmt", "embassy-nrf/defmt", "heapless/defmt-03"]
40+
trouble = ["embassy-nrf/unstable-pac", "embassy-nrf/rt", "nrf-sdc", "nrf-mpsl", "static_cell"]

README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ microbit-bsp is a board support package (BSP) library for the BBC micro:bit v2 a
1414

1515
## Example application
1616

17-
```
17+
```rust
1818
#![no_std]
1919
#![no_main]
2020

@@ -60,10 +60,11 @@ async fn main(_spawner: Spawner) {
6060

6161
To run an example:
6262

63-
```
63+
```bash
6464
cd examples/display
6565
cargo run --release
6666
```
67+
6768
## Cargo Features
6869

6970
The feature `defmt` is enabled by default, and allows

examples/trouble/Cargo.toml

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
[package]
2+
name = "trouble-ble-example"
3+
version = "0.1.0"
4+
edition = "2021"
5+
resolver = "2"
6+
7+
[dependencies]
8+
embassy-executor = { version = "0.6", default-features = false, features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers", "executor-interrupt", "task-arena-size-32768"] }
9+
embassy-time = { version = "0.3", default-features = false, features = ["defmt", "defmt-timestamp-uptime"] }
10+
microbit-bsp = { path = "../../", features = ["trouble"]}
11+
embassy-futures = "0.1.1"
12+
embassy-sync = { version = "0.6", features = ["defmt"] }
13+
14+
futures = { version = "0.3", default-features = false, features = ["async-await"]}
15+
bt-hci = { version = "0.1.1", default-features = false, features = ["defmt"] }
16+
trouble-host = { git = "https://github.com/embassy-rs/trouble.git", features = [
17+
"defmt",
18+
] }
19+
20+
defmt = "0.3"
21+
defmt-rtt = "0.4.0"
22+
23+
cortex-m = { version = "0.7.6" }
24+
cortex-m-rt = "0.7.5"
25+
panic-probe = { version = "0.3", features = ["print-defmt"] }
26+
static_cell = "2"
27+
28+
[profile.release]
29+
debug = 2

examples/trouble/build.rs

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//! This build script copies the `memory.x` file from the crate root into
2+
//! a directory where the linker can always find it at build time.
3+
//! For many projects this is optional, as the linker always searches the
4+
//! project root directory -- wherever `Cargo.toml` is. However, if you
5+
//! are using a workspace or have a more complicated build setup, this
6+
//! build script becomes required. Additionally, by requesting that
7+
//! Cargo re-run the build script whenever `memory.x` is changed,
8+
//! updating `memory.x` ensures a rebuild of the application with the
9+
//! new memory settings.
10+
11+
use std::env;
12+
use std::fs::File;
13+
use std::io::Write;
14+
use std::path::PathBuf;
15+
16+
fn main() {
17+
// Put `memory.x` in our output directory and ensure it's
18+
// on the linker search path.
19+
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20+
File::create(out.join("memory.x"))
21+
.unwrap()
22+
.write_all(include_bytes!("memory.x"))
23+
.unwrap();
24+
println!("cargo:rustc-link-search={}", out.display());
25+
26+
// By default, Cargo will re-run a build script whenever
27+
// any file in the project changes. By specifying `memory.x`
28+
// here, we ensure the build script is only re-run when
29+
// `memory.x` is changed.
30+
println!("cargo:rerun-if-changed=memory.x");
31+
32+
println!("cargo:rustc-link-arg-bins=--nmagic");
33+
println!("cargo:rustc-link-arg-bins=-Tlink.x");
34+
println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
35+
}

examples/trouble/memory.x

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
MEMORY
2+
{
3+
/* NOTE 1 K = 1 KiBi = 1024 bytes */
4+
FLASH : ORIGIN = 0x00000000, LENGTH = 512K
5+
RAM : ORIGIN = 0x20000000, LENGTH = 128K
6+
}

examples/trouble/src/main.rs

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
#![no_std]
2+
#![no_main]
3+
4+
use {defmt_rtt as _, panic_probe as _};
5+
6+
use defmt::{error, info};
7+
use embassy_executor::Spawner;
8+
use embassy_futures::join::join3;
9+
use embassy_time::{Duration, Timer};
10+
use microbit_bsp::{ble::MultiprotocolServiceLayer, Config, Microbit};
11+
use trouble_host::prelude::*;
12+
13+
/// Size of L2CAP packets (ATT MTU is this - 4)
14+
const L2CAP_MTU: usize = 251;
15+
16+
/// Max number of connections
17+
const CONNECTIONS_MAX: usize = 1;
18+
19+
/// Max number of L2CAP channels.
20+
const L2CAP_CHANNELS_MAX: usize = 2; // Signal + att
21+
22+
type Resources<C> = HostResources<C, CONNECTIONS_MAX, L2CAP_CHANNELS_MAX, L2CAP_MTU>;
23+
24+
// GATT Server definition
25+
#[gatt_server(attribute_data_size = 10)]
26+
struct Server {
27+
battery_service: BatteryService,
28+
}
29+
30+
// Battery service
31+
#[gatt_service(uuid = "180f")]
32+
struct BatteryService {
33+
#[characteristic(uuid = "2a19", read, notify)]
34+
level: u8,
35+
}
36+
37+
#[embassy_executor::task]
38+
async fn mpsl_task(mpsl: &'static MultiprotocolServiceLayer<'static>) -> ! {
39+
mpsl.run().await
40+
}
41+
42+
#[embassy_executor::main]
43+
async fn main(spawner: Spawner) {
44+
let board = Microbit::new(Config::default());
45+
let (sdc, mpsl) = board
46+
.ble
47+
.init(board.timer0, board.rng)
48+
.expect("BLE Stack failed to initialize");
49+
spawner.must_spawn(mpsl_task(mpsl));
50+
51+
run(sdc).await;
52+
}
53+
54+
pub async fn run<C>(controller: C)
55+
where
56+
C: Controller,
57+
{
58+
let address = Address::random([0x41, 0x5A, 0xE3, 0x1E, 0x83, 0xE7]);
59+
info!("Our address = {:?}", address);
60+
61+
let mut resources = Resources::new(PacketQos::None);
62+
let (stack, peripheral, _, runner) = trouble_host::new(controller, &mut resources)
63+
.set_random_address(address)
64+
.build();
65+
66+
let server = Server::new_with_config(
67+
stack,
68+
GapConfig::Peripheral(PeripheralConfig {
69+
name: "Trouble",
70+
appearance: &appearance::GENERIC_POWER,
71+
}),
72+
)
73+
.expect("Failed to create GATT server");
74+
75+
info!("Starting advertising and GATT service");
76+
let _ = join3(
77+
ble_task(runner),
78+
gatt_task(&server),
79+
advertise_task(peripheral, &server),
80+
)
81+
.await;
82+
}
83+
84+
async fn ble_task<C: Controller>(mut runner: Runner<'_, C>) -> Result<(), BleHostError<C::Error>> {
85+
runner.run().await
86+
}
87+
88+
async fn gatt_task<C: Controller>(server: &Server<'_, '_, C>) {
89+
loop {
90+
match server.next().await {
91+
Ok(GattEvent::Write {
92+
value_handle,
93+
connection: _,
94+
}) => {
95+
info!("[gatt] Write event on {:?}", value_handle);
96+
}
97+
Ok(GattEvent::Read {
98+
value_handle,
99+
connection: _,
100+
}) => {
101+
info!("[gatt] Read event on {:?}", value_handle);
102+
}
103+
Err(e) => {
104+
error!("[gatt] Error processing GATT events: {:?}", e);
105+
}
106+
}
107+
}
108+
}
109+
110+
async fn advertise_task<C: Controller>(
111+
mut peripheral: Peripheral<'_, C>,
112+
server: &Server<'_, '_, C>,
113+
) -> Result<(), BleHostError<C::Error>> {
114+
let mut adv_data = [0; 31];
115+
AdStructure::encode_slice(
116+
&[
117+
AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED),
118+
AdStructure::ServiceUuids16(&[Uuid::Uuid16([0x0f, 0x18])]),
119+
AdStructure::CompleteLocalName(b"Trouble"),
120+
],
121+
&mut adv_data[..],
122+
)?;
123+
loop {
124+
info!("[adv] advertising");
125+
let mut advertiser = peripheral
126+
.advertise(
127+
&Default::default(),
128+
Advertisement::ConnectableScannableUndirected {
129+
adv_data: &adv_data[..],
130+
scan_data: &[],
131+
},
132+
)
133+
.await?;
134+
let conn = advertiser.accept().await?;
135+
info!("[adv] connection established");
136+
// Keep connection alive
137+
let mut tick: u8 = 0;
138+
while conn.is_connected() {
139+
Timer::after(Duration::from_secs(2)).await;
140+
tick = tick.wrapping_add(1);
141+
info!("[adv] notifying connection of tick {}", tick);
142+
let _ = server.notify(&server.battery_service.level, &conn, &tick).await;
143+
}
144+
}
145+
}

0 commit comments

Comments
 (0)