Skip to content

Commit 31dc250

Browse files
authored
Merge branch 'main' into pr-typos-and-what
2 parents e2627fd + 0dc5a58 commit 31dc250

28 files changed

+367
-95
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use embassy_futures::join::{join, join3};
2+
use embassy_time::{Duration, Timer};
3+
use trouble_host::prelude::*;
4+
5+
/// Max number of connections
6+
const CONNECTIONS_MAX: usize = 2;
7+
8+
/// Max number of L2CAP channels.
9+
const L2CAP_CHANNELS_MAX: usize = 4; // Signal + att + CoC
10+
11+
pub async fn run<C>(controller: C)
12+
where
13+
C: Controller,
14+
{
15+
// Using a fixed "random" address can be useful for testing. In real scenarios, one would
16+
// use e.g. the MAC 6 byte array as the address (how to get that varies by the platform).
17+
let address: Address = Address::random([0xff, 0x8f, 0x1b, 0x05, 0xe4, 0xff]);
18+
info!("Our address = {:?}", address);
19+
20+
let mut resources: HostResources<DefaultPacketPool, CONNECTIONS_MAX, L2CAP_CHANNELS_MAX> = HostResources::new();
21+
let stack = trouble_host::new(controller, &mut resources).set_random_address(address);
22+
let Host { mut runner, .. } = stack.build();
23+
24+
info!("Scanning for peripheral...");
25+
// NOTE: Modify this to match the address of the peripheral you want to connect to.
26+
let fut1 = scan(&stack, [0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]);
27+
let fut2 = scan(&stack, [0xff, 0x8f, 0x1a, 0x05, 0xe5, 0xff]);
28+
let _ = join3(runner.run(), fut1, fut2).await;
29+
}
30+
31+
async fn scan<'a, C: Controller, P: PacketPool>(stack: &'a Stack<'a, C, P>, addr: [u8; 6]) {
32+
let Host { mut central, .. } = stack.build();
33+
let target: Address = Address::random(addr);
34+
35+
let config = ConnectConfig {
36+
connect_params: Default::default(),
37+
scan_config: ScanConfig {
38+
filter_accept_list: &[(target.kind, &target.addr)],
39+
..Default::default()
40+
},
41+
};
42+
43+
info!("Connecting to {:?}", addr);
44+
let conn = central.connect(&config).await.unwrap();
45+
info!("Connected, creating gatt client");
46+
47+
let client = GattClient::<C, _, 10>::new(stack, &conn).await.unwrap();
48+
49+
let _ = join(client.task(), async {
50+
info!("Looking for battery service");
51+
let services = client.services_by_uuid(&Uuid::new_short(0x180f)).await.unwrap();
52+
let service = services.first().unwrap().clone();
53+
54+
info!("Looking for value handle");
55+
let c: Characteristic<u8> = client
56+
.characteristic_by_uuid(&service, &Uuid::new_short(0x2a19))
57+
.await
58+
.unwrap();
59+
60+
info!("Subscribing notifications");
61+
let mut listener = client.subscribe(&c, false).await.unwrap();
62+
63+
let _ = join(
64+
async {
65+
loop {
66+
let mut data = [0; 1];
67+
client.read_characteristic(&c, &mut data[..]).await.unwrap();
68+
info!("Read value: {}", data[0]);
69+
Timer::after(Duration::from_secs(10)).await;
70+
}
71+
},
72+
async {
73+
loop {
74+
let data = listener.next().await;
75+
info!("Got notification: {:?} (val: {})", data.as_ref(), data.as_ref()[0]);
76+
}
77+
},
78+
)
79+
.await;
80+
})
81+
.await;
82+
}

examples/apps/src/ble_bas_peripheral_bonding.rs

Lines changed: 73 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,38 @@ use trouble_host::prelude::*;
1212
const CONNECTIONS_MAX: usize = 1;
1313

1414
/// Max number of L2CAP channels.
15-
const L2CAP_CHANNELS_MAX: usize = 2; // Signal + att
15+
const L2CAP_CHANNELS_MAX: usize = 4; // Signal + att
1616

1717
// GATT Server definition
1818
#[gatt_server]
1919
struct Server {
2020
battery_service: BatteryService,
21+
hid_service: HidService,
22+
}
23+
24+
static DESC: [u8; 67] = [
25+
5u8, 1u8, 9u8, 6u8, 161u8, 1u8, 5u8, 7u8, 25u8, 224u8, 41u8, 231u8, 21u8, 0u8, 37u8, 1u8, 117u8, 1u8, 149u8, 8u8,
26+
129u8, 2u8, 21u8, 0u8, 38u8, 255u8, 0u8, 117u8, 8u8, 149u8, 1u8, 129u8, 3u8, 5u8, 8u8, 25u8, 1u8, 41u8, 5u8, 37u8,
27+
1u8, 117u8, 1u8, 149u8, 5u8, 145u8, 2u8, 149u8, 3u8, 145u8, 3u8, 5u8, 7u8, 25u8, 0u8, 41u8, 221u8, 38u8, 255u8,
28+
0u8, 117u8, 8u8, 149u8, 6u8, 129u8, 0u8, 192u8,
29+
];
30+
31+
#[gatt_service(uuid = service::HUMAN_INTERFACE_DEVICE)]
32+
pub(crate) struct HidService {
33+
#[characteristic(uuid = "2a4a", read, value = [0x01, 0x01, 0x00, 0x03])]
34+
pub(crate) hid_info: [u8; 4],
35+
#[characteristic(uuid = "2a4b", read, value = DESC)]
36+
pub(crate) report_map: [u8; 67],
37+
#[characteristic(uuid = "2a4c", write_without_response)]
38+
pub(crate) hid_control_point: u8,
39+
#[characteristic(uuid = "2a4e", read, write_without_response, value = 1)]
40+
pub(crate) protocol_mode: u8,
41+
#[descriptor(uuid = "2908", read, value = [0u8, 1u8])]
42+
#[characteristic(uuid = "2a4d", read, notify)]
43+
pub(crate) input_keyboard: [u8; 8],
44+
#[descriptor(uuid = "2908", read, value = [0u8, 2u8])]
45+
#[characteristic(uuid = "2a4d", read, write, write_without_response)]
46+
pub(crate) output_keyboard: [u8; 1],
2147
}
2248

2349
/// Battery service
@@ -47,8 +73,7 @@ impl Key for StoredAddr {
4773
fn deserialize_from(buffer: &[u8]) -> Result<(Self, usize), SerializationError> {
4874
if buffer.len() < 6 {
4975
Err(SerializationError::BufferTooSmall)
50-
}
51-
else {
76+
} else {
5277
Ok((StoredAddr(BdAddr::new(buffer[0..6].try_into().unwrap())), 6))
5378
}
5479
}
@@ -75,43 +100,56 @@ impl<'a> Value<'a> for StoredBondInformation {
75100

76101
fn deserialize_from(buffer: &'a [u8]) -> Result<Self, SerializationError>
77102
where
78-
Self: Sized
103+
Self: Sized,
79104
{
80105
if buffer.len() < 17 {
81106
Err(SerializationError::BufferTooSmall)
82-
}
83-
else {
107+
} else {
84108
let ltk = LongTermKey::from_le_bytes(buffer[0..16].try_into().unwrap());
85109
let security_level = match buffer[16] {
86110
0 => SecurityLevel::NoEncryption,
87111
1 => SecurityLevel::Encrypted,
88112
2 => SecurityLevel::EncryptedAuthenticated,
89-
_ => return Err(SerializationError::InvalidData)
113+
_ => return Err(SerializationError::InvalidData),
90114
};
91115
Ok(StoredBondInformation { ltk, security_level })
92116
}
93117
}
94118
}
95119

96120
fn flash_range<S: NorFlash>() -> Range<u32> {
97-
0..2*S::ERASE_SIZE as u32
121+
0..2 * S::ERASE_SIZE as u32
98122
}
99123

100-
async fn store_bonding_info<S: NorFlash>(storage: &mut S, info: &BondInformation) -> Result<(), sequential_storage::Error<S::Error>> {
101-
// Assumes that S::ERASE_SIZE is large enough
102-
sequential_storage::erase_all(storage, 0..S::ERASE_SIZE as u32).await?;
103-
let mut buffer = [0;32];
124+
async fn store_bonding_info<S: NorFlash>(
125+
storage: &mut S,
126+
info: &BondInformation,
127+
) -> Result<(), sequential_storage::Error<S::Error>> {
128+
// Use flash range from 640KB, should be good for both ESP32 & nRF52840 examples
129+
let start_addr = 0xA0000 as u32;
130+
let storage_range = start_addr..(start_addr + 8 * S::ERASE_SIZE as u32);
131+
sequential_storage::erase_all(storage, storage_range.clone()).await?;
132+
let mut buffer = [0; 32];
104133
let key = StoredAddr(info.identity.bd_addr);
105-
let value = StoredBondInformation { ltk: info.ltk, security_level: info.security_level };
106-
sequential_storage::map::store_item(storage, flash_range::<S>(), &mut NoCache::new(), &mut buffer, &key, &value).await?;
134+
let value = StoredBondInformation {
135+
ltk: info.ltk,
136+
security_level: info.security_level,
137+
};
138+
sequential_storage::map::store_item(storage, storage_range, &mut NoCache::new(), &mut buffer, &key, &value).await?;
107139
Ok(())
108140
}
109141

110-
async fn load_bonding_info<S: NorFlash>(storage: &mut S) -> Option<BondInformation>
111-
{
112-
let mut buffer = [0;32];
142+
async fn load_bonding_info<S: NorFlash>(storage: &mut S) -> Option<BondInformation> {
143+
let mut buffer = [0; 32];
113144
let mut cache = NoCache::new();
114-
let mut iter = sequential_storage::map::fetch_all_items::<StoredAddr, _, _>(storage, flash_range::<S>(), &mut cache, &mut buffer).await.ok()?;
145+
let mut iter = sequential_storage::map::fetch_all_items::<StoredAddr, _, _>(
146+
storage,
147+
flash_range::<S>(),
148+
&mut cache,
149+
&mut buffer,
150+
)
151+
.await
152+
.ok()?;
115153
while let Some((key, value)) = iter.next::<StoredBondInformation>(&mut buffer).await.ok()? {
116154
return Some(BondInformation {
117155
identity: Identity {
@@ -120,7 +158,7 @@ async fn load_bonding_info<S: NorFlash>(storage: &mut S) -> Option<BondInformati
120158
},
121159
security_level: value.security_level,
122160
is_bonded: true,
123-
ltk: value.ltk
161+
ltk: value.ltk,
124162
});
125163
}
126164
None
@@ -147,8 +185,7 @@ where
147185
info!("Loaded bond information");
148186
stack.add_bond_information(bond_info).unwrap();
149187
true
150-
}
151-
else {
188+
} else {
152189
info!("No bond information found");
153190
false
154191
};
@@ -160,9 +197,9 @@ where
160197
info!("Starting advertising and GATT service");
161198
let server = Server::new_with_config(GapConfig::Peripheral(PeripheralConfig {
162199
name: "TrouBLE",
163-
appearance: &appearance::power_device::GENERIC_POWER_DEVICE,
200+
appearance: &appearance::human_interface_device::GENERIC_HUMAN_INTERFACE_DEVICE,
164201
}))
165-
.unwrap();
202+
.unwrap();
166203

167204
let _ = join(ble_task(runner), async {
168205
loop {
@@ -176,6 +213,7 @@ where
176213
// run until any task ends (usually because the connection has been closed),
177214
// then return to advertising state.
178215
select(a, b).await;
216+
info!("Connection dropped");
179217
}
180218
Err(e) => {
181219
#[cfg(feature = "defmt")]
@@ -185,7 +223,7 @@ where
185223
}
186224
}
187225
})
188-
.await;
226+
.await;
189227
}
190228

191229
/// This is a background task that is required to run forever alongside any other BLE tasks.
@@ -217,13 +255,18 @@ async fn ble_task<C: Controller, P: PacketPool>(mut runner: Runner<'_, C, P>) {
217255
///
218256
/// This function will handle the GATT events and process them.
219257
/// This is how we interact with read and write requests.
220-
async fn gatt_events_task<S: NorFlash>(storage: &mut S, server: &Server<'_>, conn: &GattConnection<'_, '_, DefaultPacketPool>, bond_stored: &mut bool) -> Result<(), Error> {
258+
async fn gatt_events_task<S: NorFlash>(
259+
storage: &mut S,
260+
server: &Server<'_>,
261+
conn: &GattConnection<'_, '_, DefaultPacketPool>,
262+
bond_stored: &mut bool,
263+
) -> Result<(), Error> {
221264
let level = server.battery_service.level;
222265
let reason = loop {
223266
match conn.next().await {
224267
GattConnectionEvent::Disconnected { reason } => break reason,
225268
#[cfg(feature = "security")]
226-
GattConnectionEvent::PairingComplete { security_level, bond} => {
269+
GattConnectionEvent::PairingComplete { security_level, bond } => {
227270
info!("[gatt] pairing complete: {:?}", security_level);
228271
if let Some(bond) = bond {
229272
store_bonding_info(storage, &bond).await.unwrap();
@@ -294,7 +337,10 @@ async fn advertise<'values, 'server, C: Controller>(
294337
let len = AdStructure::encode_slice(
295338
&[
296339
AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED),
297-
AdStructure::ServiceUuids16(&[[0x0f, 0x18]]),
340+
AdStructure::ServiceUuids16(&[
341+
service::BATTERY.to_le_bytes(),
342+
service::HUMAN_INTERFACE_DEVICE.to_le_bytes(),
343+
]),
298344
AdStructure::CompleteLocalName(name.as_bytes()),
299345
],
300346
&mut advertiser_data[..],

examples/apps/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub(crate) mod fmt;
77
pub mod ble_advertise;
88
pub mod ble_advertise_multiple;
99
pub mod ble_bas_central;
10+
pub mod ble_bas_central_multiple;
1011
pub mod ble_bas_central_sec;
1112
pub mod ble_bas_peripheral;
1213
pub mod ble_bas_peripheral_sec;

examples/esp32/Cargo.lock

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/esp32/Cargo.toml

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ license = "MIT OR Apache-2.0"
66

77
[dependencies]
88
embassy-executor = "0.9.0"
9+
embassy-embedded-hal = "0.5"
910
esp-bootloader-esp-idf = "0.2.0"
1011
esp-backtrace = { version = "0.17.0", features = [ "panic-handler", "println" ] }
1112
esp-hal = { version = "1.0.0-rc.0", features = [ "unstable" ] }
1213
esp-hal-embassy = { version = "0.9.0" }
1314
esp-alloc = { version = "0.8.0" }
15+
esp-storage = { version = "0.7.0" }
1416
esp-println = { version = "0.15.0", features = ["log-04"] }
1517
esp-radio = { version = "0.15.0", features = [ "unstable", "ble" ] }
1618
esp-preempt = { version = "0.0.1", features = ["log-04"] }
@@ -21,12 +23,12 @@ static_cell = "2"
2123
[features]
2224
default = ["esp32c3"]
2325

24-
esp32 = ["esp-hal/esp32", "esp-backtrace/esp32", "esp-hal-embassy/esp32", "esp-println/esp32", "esp-radio/esp32", "esp-preempt/esp32", "esp-bootloader-esp-idf/esp32"]
25-
esp32c2 = ["esp-hal/esp32c2", "esp-backtrace/esp32c2", "esp-hal-embassy/esp32c2", "esp-println/esp32c2", "esp-preempt/esp32c2", "esp-radio/esp32c2", "esp-bootloader-esp-idf/esp32c2"]
26-
esp32c3 = ["esp-hal/esp32c3", "esp-backtrace/esp32c3", "esp-hal-embassy/esp32c3", "esp-println/esp32c3", "esp-preempt/esp32c3", "esp-radio/esp32c3", "esp-bootloader-esp-idf/esp32c3"]
27-
esp32c6 = ["esp-hal/esp32c6", "esp-backtrace/esp32c6", "esp-hal-embassy/esp32c6", "esp-println/esp32c6", "esp-preempt/esp32c6", "esp-radio/esp32c6", "esp-bootloader-esp-idf/esp32c6"]
28-
esp32h2 = ["esp-hal/esp32h2", "esp-backtrace/esp32h2", "esp-hal-embassy/esp32h2", "esp-println/esp32h2", "esp-preempt/esp32h2", "esp-radio/esp32h2", "esp-bootloader-esp-idf/esp32h2"]
29-
esp32s3 = ["esp-hal/esp32s3", "esp-backtrace/esp32s3", "esp-hal-embassy/esp32s3", "esp-println/esp32s3", "esp-preempt/esp32s3", "esp-radio/esp32s3", "esp-bootloader-esp-idf/esp32s3"]
26+
esp32 = ["esp-hal/esp32", "esp-backtrace/esp32", "esp-hal-embassy/esp32", "esp-println/esp32", "esp-storage/esp32", "esp-radio/esp32", "esp-preempt/esp32", "esp-bootloader-esp-idf/esp32"]
27+
esp32c2 = ["esp-hal/esp32c2", "esp-backtrace/esp32c2", "esp-hal-embassy/esp32c2", "esp-println/esp32c2", "esp-storage/esp32c2", "esp-preempt/esp32c2", "esp-radio/esp32c2", "esp-bootloader-esp-idf/esp32c2"]
28+
esp32c3 = ["esp-hal/esp32c3", "esp-backtrace/esp32c3", "esp-hal-embassy/esp32c3", "esp-println/esp32c3", "esp-storage/esp32c3", "esp-preempt/esp32c3", "esp-radio/esp32c3", "esp-bootloader-esp-idf/esp32c3"]
29+
esp32c6 = ["esp-hal/esp32c6", "esp-backtrace/esp32c6", "esp-hal-embassy/esp32c6", "esp-println/esp32c6", "esp-storage/esp32c6", "esp-preempt/esp32c6", "esp-radio/esp32c6", "esp-bootloader-esp-idf/esp32c6"]
30+
esp32h2 = ["esp-hal/esp32h2", "esp-backtrace/esp32h2", "esp-hal-embassy/esp32h2", "esp-println/esp32h2", "esp-storage/esp32h2", "esp-preempt/esp32h2", "esp-radio/esp32h2", "esp-bootloader-esp-idf/esp32h2"]
31+
esp32s3 = ["esp-hal/esp32s3", "esp-backtrace/esp32s3", "esp-hal-embassy/esp32s3", "esp-println/esp32s3", "esp-storage/esp32s3", "esp-preempt/esp32s3", "esp-radio/esp32s3", "esp-bootloader-esp-idf/esp32s3"]
3032

3133
security = [
3234
"trouble-example-apps/security",
@@ -54,9 +56,14 @@ required-features = ["security"]
5456
name = "ble_bas_peripheral_sec"
5557
required-features = ["security"]
5658

59+
[[bin]]
60+
name = "ble_bas_peripheral_bonding"
61+
required-features = ["security"]
62+
5763
[patch.crates-io]
5864
esp-radio = {git = "https://github.com/lulf/esp-hal.git", branch = "bt-hci-fix"}
5965
esp-preempt = {git = "https://github.com/lulf/esp-hal.git", branch = "bt-hci-fix"}
66+
esp-storage = {git = "https://github.com/lulf/esp-hal.git", branch = "bt-hci-fix"}
6067
esp-backtrace = {git = "https://github.com/lulf/esp-hal.git", branch = "bt-hci-fix"}
6168
esp-hal = {git = "https://github.com/lulf/esp-hal.git", branch = "bt-hci-fix"}
6269
esp-bootloader-esp-idf = {git = "https://github.com/lulf/esp-hal.git", branch = "bt-hci-fix"}

0 commit comments

Comments
 (0)