summaryrefslogtreecommitdiff
path: root/examples/src/bin/ble_peripheral_onoff.rs
blob: 99dcac4394310cecaf54368b46c30c11df799875 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]

#[path = "../example_common.rs"]
mod example_common;

use core::mem;

use cortex_m_rt::entry;
use defmt::*;
use embassy_executor::executor::Executor;
use embassy_nrf::gpio::{AnyPin, Input, Pin as _, Pull};
use embassy_nrf::interrupt::Priority;
use embassy_util::Forever;
use futures::pin_mut;
use nrf_softdevice::ble::{gatt_server, peripheral};
use nrf_softdevice::{raw, Softdevice};

static EXECUTOR: Forever<Executor> = Forever::new();

#[embassy_executor::task]
async fn softdevice_task(sd: &'static Softdevice) {
    sd.run().await;
}

#[nrf_softdevice::gatt_service(uuid = "9e7312e0-2354-11eb-9f10-fbc30a62cf38")]
struct FooService {
    #[characteristic(uuid = "9e7312e0-2354-11eb-9f10-fbc30a63cf38", read, write, notify)]
    foo: u16,
}

#[nrf_softdevice::gatt_server]
struct Server {
    foo: FooService,
}

async fn run_bluetooth(sd: &'static Softdevice, server: &Server) {
    #[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 config = peripheral::Config::default();
        let adv = peripheral::ConnectableAdvertisement::ScannableUndirected { adv_data, scan_data };
        let conn = unwrap!(peripheral::advertise_connectable(sd, adv, &config).await);

        info!("advertising done!");

        let res = gatt_server::run(&conn, server, |e| match e {
            ServerEvent::Foo(FooServiceEvent::FooWrite(val)) => {
                info!("wrote foo level: {}", val);
                if let Err(e) = server.foo.foo_notify(&conn, val + 1) {
                    info!("send notification error: {:?}", e);
                }
            }
            ServerEvent::Foo(FooServiceEvent::FooCccdWrite { notifications }) => {
                info!("foo notifications: {}", notifications)
            }
        })
        .await;

        if let Err(e) = res {
            info!("gatt_server run exited with error: {:?}", e);
        }
    }
}

#[embassy_executor::task]
async fn bluetooth_task(sd: &'static Softdevice, server: Server, button1: AnyPin, button2: AnyPin) {
    info!("Bluetooth is OFF");
    info!("Press nrf52840-dk button 1 to enable, button 2 to disable");

    let button1 = Input::new(button1, Pull::Up);
    let button2 = Input::new(button2, Pull::Up);
    pin_mut!(button1);
    pin_mut!(button2);
    loop {
        button1.as_mut().wait_for_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 {
            button2.as_mut().wait_for_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_connectable` 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 mut config = embassy_nrf::config::Config::default();
    config.gpiote_interrupt_priority = Priority::P2;
    config.time_interrupt_priority = Priority::P2;
    let p = embassy_nrf::init(config);

    let config = nrf_softdevice::Config {
        clock: Some(raw::nrf_clock_lf_cfg_t {
            source: raw::NRF_CLOCK_LF_SRC_RC as u8,
            rc_ctiv: 4,
            rc_temp_ctiv: 2,
            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 sd = Softdevice::enable(&config);

    let executor = EXECUTOR.put(Executor::new());
    executor.run(move |spawner| {
        let server = unwrap!(Server::new(sd));
        unwrap!(spawner.spawn(softdevice_task(sd)));
        unwrap!(spawner.spawn(bluetooth_task(sd, server, p.P0_11.degrade(), p.P0_12.degrade())));
    });
}