Skip to main content

heart_rate/
core.rs

1// main.rs
2use btleplug::api::{Central, Manager as _, Peripheral as _};
3use btleplug::platform::{Adapter, Manager};
4use futures::stream::StreamExt;
5use std::sync::Arc;
6use std::time::Duration;
7use tokio::sync::mpsc;
8use tokio::time;
9use uuid::Uuid;
10
11// Constants
12const UUID_STR: &str = "00002a37-0000-1000-8000-00805f9b34fb";
13const SUPPORT_DEVICES: &[&str] = &["Xiaomi Smart Band 9 082F"];
14
15// Custom Error Type
16#[derive(thiserror::Error, Debug)]
17pub enum Error {
18    #[error("Bluetooth adapters not found")]
19    BluetoothAdaptersNotFound,
20    #[error("Device not found")]
21    DeviceNotFound,
22    #[error("Heart rate characteristic not found")]
23    CharacteristicNotFound,
24    #[error("HeartRateMonitor not found")]
25    HeartRateMonitorNotFound,
26    #[error("Bluetooth error: {0}")]
27    Bluetooth(#[from] btleplug::Error),
28}
29
30// Heart Rate Monitor Struct
31pub struct HeartRateMonitor {
32    adapter: Adapter,
33    device_address: String,
34}
35
36impl HeartRateMonitor {
37    pub async fn new(adapter: Adapter, device_address: String) -> Self {
38        Self {
39            adapter,
40            device_address,
41        }
42    }
43
44    pub async fn start_monitoring(&self) -> mpsc::Receiver<u8> {
45        let (sender, receiver) = mpsc::channel(100);
46        let adapter = self.adapter.clone();
47        let device_address = self.device_address.clone();
48        let adapter_arc = Arc::new(adapter);
49        tokio::spawn({
50            let adapter_arc = Arc::clone(&adapter_arc);
51            async move {
52                loop {
53                    match Self::connect_and_monitor(&adapter_arc, &device_address, &sender).await {
54                        Ok(_) => break,
55                        Err(e) => {
56                            eprintln!("Monitoring error: {e}");
57                            time::sleep(Duration::from_secs(5)).await;
58                        }
59                    }
60                }
61            }
62        });
63
64        receiver
65    }
66
67    async fn connect_and_monitor(
68        adapter: &Adapter,
69        device_address: &str,
70        sender: &mpsc::Sender<u8>,
71    ) -> Result<(), Error> {
72        let heart_rate_uuid: Uuid = Uuid::parse_str(UUID_STR).expect("UUID_STR");
73        adapter.start_scan(Default::default()).await?;
74        time::sleep(Duration::from_secs(5)).await;
75
76        let peripherals = adapter.peripherals().await?;
77        let device = peripherals
78            .into_iter()
79            .find(|p| p.address().to_string() == device_address)
80            .ok_or(Error::DeviceNotFound)?;
81
82        device.connect().await?;
83        device.discover_services().await?;
84
85        let characteristics = device.characteristics();
86        let hr_char = characteristics
87            .into_iter()
88            .find(|c| c.uuid == heart_rate_uuid)
89            .ok_or(Error::CharacteristicNotFound)?;
90
91        device.subscribe(&hr_char).await?;
92        let mut notification_stream = device.notifications().await?;
93
94        while let Some(data) = notification_stream.next().await {
95            if data.uuid == heart_rate_uuid {
96                let value = parse_heart_rate(&data.value);
97                if sender.send(value).await.is_err() {
98                    break; // Receiver closed
99                }
100            }
101        }
102
103        device.disconnect().await?;
104        Ok(())
105    }
106}
107
108fn parse_heart_rate(data: &[u8]) -> u8 {
109    if data.len() >= 2 {
110        if data[0] & 0x01 == 0 {
111            data[1]
112        } else {
113            u16::from_le_bytes([data[1], data[2]]) as u8
114        }
115    } else {
116        0
117    }
118}
119
120async fn get_device_address(device_name: &str) -> Result<String, Error> {
121    let manager = Manager::new().await?;
122    let adapters = manager.adapters().await?;
123    let adapter = adapters
124        .into_iter()
125        .next()
126        .ok_or(Error::BluetoothAdaptersNotFound)?;
127
128    adapter.start_scan(Default::default()).await?;
129    time::sleep(Duration::from_secs(5)).await;
130
131    let peripherals = adapter.peripherals().await?;
132    for peripheral in peripherals {
133        let properties = peripheral.properties().await?;
134        if let Some(props) = properties {
135            if let Some(name) = props.local_name {
136                if name == device_name {
137                    let address = props.address.to_string();
138                    adapter.stop_scan().await?;
139                    return Ok(address);
140                }
141            }
142        }
143    }
144
145    Err(Error::DeviceNotFound)
146}
147
148pub async fn detect_monitor() -> Result<HeartRateMonitor, Error> {
149    for device_name in SUPPORT_DEVICES {
150        match get_device_address(device_name).await {
151            Ok(device_address) => {
152                let manager = Manager::new().await?;
153                let adapters = manager.adapters().await?;
154                let adapter = adapters
155                    .into_iter()
156                    .next()
157                    .ok_or(Error::BluetoothAdaptersNotFound)?;
158                let monitor = HeartRateMonitor::new(adapter, device_address).await;
159                return Ok(monitor);
160            }
161            Err(e) => eprintln!("Device not found: {device_name} - Error: {e}"),
162        }
163    }
164    Err(Error::HeartRateMonitorNotFound)
165}