summaryrefslogtreecommitdiff
path: root/examples/src/bin/ble_peripheral_onoff.rs
diff options
context:
space:
mode:
authorDario Nieuwenhuis <dirbaio@dirbaio.net>2020-11-10 15:22:22 +0100
committerDario Nieuwenhuis <dirbaio@dirbaio.net>2020-11-10 15:22:22 +0100
commit271eb2224230f31349ff3e69f76bde9db594d0e4 (patch)
treee991a84634567e57c7843aa1ba3fc523777ff9ae /examples/src/bin/ble_peripheral_onoff.rs
parent1c15bd7677a77ba8a453ac3723d0153b0120d473 (diff)
downloadnrf-softdevice-271eb2224230f31349ff3e69f76bde9db594d0e4.zip
Properly handle future drops. Add on/off example showcasing it.
Diffstat (limited to 'examples/src/bin/ble_peripheral_onoff.rs')
-rw-r--r--examples/src/bin/ble_peripheral_onoff.rs180
1 files changed, 180 insertions, 0 deletions
diff --git a/examples/src/bin/ble_peripheral_onoff.rs b/examples/src/bin/ble_peripheral_onoff.rs
new file mode 100644
index 0000000..800788c
--- /dev/null
+++ b/examples/src/bin/ble_peripheral_onoff.rs
@@ -0,0 +1,180 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+#[path = "../example_common.rs"]
+mod example_common;
+use example_common::*;
+
+use anyfmt::{panic, *};
+use core::mem;
+use cortex_m_rt::entry;
+use embassy::executor::{task, Executor};
+use embassy::util::Forever;
+use embassy_nrf::gpiote::{Gpiote, PortInputPolarity};
+use futures::pin_mut;
+use nrf52840_hal::gpio;
+
+use nrf_softdevice::ble::{gatt_server, peripheral, Connection};
+use nrf_softdevice::{pac, raw, RawError, Softdevice};
+
+static EXECUTOR: Forever<Executor> = Forever::new();
+
+#[task]
+async fn softdevice_task(sd: &'static Softdevice) {
+ sd.run().await;
+}
+
+#[nrf_softdevice::gatt_server(uuid = "9e7312e0-2354-11eb-9f10-fbc30a62cf38")]
+struct FooService {
+ #[characteristic(uuid = "9e7312e0-2354-11eb-9f10-fbc30a63cf38", read, write, notify)]
+ foo: u16,
+}
+
+async fn run_bluetooth(sd: &'static Softdevice, server: &FooService) {
+ #[rustfmt::skip]
+ let adv_data = &[
+ 0x02, 0x01, raw::BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE as u8,
+ 0x03, 0x03, 0x09, 0x18,
+ 0x0a, 0x09, b'H', b'e', b'l', b'l', b'o', b'R', b'u', b's', b't',
+ ];
+ #[rustfmt::skip]
+ let scan_data = &[
+ 0x03, 0x03, 0x09, 0x18,
+ ];
+
+ loop {
+ let conn = unwrap!(
+ peripheral::advertise(
+ sd,
+ peripheral::ConnectableAdvertisement::ScannableUndirected {
+ adv_data,
+ scan_data,
+ },
+ peripheral::Config::default(),
+ )
+ .await
+ );
+
+ info!("advertising done!");
+
+ let res = gatt_server::run(&conn, server, |e| match e {
+ FooServiceEvent::FooWrite(val) => {
+ info!("wrote foo level: {:u16}", val);
+ if let Err(e) = server.foo_notify(&conn, val + 1) {
+ info!("send notification error: {:?}", e);
+ }
+ }
+ FooServiceEvent::FooNotificationsEnabled => info!("notifications enabled"),
+ FooServiceEvent::FooNotificationsDisabled => info!("notifications disabled"),
+ })
+ .await;
+
+ if let Err(e) = res {
+ info!("gatt_server run exited with error: {:?}", e);
+ }
+ }
+}
+
+#[task]
+async fn bluetooth_task(sd: &'static Softdevice, gpiote: pac::GPIOTE, p0: pac::P0) {
+ let server: FooService = unwrap!(gatt_server::register(sd));
+
+ let port0 = gpio::p0::Parts::new(p0);
+ let gpiote = Gpiote::new(gpiote);
+
+ info!("Bluetooth is OFF");
+ info!("Press nrf52840-dk button 1 to enable, button 2 to disable");
+
+ let button1 = port0.p0_11.into_pullup_input().degrade();
+ let button2 = port0.p0_12.into_pullup_input().degrade();
+
+ loop {
+ gpiote
+ .wait_port_input(&button1, PortInputPolarity::Low)
+ .await;
+ info!("Bluetooth ON!");
+
+ // Create a future that will run the bluetooth loop.
+ // Note the lack of `.await`! This creates the future but doesn't poll it yet.
+ let bluetooth_fut = run_bluetooth(sd, &server);
+
+ // Create a future that will resolve when the OFF button is pressed.
+ let off_fut = async {
+ gpiote
+ .wait_port_input(&button2, PortInputPolarity::Low)
+ .await;
+ info!("Bluetooth OFF!");
+ };
+
+ pin_mut!(bluetooth_fut);
+ pin_mut!(off_fut);
+
+ // Select the two futures.
+ //
+ // select() returns when one of the two futures returns. The other future is dropped before completing.
+ //
+ // Since the bluetooth future never finishes, this can only happen when the Off button is pressed.
+ // This will cause the bluetooth future to be dropped.
+ //
+ // If it was advertising, the nested `peripheral::advertise` future will be dropped, which will cause
+ // the softdevice to stop advertising.
+ // If it was connected, it will drop everything including the `Connection` instance, which
+ // will tell the softdevice to disconnect it.
+ //
+ // This demonstrates the awesome power of Rust's async-await combined with nrf-softdevice's async wrappers.
+ // It's super easy to cancel a complex tree of operations: just drop its future!
+ futures::future::select(bluetooth_fut, off_fut).await;
+ }
+}
+
+#[entry]
+fn main() -> ! {
+ info!("Hello World!");
+
+ let config = nrf_softdevice::Config {
+ clock: Some(raw::nrf_clock_lf_cfg_t {
+ source: raw::NRF_CLOCK_LF_SRC_XTAL as u8,
+ rc_ctiv: 0,
+ rc_temp_ctiv: 0,
+ accuracy: 7,
+ }),
+ conn_gap: Some(raw::ble_gap_conn_cfg_t {
+ conn_count: 6,
+ event_length: 24,
+ }),
+ conn_gatt: Some(raw::ble_gatt_conn_cfg_t { att_mtu: 256 }),
+ gatts_attr_tab_size: Some(raw::ble_gatts_cfg_attr_tab_size_t {
+ attr_tab_size: 32768,
+ }),
+ gap_role_count: Some(raw::ble_gap_cfg_role_count_t {
+ adv_set_count: 1,
+ periph_role_count: 3,
+ central_role_count: 3,
+ central_sec_count: 0,
+ _bitfield_1: raw::ble_gap_cfg_role_count_t::new_bitfield_1(0),
+ }),
+ gap_device_name: Some(raw::ble_gap_cfg_device_name_t {
+ p_value: b"HelloRust" as *const u8 as _,
+ current_len: 9,
+ max_len: 9,
+ write_perm: unsafe { mem::zeroed() },
+ _bitfield_1: raw::ble_gap_cfg_device_name_t::new_bitfield_1(
+ raw::BLE_GATTS_VLOC_STACK as u8,
+ ),
+ }),
+ ..Default::default()
+ };
+
+ let (sdp, p) = take_peripherals();
+ let sd = Softdevice::enable(sdp, &config);
+
+ let executor = EXECUTOR.put(Executor::new(cortex_m::asm::sev));
+ unwrap!(executor.spawn(softdevice_task(sd)));
+ unwrap!(executor.spawn(bluetooth_task(sd, p.GPIOTE, p.P0,)));
+
+ loop {
+ executor.run();
+ cortex_m::asm::wfe();
+ }
+}