blinky/
blinky.rs

1use std::error::Error;
2use std::time::Duration;
3
4use bluest::{Adapter, Uuid};
5use futures_lite::{future, StreamExt};
6use tracing::metadata::LevelFilter;
7use tracing::{error, info};
8
9const NORDIC_LED_AND_BUTTON_SERVICE: Uuid = Uuid::from_u128(0x00001523_1212_efde_1523_785feabcd123);
10const BLINKY_BUTTON_STATE_CHARACTERISTIC: Uuid = Uuid::from_u128(0x00001524_1212_efde_1523_785feabcd123);
11const BLINKY_LED_STATE_CHARACTERISTIC: Uuid = Uuid::from_u128(0x00001525_1212_efde_1523_785feabcd123);
12
13#[tokio::main]
14async fn main() -> Result<(), Box<dyn Error>> {
15    use tracing_subscriber::prelude::*;
16    use tracing_subscriber::{fmt, EnvFilter};
17
18    tracing_subscriber::registry()
19        .with(fmt::layer())
20        .with(
21            EnvFilter::builder()
22                .with_default_directive(LevelFilter::INFO.into())
23                .from_env_lossy(),
24        )
25        .init();
26
27    let adapter = Adapter::default().await.ok_or("Bluetooth adapter not found")?;
28    adapter.wait_available().await?;
29
30    info!("looking for device");
31    let device = adapter
32        .discover_devices(&[NORDIC_LED_AND_BUTTON_SERVICE])
33        .await?
34        .next()
35        .await
36        .ok_or("Failed to discover device")??;
37    info!(
38        "found device: {} ({:?})",
39        device.name().as_deref().unwrap_or("(unknown)"),
40        device.id()
41    );
42
43    adapter.connect_device(&device).await?;
44    info!("connected!");
45
46    let service = match device
47        .discover_services_with_uuid(NORDIC_LED_AND_BUTTON_SERVICE)
48        .await?
49        .first()
50    {
51        Some(service) => service.clone(),
52        None => return Err("service not found".into()),
53    };
54    info!("found LED and button service");
55
56    let characteristics = service.characteristics().await?;
57    info!("discovered characteristics");
58
59    let button_characteristic = characteristics
60        .iter()
61        .find(|x| x.uuid() == BLINKY_BUTTON_STATE_CHARACTERISTIC)
62        .ok_or("button characteristic not found")?;
63
64    let button_fut = async {
65        info!("enabling button notifications");
66        let mut updates = button_characteristic.notify().await?;
67        info!("waiting for button changes");
68        while let Some(val) = updates.next().await {
69            info!("Button state changed: {:?}", val?);
70        }
71        Ok(())
72    };
73
74    let led_characteristic = characteristics
75        .iter()
76        .find(|x| x.uuid() == BLINKY_LED_STATE_CHARACTERISTIC)
77        .ok_or("led characteristic not found")?;
78
79    let blink_fut = async {
80        info!("blinking LED");
81        tokio::time::sleep(Duration::from_secs(1)).await;
82        loop {
83            led_characteristic.write(&[0x01]).await?;
84            info!("LED on");
85            tokio::time::sleep(Duration::from_secs(1)).await;
86            led_characteristic.write(&[0x00]).await?;
87            info!("LED off");
88            tokio::time::sleep(Duration::from_secs(1)).await;
89        }
90    };
91
92    type R = Result<(), Box<dyn Error>>;
93    let button_fut = async move {
94        let res: R = button_fut.await;
95        error!("Button task exited: {:?}", res);
96    };
97    let blink_fut = async move {
98        let res: R = blink_fut.await;
99        error!("Blink task exited: {:?}", res);
100    };
101
102    future::zip(blink_fut, button_fut).await;
103
104    Ok(())
105}