summaryrefslogtreecommitdiff
path: root/examples/src
diff options
context:
space:
mode:
authorDario Nieuwenhuis <dirbaio@dirbaio.net>2020-09-14 17:47:43 +0200
committerDario Nieuwenhuis <dirbaio@dirbaio.net>2020-09-14 17:47:43 +0200
commit1d840cf4b190397f5944ed1fb7e9007e5a2d82dc (patch)
tree5e259e4ee5234c6a9b5a8e382ef19b943c867fcf /examples/src
parentcb6771aceb53281797e578256a661ecbb1286f87 (diff)
downloadnrf-softdevice-1d840cf4b190397f5944ed1fb7e9007e5a2d82dc.zip
example -> examples
Diffstat (limited to 'examples/src')
-rw-r--r--examples/src/bin/ble_bas_central.rs142
-rw-r--r--examples/src/bin/ble_bas_peripheral.rs155
-rw-r--r--examples/src/bin/ble_peripheral_gattspam.rs159
-rw-r--r--examples/src/bin/flash.rs47
-rw-r--r--examples/src/bin/interrupts.rs93
-rw-r--r--examples/src/bin/rtic.rs135
-rw-r--r--examples/src/example_common.rs68
7 files changed, 799 insertions, 0 deletions
diff --git a/examples/src/bin/ble_bas_central.rs b/examples/src/bin/ble_bas_central.rs
new file mode 100644
index 0000000..2fa4807
--- /dev/null
+++ b/examples/src/bin/ble_bas_central.rs
@@ -0,0 +1,142 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+#[path = "../example_common.rs"]
+mod example_common;
+use example_common::*;
+
+use core::mem;
+use cortex_m_rt::entry;
+use defmt::info;
+
+use nrf_softdevice::ble::{central, gatt_client, Address, Connection, Uuid};
+use nrf_softdevice::raw;
+use nrf_softdevice::Softdevice;
+
+#[static_executor::task]
+async fn softdevice_task(sd: &'static Softdevice) {
+ sd.run().await;
+}
+
+struct BatteryServiceClient {
+ conn: Connection,
+ battery_level_value_handle: u16,
+ battery_level_cccd_handle: u16,
+}
+
+const GATT_BAS_SVC_UUID: Uuid = Uuid::new_16(0x180F);
+const GATT_BAS_BATTERY_LEVEL_CHAR_UUID: Uuid = Uuid::new_16(0x2A19);
+
+// This is mostly boilerplate, ideally it'll be generated with a proc macro in the future.
+impl gatt_client::Client for BatteryServiceClient {
+ fn uuid() -> Uuid {
+ return GATT_BAS_SVC_UUID;
+ }
+
+ fn new_undiscovered(conn: Connection) -> Self {
+ Self {
+ conn,
+ battery_level_value_handle: 0,
+ battery_level_cccd_handle: 0,
+ }
+ }
+
+ fn discovered_characteristic(
+ &mut self,
+ characteristic: &gatt_client::Characteristic,
+ descriptors: &[gatt_client::Descriptor],
+ ) {
+ if let Some(char_uuid) = characteristic.uuid {
+ if char_uuid == GATT_BAS_BATTERY_LEVEL_CHAR_UUID {
+ // TODO maybe check the char_props have the necessary operations allowed? read/write/notify/etc
+ self.battery_level_value_handle = characteristic.handle_value;
+ for desc in descriptors {
+ if let Some(desc_uuid) = desc.uuid {
+ if desc_uuid
+ == Uuid::new_16(raw::BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG as u16)
+ {
+ self.battery_level_cccd_handle = desc.handle;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ fn discovery_complete(&mut self) -> Result<(), gatt_client::DiscoverError> {
+ if self.battery_level_cccd_handle == 0 || self.battery_level_value_handle == 0 {
+ return Err(gatt_client::DiscoverError::ServiceIncomplete);
+ }
+ Ok(())
+ }
+}
+
+#[static_executor::task]
+async fn ble_central_task(sd: &'static Softdevice) {
+ let addrs = &[Address::new_random_static([
+ 0x59, 0xf9, 0xb1, 0x9c, 0x01, 0xf5,
+ ])];
+
+ let conn = central::connect(sd, addrs)
+ .await
+ .dexpect(intern!("connect"));
+ info!("connected");
+
+ let client: BatteryServiceClient = gatt_client::discover(&conn)
+ .await
+ .dexpect(intern!("discover"));
+
+ info!(
+ "discovered! {:u16} {:u16}",
+ client.battery_level_value_handle, client.battery_level_cccd_handle
+ );
+}
+
+#[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: 6,
+ }),
+ conn_gatt: Some(raw::ble_gatt_conn_cfg_t { att_mtu: 128 }),
+ 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 sd = Softdevice::enable(&config);
+
+ unsafe {
+ softdevice_task.spawn(sd).dewrap();
+ ble_central_task.spawn(sd).dewrap();
+
+ static_executor::run();
+ }
+}
diff --git a/examples/src/bin/ble_bas_peripheral.rs b/examples/src/bin/ble_bas_peripheral.rs
new file mode 100644
index 0000000..f148005
--- /dev/null
+++ b/examples/src/bin/ble_bas_peripheral.rs
@@ -0,0 +1,155 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+#[path = "../example_common.rs"]
+mod example_common;
+use example_common::*;
+
+use core::mem;
+use cortex_m_rt::entry;
+use defmt::info;
+
+use nrf_softdevice::ble::{peripheral, Uuid};
+use nrf_softdevice::{raw, RawError, Softdevice};
+
+#[static_executor::task]
+async fn softdevice_task(sd: &'static Softdevice) {
+ sd.run().await;
+}
+
+const GATT_BAS_SVC_UUID: Uuid = Uuid::new_16(0x180F);
+const GATT_BAS_BATTERY_LEVEL_CHAR_UUID: Uuid = Uuid::new_16(0x2A19);
+
+#[static_executor::task]
+async fn bluetooth_task(sd: &'static Softdevice) {
+ // There'll eventually be a safe API for creating GATT servers.
+ // but for now this allows us to test ble_bas_central.
+
+ let mut service_handle: u16 = 0;
+ let ret = unsafe {
+ raw::sd_ble_gatts_service_add(
+ raw::BLE_GATTS_SRVC_TYPE_PRIMARY as u8,
+ GATT_BAS_SVC_UUID.as_raw_ptr(),
+ &mut service_handle as _,
+ )
+ };
+ RawError::convert(ret).dewrap();
+
+ let mut val: u8 = 123;
+
+ let mut cccd_attr_md: raw::ble_gatts_attr_md_t = unsafe { mem::zeroed() };
+ cccd_attr_md.read_perm = raw::ble_gap_conn_sec_mode_t {
+ _bitfield_1: raw::ble_gap_conn_sec_mode_t::new_bitfield_1(1, 1),
+ };
+ cccd_attr_md.write_perm = raw::ble_gap_conn_sec_mode_t {
+ _bitfield_1: raw::ble_gap_conn_sec_mode_t::new_bitfield_1(1, 1),
+ };
+ cccd_attr_md.set_vloc(raw::BLE_GATTS_VLOC_STACK as u8);
+
+ let mut attr_md: raw::ble_gatts_attr_md_t = unsafe { mem::zeroed() };
+ attr_md.read_perm = raw::ble_gap_conn_sec_mode_t {
+ _bitfield_1: raw::ble_gap_conn_sec_mode_t::new_bitfield_1(1, 1),
+ };
+ attr_md.write_perm = raw::ble_gap_conn_sec_mode_t {
+ _bitfield_1: raw::ble_gap_conn_sec_mode_t::new_bitfield_1(1, 1),
+ };
+ attr_md.set_vloc(raw::BLE_GATTS_VLOC_STACK as u8);
+
+ let mut attr: raw::ble_gatts_attr_t = unsafe { mem::zeroed() };
+ attr.p_uuid = unsafe { GATT_BAS_BATTERY_LEVEL_CHAR_UUID.as_raw_ptr() };
+ attr.p_attr_md = &attr_md as _;
+ attr.init_len = 1;
+ attr.max_len = 1;
+ attr.p_value = &mut val;
+
+ let mut char_md: raw::ble_gatts_char_md_t = unsafe { mem::zeroed() };
+ char_md.char_props.set_read(1);
+ char_md.char_props.set_notify(1);
+ char_md.p_cccd_md = &mut cccd_attr_md;
+
+ let mut char_handles: raw::ble_gatts_char_handles_t = unsafe { mem::zeroed() };
+
+ let ret = unsafe {
+ raw::sd_ble_gatts_characteristic_add(
+ service_handle,
+ &mut char_md as _,
+ &mut attr as _,
+ &mut char_handles as _,
+ )
+ };
+ RawError::convert(ret).dewrap();
+
+ #[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 = peripheral::advertise(
+ sd,
+ peripheral::ConnectableAdvertisement::ScannableUndirected {
+ adv_data,
+ scan_data,
+ },
+ )
+ .await
+ .dewrap();
+
+ info!("advertising done!");
+ }
+}
+
+#[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: 6,
+ }),
+ conn_gatt: Some(raw::ble_gatt_conn_cfg_t { att_mtu: 128 }),
+ 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 sd = Softdevice::enable(&config);
+
+ unsafe {
+ softdevice_task.spawn(sd).dewrap();
+ bluetooth_task.spawn(sd).dewrap();
+
+ static_executor::run();
+ }
+}
diff --git a/examples/src/bin/ble_peripheral_gattspam.rs b/examples/src/bin/ble_peripheral_gattspam.rs
new file mode 100644
index 0000000..4aa1596
--- /dev/null
+++ b/examples/src/bin/ble_peripheral_gattspam.rs
@@ -0,0 +1,159 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+#[path = "../example_common.rs"]
+mod example_common;
+use example_common::*;
+
+use core::mem;
+use cortex_m_rt::entry;
+use defmt::info;
+
+use nrf_softdevice::ble::{peripheral, Uuid};
+use nrf_softdevice::{raw, RawError, Softdevice};
+
+#[static_executor::task]
+async fn softdevice_task(sd: &'static Softdevice) {
+ sd.run().await;
+}
+
+#[static_executor::task]
+async fn bluetooth_task(sd: &'static Softdevice) {
+ for i in 0..24 {
+ let service_uuid = Uuid::new_16(0x4200 + i);
+
+ let mut service_handle: u16 = 0;
+ let ret = unsafe {
+ raw::sd_ble_gatts_service_add(
+ raw::BLE_GATTS_SRVC_TYPE_PRIMARY as u8,
+ service_uuid.as_raw_ptr(),
+ &mut service_handle as _,
+ )
+ };
+ RawError::convert(ret).dewrap();
+
+ let max = if i == 0 { 64 } else { 16 };
+ for j in 0..max {
+ let char_uuid = Uuid::new_16(0x6900 + j);
+
+ let mut val: u8 = 123;
+
+ let mut cccd_attr_md: raw::ble_gatts_attr_md_t = unsafe { mem::zeroed() };
+ cccd_attr_md.read_perm = raw::ble_gap_conn_sec_mode_t {
+ _bitfield_1: raw::ble_gap_conn_sec_mode_t::new_bitfield_1(1, 1),
+ };
+ cccd_attr_md.write_perm = raw::ble_gap_conn_sec_mode_t {
+ _bitfield_1: raw::ble_gap_conn_sec_mode_t::new_bitfield_1(1, 1),
+ };
+ cccd_attr_md.set_vloc(raw::BLE_GATTS_VLOC_STACK as u8);
+
+ let mut attr_md: raw::ble_gatts_attr_md_t = unsafe { mem::zeroed() };
+ attr_md.read_perm = raw::ble_gap_conn_sec_mode_t {
+ _bitfield_1: raw::ble_gap_conn_sec_mode_t::new_bitfield_1(1, 1),
+ };
+ attr_md.write_perm = raw::ble_gap_conn_sec_mode_t {
+ _bitfield_1: raw::ble_gap_conn_sec_mode_t::new_bitfield_1(1, 1),
+ };
+ attr_md.set_vloc(raw::BLE_GATTS_VLOC_STACK as u8);
+
+ let mut attr: raw::ble_gatts_attr_t = unsafe { mem::zeroed() };
+ attr.p_uuid = unsafe { char_uuid.as_raw_ptr() };
+ attr.p_attr_md = &attr_md as _;
+ attr.init_len = 1;
+ attr.max_len = 1;
+ attr.p_value = &mut val;
+
+ let mut char_md: raw::ble_gatts_char_md_t = unsafe { mem::zeroed() };
+ char_md.char_props.set_read(1);
+ char_md.char_props.set_write(1);
+ char_md.char_props.set_notify(1);
+ char_md.p_cccd_md = &mut cccd_attr_md;
+
+ let mut char_handles: raw::ble_gatts_char_handles_t = unsafe { mem::zeroed() };
+
+ let ret = unsafe {
+ raw::sd_ble_gatts_characteristic_add(
+ service_handle,
+ &mut char_md as _,
+ &mut attr as _,
+ &mut char_handles as _,
+ )
+ };
+ RawError::convert(ret).dewrap();
+ }
+ }
+
+ #[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 = peripheral::advertise(
+ sd,
+ peripheral::ConnectableAdvertisement::ScannableUndirected {
+ adv_data,
+ scan_data,
+ },
+ )
+ .await
+ .dewrap();
+
+ info!("advertising done!");
+ }
+}
+
+#[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: 6,
+ }),
+ conn_gatt: Some(raw::ble_gatt_conn_cfg_t { att_mtu: 128 }),
+ 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 sd = Softdevice::enable(&config);
+
+ unsafe {
+ softdevice_task.spawn(sd).dewrap();
+ bluetooth_task.spawn(sd).dewrap();
+
+ static_executor::run();
+ }
+}
diff --git a/examples/src/bin/flash.rs b/examples/src/bin/flash.rs
new file mode 100644
index 0000000..e4570c2
--- /dev/null
+++ b/examples/src/bin/flash.rs
@@ -0,0 +1,47 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+#[path = "../example_common.rs"]
+mod example_common;
+use example_common::*;
+
+use async_flash::Flash as _;
+use cortex_m_rt::entry;
+use nrf_softdevice::{Flash, Softdevice};
+
+#[static_executor::task]
+async fn softdevice_task(sd: &'static Softdevice) {
+ sd.run().await;
+}
+
+#[static_executor::task]
+async fn flash_task(sd: &'static Softdevice) {
+ let mut f = Flash::take(sd);
+
+ info!("starting erase");
+ match f.erase(0x80000).await {
+ Ok(()) => info!("erased!"),
+ Err(e) => depanic!("erase failed: {:?}", e),
+ }
+
+ info!("starting write");
+ match f.write(0x80000, &[1, 2, 3, 4]).await {
+ Ok(()) => info!("write done!"),
+ Err(e) => depanic!("write failed: {:?}", e),
+ }
+}
+
+#[entry]
+fn main() -> ! {
+ info!("Hello World!");
+
+ let sd = Softdevice::enable(&Default::default());
+
+ unsafe {
+ softdevice_task.spawn(sd).dewrap();
+ flash_task.spawn(sd).dewrap();
+
+ static_executor::run();
+ }
+}
diff --git a/examples/src/bin/interrupts.rs b/examples/src/bin/interrupts.rs
new file mode 100644
index 0000000..de9feae
--- /dev/null
+++ b/examples/src/bin/interrupts.rs
@@ -0,0 +1,93 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+#[path = "../example_common.rs"]
+mod example_common;
+use example_common::*;
+
+use cortex_m_rt::entry;
+use nrf_softdevice::interrupt;
+use nrf_softdevice::Softdevice;
+
+#[static_executor::task]
+async fn softdevice_task(sd: &'static Softdevice) {
+ sd.run().await;
+}
+
+#[static_executor::task]
+async fn interrupt_task(sd: &'static Softdevice) {
+ let enabled = interrupt::is_enabled(interrupt::SWI0_EGU0);
+ info!("enabled: {:?}", enabled);
+
+ // This would panic with "irq RADIO is reserved for the softdevice"
+ // interrupt::set_priority(interrupt::RADIO, interrupt::Priority::Level7);
+
+ // This would panic with "priority level Level1 is reserved for the softdevice"
+ // interrupt::set_priority(interrupt::SWI0_EGU0, interrupt::Priority::Level1);
+
+ // This would panic with "irq SWI0_EGU0 has priority Level0 which is reserved for the softdevice. Set another prority before enabling it.""
+ // interrupt::enable(interrupt::SWI0_EGU0);
+
+ // If we set a non-reserved priority first, we can enable the interrupt
+ interrupt::set_priority(interrupt::SWI0_EGU0, interrupt::Priority::Level7);
+ interrupt::enable(interrupt::SWI0_EGU0);
+
+ // Now it's enabled
+ let enabled = interrupt::is_enabled(interrupt::SWI0_EGU0);
+ info!("enabled: {:?}", enabled);
+
+ // The interrupt will trigger instantly
+ info!("before pend");
+ interrupt::pend(interrupt::SWI0_EGU0);
+ info!("after pend");
+
+ interrupt::free(|_| {
+ info!("Hello from critical section!");
+
+ // The interrupt will trigger at end of critical section
+ info!("before pend");
+ interrupt::pend(interrupt::SWI0_EGU0);
+ info!("after pend");
+
+ // This will print true even if we're inside a critical section
+ // because it reads a "backup" of the irq enabled registers.
+ let enabled = interrupt::is_enabled(interrupt::SWI0_EGU0);
+ info!("enabled: {:?}", enabled);
+
+ // You can also enable/disable interrupts inside a critical section
+ // They don't take effect until exiting the critical section, so it's safe.
+ // (they modify the "backup" register which gets restored on CS exit)
+ interrupt::set_priority(interrupt::SWI1_EGU1, interrupt::Priority::Level6);
+ interrupt::enable(interrupt::SWI1_EGU1);
+ interrupt::pend(interrupt::SWI1_EGU1);
+
+ info!("exiting critical section...");
+ });
+
+ info!("exited critical section");
+}
+
+#[interrupt]
+fn SWI0_EGU0() {
+ info!("SWI0_EGU0 triggered!")
+}
+
+#[interrupt]
+fn SWI1_EGU1() {
+ info!("SWI1_EGU1 triggered!")
+}
+
+#[entry]
+fn main() -> ! {
+ info!("Hello World!");
+
+ let sd = Softdevice::enable(&Default::default());
+
+ unsafe {
+ softdevice_task.spawn(sd).dewrap();
+ interrupt_task.spawn(sd).dewrap();
+
+ static_executor::run();
+ }
+}
diff --git a/examples/src/bin/rtic.rs b/examples/src/bin/rtic.rs
new file mode 100644
index 0000000..93851a4
--- /dev/null
+++ b/examples/src/bin/rtic.rs
@@ -0,0 +1,135 @@
+#![no_main]
+#![no_std]
+#![feature(type_alias_impl_trait)]
+
+#[path = "../example_common.rs"]
+mod example_common;
+use example_common::*;
+
+use core::mem;
+use nrf52840_hal::pac::TIMER1;
+use nrf52840_hal::prelude::*;
+use nrf52840_hal::timer::{Periodic, Timer};
+use nrf_softdevice::ble::peripheral;
+use nrf_softdevice::{raw, Softdevice};
+use rtic::app;
+
+// This example showcases how to use nrf-softdevice inside RTIC.
+//
+// It mixes RTIC's real-time interrupt-based multitasking with
+// static-executor's cooperative async/await multitasking.
+//
+// static-executor is run in RTIC's idle task, at lowest priority, so all RTIC
+// tasks will preempt async tasks if needed.
+//
+// Note that this is not fully safe: you must not use the softdevice's reserved
+// priorities for RTIC tasks. There is no compile-time checking for that for now.
+
+#[static_executor::task]
+async fn softdevice_task(sd: &'static Softdevice) {
+ sd.run().await;
+}
+
+#[static_executor::task]
+async fn bluetooth_task(sd: &'static Softdevice) {
+ #[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'T', b'I', b'C',
+ ];
+ #[rustfmt::skip]
+ let scan_data = &[
+ 0x03, 0x03, 0x09, 0x18,
+ ];
+
+ loop {
+ let _conn = peripheral::advertise(
+ sd,
+ peripheral::ConnectableAdvertisement::ScannableUndirected {
+ adv_data,
+ scan_data,
+ },
+ )
+ .await
+ .dewrap();
+
+ info!("advertising done!");
+
+ // conn will now get disconnected because it's dropped
+ }
+}
+
+#[app(device = nrf52840_hal::pac, peripherals = true)]
+const APP: () = {
+ struct Resources {
+ timer: Timer<TIMER1, Periodic>,
+ }
+
+ #[init()]
+ fn init(cx: init::Context) -> init::LateResources {
+ info!("init");
+
+ let mut timer = Timer::new(cx.device.TIMER1);
+ timer.enable_interrupt();
+ let mut timer = timer.into_periodic();
+ timer.start(1_000_000u32); // 1Mhz, so once per second
+
+ init::LateResources { timer }
+ }
+
+ #[idle]
+ fn idle(_: idle::Context) -> ! {
+ 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: 6,
+ }),
+ conn_gatt: Some(raw::ble_gatt_conn_cfg_t { att_mtu: 128 }),
+ 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"HelloRTIC" 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()
+ };
+
+ // Softdevice enable must not be done in RTIC init
+ // because RTIC runs init with interrupts disabled, and the
+ // softdevice crashes if it's enabled with interrupts disabled.
+ let sd = Softdevice::enable(&config);
+
+ unsafe {
+ softdevice_task.spawn(sd).dewrap();
+ bluetooth_task.spawn(sd).dewrap();
+
+ static_executor::run();
+ }
+ }
+
+ #[task(binds = TIMER1, resources = [timer], priority = 1)]
+ fn exec(cx: exec::Context) {
+ cx.resources.timer.wait().unwrap();
+ info!("tick");
+ }
+};
diff --git a/examples/src/example_common.rs b/examples/src/example_common.rs
new file mode 100644
index 0000000..e991915
--- /dev/null
+++ b/examples/src/example_common.rs
@@ -0,0 +1,68 @@
+#![macro_use]
+
+use defmt_rtt as _; // global logger
+use nrf52840_hal as _;
+use panic_probe as _;
+use static_executor_cortex_m as _;
+
+pub use defmt::{info, intern};
+
+use core::sync::atomic::{AtomicUsize, Ordering};
+
+#[defmt::timestamp]
+fn timestamp() -> u64 {
+ static COUNT: AtomicUsize = AtomicUsize::new(0);
+ // NOTE(no-CAS) `timestamps` runs with interrupts disabled
+ let n = COUNT.load(Ordering::Relaxed);
+ COUNT.store(n + 1, Ordering::Relaxed);
+ n as u64
+}
+
+macro_rules! depanic {
+ ($( $i:expr ),*) => {
+ {
+ defmt::error!($( $i ),*);
+ panic!();
+ }
+ }
+}
+
+pub trait Dewrap<T> {
+ /// dewrap = defmt unwrap
+ fn dewrap(self) -> T;
+
+ /// dexpect = defmt expect
+ fn dexpect<M: defmt::Format>(self, msg: M) -> T;
+}
+
+impl<T> Dewrap<T> for Option<T> {
+ fn dewrap(self) -> T {
+ match self {
+ Some(t) => t,
+ None => depanic!("Dewrap failed: enum is none"),
+ }
+ }
+
+ fn dexpect<M: defmt::Format>(self, msg: M) -> T {
+ match self {
+ Some(t) => t,
+ None => depanic!("Unexpected None: {:?}", msg),
+ }
+ }
+}
+
+impl<T, E: defmt::Format> Dewrap<T> for Result<T, E> {
+ fn dewrap(self) -> T {
+ match self {
+ Ok(t) => t,
+ Err(e) => depanic!("Dewrap failed: {:?}", e),
+ }
+ }
+
+ fn dexpect<M: defmt::Format>(self, msg: M) -> T {
+ match self {
+ Ok(t) => t,
+ Err(e) => depanic!("Unexpected error: {:?}: {:?}", msg, e),
+ }
+ }
+}