Skip to content

Commit 0dc5a58

Browse files
authored
Merge pull request #468 from HaoboGu/example/bonding
Improve bonding example, add ESP32 bonding example
2 parents 0af97d0 + 133aa5f commit 0dc5a58

21 files changed

+200
-80
lines changed

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/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"}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#![no_std]
2+
#![no_main]
3+
4+
use embassy_executor::Spawner;
5+
use esp_hal::clock::CpuClock;
6+
use esp_hal::rng::{Trng, TrngSource};
7+
use esp_hal::timer::timg::TimerGroup;
8+
use esp_radio::Controller;
9+
use esp_radio::ble::controller::BleConnector;
10+
use esp_storage::FlashStorage;
11+
use static_cell::StaticCell;
12+
use trouble_example_apps::ble_bas_peripheral_bonding;
13+
use trouble_host::prelude::ExternalController;
14+
use {esp_alloc as _, esp_backtrace as _};
15+
16+
esp_bootloader_esp_idf::esp_app_desc!();
17+
18+
#[esp_hal_embassy::main]
19+
async fn main(_s: Spawner) {
20+
esp_println::logger::init_logger_from_env();
21+
let peripherals = esp_hal::init(esp_hal::Config::default().with_cpu_clock(CpuClock::max()));
22+
esp_alloc::heap_allocator!(size: 72 * 1024);
23+
let timg0 = TimerGroup::new(peripherals.TIMG0);
24+
#[cfg(target_arch = "riscv32")]
25+
let software_interrupt = esp_hal::interrupt::software::SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
26+
27+
esp_preempt::start(
28+
timg0.timer0,
29+
#[cfg(target_arch = "riscv32")]
30+
software_interrupt.software_interrupt0,
31+
);
32+
33+
let _trng_source = TrngSource::new(peripherals.RNG, peripherals.ADC1);
34+
let mut trng = Trng::try_new().unwrap(); // Ok when there's a TrngSource accessible
35+
36+
static RADIO: StaticCell<Controller<'static>> = StaticCell::new();
37+
let radio = RADIO.init(esp_radio::init().unwrap());
38+
39+
#[cfg(not(feature = "esp32"))]
40+
{
41+
let systimer = esp_hal::timer::systimer::SystemTimer::new(peripherals.SYSTIMER);
42+
esp_hal_embassy::init(systimer.alarm0);
43+
}
44+
#[cfg(feature = "esp32")]
45+
{
46+
esp_hal_embassy::init(timg0.timer1);
47+
}
48+
49+
let bluetooth = peripherals.BT;
50+
let connector = BleConnector::new(radio, bluetooth);
51+
let controller: ExternalController<_, 20> = ExternalController::new(connector);
52+
53+
// Initialize the flash
54+
let mut flash = embassy_embedded_hal::adapter::BlockingAsync::new(FlashStorage::new(peripherals.FLASH));
55+
56+
// ble_bas_peripheral_sec::run(controller, &mut trng).await;
57+
ble_bas_peripheral_bonding::run(controller, &mut trng, &mut flash).await;
58+
}

examples/nrf-sdc/Cargo.lock

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

0 commit comments

Comments
 (0)