psylink/
bluetooth.rs

1use crate::prelude::*;
2use btleplug::api::{
3    Central, Characteristic, Manager as _, Peripheral, PeripheralProperties, ScanFilter,
4};
5use btleplug::platform::Manager;
6use std::error::Error;
7use std::io;
8use std::sync::{Arc, Mutex};
9use std::time::Duration;
10use tokio::time;
11use uuid::Uuid;
12
13//pub struct State {
14//    manager: Manager,
15//}
16
17#[derive(Clone)]
18pub struct Device {
19    pub name: String,
20    pub address: String,
21    peripheral: btleplug::platform::Peripheral,
22    characteristics: Option<Characteristics>,
23}
24
25#[derive(Clone)]
26pub struct Characteristics {
27    _channel_count: Characteristic,
28    sensor: Characteristic,
29}
30
31impl Device {
32    pub async fn find_characteristics(&mut self) {
33        let uuid_sensor = Uuid::parse_str(firmware::SENSOR_CHARACTERISTICS_UUID).unwrap();
34        let uuid_channel_count =
35            Uuid::parse_str(firmware::CHANNEL_COUNT_CHARACTERISTICS_UUID).unwrap();
36
37        let _ = self.peripheral.connect().await;
38        let _ = self.peripheral.discover_services().await;
39        let characteristics = self.peripheral.characteristics();
40        let chr_sensor = characteristics
41            .iter()
42            .find(|c| c.uuid == uuid_sensor)
43            .unwrap();
44        let chr_channel_count = characteristics
45            .iter()
46            .find(|c| c.uuid == uuid_channel_count)
47            .unwrap();
48
49        self.characteristics = Some(Characteristics {
50            _channel_count: chr_channel_count.clone(),
51            sensor: chr_sensor.clone(),
52        });
53    }
54
55    pub async fn read(&self) -> Result<Vec<u8>, Box<dyn Error>> {
56        if let Some(chr) = &self.characteristics {
57            Ok(self.peripheral.read(&chr.sensor).await?)
58        } else {
59            Err(Box::new(std::io::Error::new(
60                std::io::ErrorKind::Other,
61                "Must load characteristics before calling read()",
62            )))
63        }
64    }
65
66    pub async fn disconnect(&mut self) -> Result<(), Box<dyn Error>> {
67        self.peripheral.disconnect().await?;
68        Ok(())
69    }
70}
71
72pub async fn scan(app: App) -> Result<(), Box<dyn Error>> {
73    println!("Scanning Bluetooth for PsyLink device...");
74
75    let manager = Manager::new().await?;
76    let adapter_list = manager.adapters().await?;
77    if adapter_list.is_empty() {
78        eprintln!("No Bluetooth adapters found");
79    }
80
81    for adapter in adapter_list.iter() {
82        if app.verbose > 0 {
83            println!(
84                "Trying bluetooth adapter {}...",
85                adapter.adapter_info().await?
86            );
87        }
88        adapter
89            .start_scan(ScanFilter::default())
90            .await
91            .expect("Can't scan BLE adapter for connected devices...");
92        time::sleep(Duration::from_secs_f32(app.scantime)).await;
93
94        let peripherals = adapter.peripherals().await?;
95        if peripherals.is_empty() {
96            eprintln!("No BLE peripheral devices found.");
97        } else {
98            for peripheral in peripherals.iter() {
99                let properties = peripheral.properties().await?;
100                if app.verbose > 1 {
101                    dbg!(&properties);
102                }
103                if let Some(PeripheralProperties {
104                    address,
105                    local_name: Some(name),
106                    ..
107                }) = &properties
108                {
109                    if name == "PsyLink" {
110                        println!("Found PsyLink device with address {address}");
111                    }
112                }
113            }
114        }
115    }
116    Ok(())
117}
118
119pub async fn stream(app: App) -> Result<(), Box<dyn Error>> {
120    println!("Scanning Bluetooth for PsyLink device...");
121    let manager = Manager::new().await?;
122    let adapter_list = manager.adapters().await?;
123    if adapter_list.is_empty() {
124        eprintln!("No Bluetooth adapters found");
125    }
126    let sensor_uuid = Uuid::parse_str(firmware::SENSOR_CHARACTERISTICS_UUID).unwrap();
127
128    let psylink = find_peripheral(app, None).await?;
129
130    let _ = psylink.peripheral.connect().await;
131    let _ = psylink.peripheral.discover_services().await;
132    let characteristics = psylink.peripheral.characteristics();
133
134    let sensor_characteristic = characteristics
135        .iter()
136        .find(|c| c.uuid == sensor_uuid)
137        .unwrap();
138    loop {
139        let data = psylink.peripheral.read(sensor_characteristic).await?;
140        dbg!(data);
141    }
142}
143
144pub async fn find_peripheral(
145    app: App,
146    mutex_quit: Option<Arc<Mutex<bool>>>,
147) -> Result<Device, Box<dyn Error>> {
148    println!("Scanning Bluetooth for PsyLink device...");
149
150    let manager = Manager::new().await?;
151    let adapter_list = manager.adapters().await?;
152    if adapter_list.is_empty() {
153        eprintln!("No Bluetooth adapters found");
154    }
155
156    loop {
157        for adapter in adapter_list.iter() {
158            if app.verbose > 0 {
159                println!(
160                    "Trying Bluetooth adapter {}...",
161                    adapter.adapter_info().await?
162                );
163            }
164            let _ = adapter.start_scan(ScanFilter::default()).await;
165            //.expect("Can't scan BLE adapter for connected devices...");
166            time::sleep(Duration::from_secs_f32(0.1)).await;
167
168            let peripherals = adapter.peripherals().await?;
169            if peripherals.is_empty() {
170                eprintln!("No BLE peripheral devices found.");
171            } else {
172                for peripheral in peripherals.iter() {
173                    let properties = peripheral.properties().await?;
174                    if app.verbose > 1 {
175                        dbg!(&properties);
176                    }
177                    if let Some(PeripheralProperties {
178                        address,
179                        local_name: Some(name),
180                        ..
181                    }) = &properties
182                    {
183                        if name == "PsyLink" {
184                            println!("Found PsyLink device with address {address}");
185                            return Ok(Device {
186                                name: name.to_string(),
187                                address: address.to_string(),
188                                peripheral: peripheral.clone(),
189                                characteristics: None,
190                            });
191                        }
192                    }
193                }
194            }
195        }
196        if let Some(mutex_quit) = &mutex_quit {
197            if *(mutex_quit.lock().unwrap()) {
198                println!("Quitting bluetooth::Device::find_peripheral");
199                return Err(Box::new(io::Error::new(io::ErrorKind::Other, "The bluetooth::Device::find_peripheral function received a quit command while scanning for peripherals")));
200            }
201        }
202    }
203}