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}