-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Nordic Uart Service (NUS) peripheral to examples
- Loading branch information
Showing
5 changed files
with
245 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
/* Nordic Uart Service (NUS) peripheral example */ | ||
use embassy_futures::join::join3; | ||
use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
use embassy_time::{Duration, Timer}; | ||
use heapless::Vec; | ||
use trouble_host::prelude::*; | ||
|
||
/// Size of L2CAP packets (ATT MTU is this - 4) | ||
const L2CAP_MTU: usize = 251; | ||
|
||
/// Max number of connections | ||
const CONNECTIONS_MAX: usize = 1; | ||
|
||
/// Max number of L2CAP channels. | ||
const L2CAP_CHANNELS_MAX: usize = 2; // Signal + att | ||
|
||
const MAX_ATTRIBUTES: usize = 32; | ||
|
||
pub const MTU: usize = 120; | ||
// Aligned to 4 bytes + 3 bytes for header | ||
pub const ATT_MTU: usize = MTU + 3; | ||
|
||
type Resources<C> = HostResources<C, CONNECTIONS_MAX, L2CAP_CHANNELS_MAX, L2CAP_MTU>; | ||
|
||
// GATT Server | ||
#[gatt_server] | ||
struct Server { | ||
nrf_uart: NrfUartService, | ||
} | ||
|
||
// NRF UART Service | ||
#[gatt_service(uuid = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E")] | ||
struct NrfUartService { | ||
#[characteristic(uuid = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E", write)] | ||
rx: Vec<u8, ATT_MTU>, | ||
|
||
#[characteristic(uuid = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E", notify)] | ||
tx: Vec<u8, ATT_MTU>, | ||
} | ||
|
||
pub async fn run<C>(controller: C) | ||
where | ||
C: Controller, | ||
{ | ||
let address = Address::random([0x41, 0x5A, 0xE3, 0x1E, 0x83, 0xE7]); | ||
info!("Our address = {:?}", address); | ||
|
||
let mut resources = Resources::new(PacketQos::None); | ||
let (stack, peripheral, _, runner) = trouble_host::new(controller, &mut resources) | ||
.set_random_address(address) | ||
.build(); | ||
|
||
let mut table: AttributeTable<'_, NoopRawMutex, MAX_ATTRIBUTES> = AttributeTable::new(); | ||
|
||
// Generic Access Service (mandatory) | ||
let id = b"Trouble Example Device"; | ||
let mut svc = table.add_service(Service::new(0x1800)); | ||
let _ = svc.add_characteristic_ro(0x2a00, id); | ||
svc.build(); | ||
|
||
// Generic attribute service (mandatory) | ||
table.add_service(Service::new(0x1801)); | ||
|
||
let server = Server::new(stack, &mut table); | ||
|
||
info!("Starting advertising and GATT service"); | ||
let _ = join3( | ||
ble_task(runner), | ||
gatt_task(&server), | ||
advertise_task(peripheral, &server), | ||
) | ||
.await; | ||
} | ||
|
||
async fn ble_task<C: Controller>(mut runner: Runner<'_, C>) -> Result<(), BleHostError<C::Error>> { | ||
runner.run().await | ||
} | ||
|
||
async fn gatt_task<C: Controller>(server: &Server<'_, '_, C>) { | ||
loop { | ||
match server.next().await { | ||
Ok(GattEvent::Write { handle, connection: _ }) => { | ||
let _ = server.get(handle, |value| { | ||
info!("[gatt] Write event on {:?}. Value written: {:?}", handle, value); | ||
}); | ||
} | ||
Ok(GattEvent::Read { handle, connection: _ }) => { | ||
info!("[gatt] Read event on {:?}", handle); | ||
} | ||
Err(e) => { | ||
error!("[gatt] Error processing GATT events: {:?}", e); | ||
} | ||
} | ||
} | ||
} | ||
|
||
async fn advertise_task<C: Controller>( | ||
mut peripheral: Peripheral<'_, C>, | ||
server: &Server<'_, '_, C>, | ||
) -> Result<(), BleHostError<C::Error>> { | ||
let mut adv_data = [0; 31]; | ||
AdStructure::encode_slice( | ||
&[ | ||
AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), | ||
AdStructure::CompleteLocalName(b"Trouble NRF UART"), | ||
], | ||
&mut adv_data[..], | ||
)?; | ||
loop { | ||
info!("[adv] advertising"); | ||
let mut advertiser = peripheral | ||
.advertise( | ||
&Default::default(), | ||
Advertisement::ConnectableScannableUndirected { | ||
adv_data: &adv_data[..], | ||
scan_data: &[], | ||
}, | ||
) | ||
.await?; | ||
let conn = advertiser.accept().await?; | ||
|
||
/* TODO: Implement "echo" and push rx bytes back to tx? */ | ||
let mut tx = [0; ATT_MTU]; | ||
let mut tick: u8 = 0; | ||
while conn.is_connected() { | ||
Timer::after(Duration::from_secs(2)).await; | ||
tick = tick.wrapping_add(1); | ||
tx[0] = tick; | ||
info!("[adv] notifying connection of tick {}", tick); | ||
let _ = server.notify(server.nrf_uart.tx, &conn, &tx[..]).await; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
#![no_std] | ||
#![no_main] | ||
|
||
use defmt::unwrap; | ||
use embassy_executor::Spawner; | ||
use embassy_nrf::peripherals::RNG; | ||
use embassy_nrf::{bind_interrupts, pac, rng}; | ||
use nrf_sdc::mpsl::MultiprotocolServiceLayer; | ||
use nrf_sdc::{self as sdc, mpsl}; | ||
use static_cell::StaticCell; | ||
use trouble_example_apps::ble_nus_peripheral as nrf_uart; | ||
use {defmt_rtt as _, panic_probe as _}; | ||
|
||
bind_interrupts!(struct Irqs { | ||
RNG => rng::InterruptHandler<RNG>; | ||
SWI0_EGU0 => nrf_sdc::mpsl::LowPrioInterruptHandler; | ||
POWER_CLOCK => nrf_sdc::mpsl::ClockInterruptHandler; | ||
RADIO => nrf_sdc::mpsl::HighPrioInterruptHandler; | ||
TIMER0 => nrf_sdc::mpsl::HighPrioInterruptHandler; | ||
RTC0 => nrf_sdc::mpsl::HighPrioInterruptHandler; | ||
}); | ||
|
||
#[embassy_executor::task] | ||
async fn mpsl_task(mpsl: &'static MultiprotocolServiceLayer<'static>) -> ! { | ||
mpsl.run().await | ||
} | ||
|
||
fn build_sdc<'d, const N: usize>( | ||
p: nrf_sdc::Peripherals<'d>, | ||
rng: &'d mut rng::Rng<RNG>, | ||
mpsl: &'d MultiprotocolServiceLayer, | ||
mem: &'d mut sdc::Mem<N>, | ||
) -> Result<nrf_sdc::SoftdeviceController<'d>, nrf_sdc::Error> { | ||
sdc::Builder::new()? | ||
.support_adv()? | ||
.support_peripheral()? | ||
.peripheral_count(1)? | ||
.build(p, rng, mpsl, mem) | ||
} | ||
|
||
#[embassy_executor::main] | ||
async fn main(spawner: Spawner) { | ||
let p = embassy_nrf::init(Default::default()); | ||
let pac_p = pac::Peripherals::take().unwrap(); | ||
|
||
let mpsl_p = mpsl::Peripherals::new( | ||
pac_p.CLOCK, | ||
pac_p.RADIO, | ||
p.RTC0, | ||
p.TIMER0, | ||
p.TEMP, | ||
p.PPI_CH19, | ||
p.PPI_CH30, | ||
p.PPI_CH31, | ||
); | ||
let lfclk_cfg = mpsl::raw::mpsl_clock_lfclk_cfg_t { | ||
source: mpsl::raw::MPSL_CLOCK_LF_SRC_RC as u8, | ||
rc_ctiv: mpsl::raw::MPSL_RECOMMENDED_RC_CTIV as u8, | ||
rc_temp_ctiv: mpsl::raw::MPSL_RECOMMENDED_RC_TEMP_CTIV as u8, | ||
accuracy_ppm: mpsl::raw::MPSL_DEFAULT_CLOCK_ACCURACY_PPM as u16, | ||
skip_wait_lfclk_started: mpsl::raw::MPSL_DEFAULT_SKIP_WAIT_LFCLK_STARTED != 0, | ||
}; | ||
static MPSL: StaticCell<MultiprotocolServiceLayer> = StaticCell::new(); | ||
let mpsl = MPSL.init(unwrap!(mpsl::MultiprotocolServiceLayer::new(mpsl_p, Irqs, lfclk_cfg))); | ||
spawner.must_spawn(mpsl_task(&*mpsl)); | ||
|
||
let sdc_p = sdc::Peripherals::new( | ||
pac_p.ECB, pac_p.AAR, p.PPI_CH17, p.PPI_CH18, p.PPI_CH20, p.PPI_CH21, p.PPI_CH22, p.PPI_CH23, p.PPI_CH24, | ||
p.PPI_CH25, p.PPI_CH26, p.PPI_CH27, p.PPI_CH28, p.PPI_CH29, | ||
); | ||
|
||
let mut rng = rng::Rng::new(p.RNG, Irqs); | ||
|
||
let mut sdc_mem = sdc::Mem::<5312>::new(); | ||
let sdc = unwrap!(build_sdc(sdc_p, &mut rng, mpsl, &mut sdc_mem)); | ||
|
||
nrf_uart::run(sdc).await; | ||
} |