1use std::pin::Pin;
2use std::time::{Duration, Instant};
3
4use btleplug::api::{
5 Central, CentralEvent, Characteristic, Manager as _, Peripheral as _, ScanFilter,
6 ValueNotification, WriteType,
7};
8use btleplug::platform::{Manager, Peripheral};
9use futures::Stream;
10use log::{debug, error, trace, warn};
11use thiserror::Error;
12use tokio::select;
13use tokio::time::sleep;
14use tokio_stream::StreamExt;
15use uuid::Uuid;
16
17use crate::trim_packets;
18
19use vex_cdc::{Decode, DecodeError, Encode, FixedStringSizeError, cdc2::Cdc2Ack};
20
21use super::{CheckHeader, Connection, ConnectionType, RawPacket};
22
23pub const V5_SERVICE: Uuid = Uuid::from_u128(0x08590f7e_db05_467e_8757_72f6faeb13d5);
25
26pub const CHARACTERISTIC_SYSTEM_TX: Uuid = Uuid::from_u128(0x08590f7e_db05_467e_8757_72f6faeb1306); pub const CHARACTERISTIC_SYSTEM_RX: Uuid = Uuid::from_u128(0x08590f7e_db05_467e_8757_72f6faeb13f5); pub const CHARACTERISTIC_USER_TX: Uuid = Uuid::from_u128(0x08590f7e_db05_467e_8757_72f6faeb1316); pub const CHARACTERISTIC_USER_RX: Uuid = Uuid::from_u128(0x08590f7e_db05_467e_8757_72f6faeb1326); pub const CHARACTERISTIC_PAIRING: Uuid = Uuid::from_u128(0x08590f7e_db05_467e_8757_72f6faeb13e5); pub const UNPAIRED_MAGIC: u32 = 0xdeadface;
38
39#[derive(Debug, Clone)]
40pub struct BluetoothDevice(pub Peripheral);
41
42impl BluetoothDevice {
43 pub async fn connect(&self) -> Result<BluetoothConnection, BluetoothError> {
44 BluetoothConnection::open(self.clone()).await
45 }
46}
47
48pub async fn find_devices(
50 scan_time: Duration,
51 max_device_count: Option<usize>,
52) -> Result<Vec<BluetoothDevice>, BluetoothError> {
53 let manager = Manager::new().await?;
55
56 let adapter = if let Some(adapter) = manager.adapters().await?.into_iter().next() {
58 adapter
59 } else {
60 return Err(BluetoothError::NoBluetoothAdapter);
62 };
63
64 let mut events = adapter.events().await?;
68
69 let mut devices = Vec::<BluetoothDevice>::new();
71
72 let scan_start_time = Instant::now();
74 adapter
75 .start_scan(ScanFilter {
76 services: vec![V5_SERVICE],
77 })
78 .await?;
79
80 while let Some(event) = events.next().await {
83 match event {
84 CentralEvent::DeviceDiscovered(id) | CentralEvent::DeviceUpdated(id) => {
85 let peripheral = adapter.peripheral(&id).await?;
86
87 if let Some(properties) = peripheral.properties().await? {
88 if properties.services.contains(&V5_SERVICE) {
89 debug!("Found V5 brain at {}", peripheral.address());
91
92 devices.push(BluetoothDevice(peripheral));
93
94 if let Some(count) = max_device_count {
96 if devices.len() == count {
97 break;
98 }
99 }
100 }
101 }
102 }
103 _ => {}
104 }
105
106 if scan_start_time.elapsed() > scan_time {
108 break;
109 }
110 }
111
112 debug!(
113 "Found {} devices in {:?}",
114 devices.len(),
115 scan_start_time.elapsed()
116 );
117
118 Ok(devices)
119}
120
121pub struct BluetoothConnection {
122 pub peripheral: Peripheral,
123 pub system_tx: Characteristic,
124 pub system_rx: Characteristic,
125 pub user_tx: Characteristic,
126 pub user_rx: Characteristic,
127 pub pairing: Characteristic,
128
129 notification_stream: Pin<Box<dyn Stream<Item = ValueNotification> + Send>>,
130 incoming_packets: Vec<RawPacket>,
131}
132
133impl BluetoothConnection {
134 pub const MAX_PACKET_SIZE: usize = 244;
135
136 pub async fn open(device: BluetoothDevice) -> Result<Self, BluetoothError> {
137 let peripheral = device.0;
138
139 if !peripheral.is_connected().await? {
140 peripheral.connect().await?;
141 } else {
142 warn!("Peripheral already connected?");
143 }
144
145 peripheral.discover_services().await?;
146
147 let mut system_tx: Option<Characteristic> = None;
148 let mut system_rx: Option<Characteristic> = None;
149 let mut user_tx: Option<Characteristic> = None;
150 let mut user_rx: Option<Characteristic> = None;
151 let mut pairing: Option<Characteristic> = None;
152
153 for characteric in peripheral.characteristics() {
154 match characteric.uuid {
155 CHARACTERISTIC_SYSTEM_TX => {
156 system_tx = Some(characteric);
157 }
158 CHARACTERISTIC_SYSTEM_RX => {
159 system_rx = Some(characteric);
160 }
161 CHARACTERISTIC_USER_TX => {
162 user_tx = Some(characteric);
163 }
164 CHARACTERISTIC_USER_RX => {
165 user_rx = Some(characteric);
166 }
167 CHARACTERISTIC_PAIRING => {
168 pairing = Some(characteric);
169 }
170 _ => {}
171 }
172 }
173
174 let connection = Self {
175 notification_stream: peripheral.notifications().await?,
176 peripheral,
177 system_tx: system_tx.ok_or(BluetoothError::MissingCharacteristic)?,
178 system_rx: system_rx.ok_or(BluetoothError::MissingCharacteristic)?,
179 user_tx: user_tx.ok_or(BluetoothError::MissingCharacteristic)?,
180 user_rx: user_rx.ok_or(BluetoothError::MissingCharacteristic)?,
181 pairing: pairing.ok_or(BluetoothError::MissingCharacteristic)?,
182 incoming_packets: Vec::new(),
183 };
184
185 connection
186 .peripheral
187 .subscribe(&connection.system_tx)
188 .await?;
189 connection.peripheral.subscribe(&connection.user_tx).await?;
190
191 Ok(connection)
192 }
193
194 pub async fn is_paired(&self) -> Result<bool, BluetoothError> {
195 let auth_bytes = self.peripheral.read(&self.pairing).await?;
196 Ok(u32::from_be_bytes(auth_bytes[0..4].try_into().unwrap()) != UNPAIRED_MAGIC)
197 }
198
199 pub async fn request_pairing(&mut self) -> Result<(), BluetoothError> {
200 self.peripheral
201 .write(
202 &self.pairing,
203 &[0xFF, 0xFF, 0xFF, 0xFF],
204 WriteType::WithoutResponse,
205 )
206 .await?;
207
208 Ok(())
209 }
210
211 pub async fn authenticate_pairing(&mut self, pin: [u8; 4]) -> Result<(), BluetoothError> {
212 self.peripheral
213 .write(&self.pairing, &pin, WriteType::WithoutResponse)
214 .await?;
215
216 let read = self.peripheral.read(&self.pairing).await?;
217
218 if read != pin {
219 return Err(BluetoothError::IncorrectPin);
220 }
221
222 Ok(())
223 }
224
225 async fn receive_one_packet(&mut self) -> Result<(), BluetoothError> {
226 loop {
227 let Some(notification) = self.notification_stream.next().await else {
228 return Err(BluetoothError::NoResponse);
229 };
230
231 if notification.uuid == CHARACTERISTIC_SYSTEM_TX {
232 let data = notification.value;
233 trace!("received packet: {:x?}", data);
234 let packet = RawPacket::new(data);
235 self.incoming_packets.push(packet);
236 break;
237 }
238 }
239
240 Ok(())
241 }
242}
243
244impl Connection for BluetoothConnection {
245 type Error = BluetoothError;
246
247 fn connection_type(&self) -> ConnectionType {
248 ConnectionType::Bluetooth
249 }
250
251 async fn send(&mut self, packet: impl Encode) -> Result<(), BluetoothError> {
252 if !self.is_paired().await? {
253 return Err(BluetoothError::PairingRequired);
254 }
255
256 let mut encoded = vec![0; packet.size()];
258 packet.encode(&mut encoded);
259
260 trace!("sent packet: {:x?}", encoded);
261
262 self.peripheral
264 .write(&self.system_rx, &encoded, WriteType::WithoutResponse)
265 .await?;
266
267 Ok(())
268 }
269
270 async fn recv<P: Decode + CheckHeader>(
271 &mut self,
272 timeout: Duration,
273 ) -> Result<P, BluetoothError> {
274 select! {
276 result = async {
277 loop {
278 for packet in self.incoming_packets.iter_mut() {
279 if packet.check_header::<P>() {
280 match packet.decode_and_use::<P>() {
281 Ok(decoded) => {
282 trim_packets(&mut self.incoming_packets);
283 return Ok(decoded);
284 }
285 Err(e) => {
286 error!("Failed to decode packet with valid header: {}", e);
287 packet.used = true;
288 return Err(BluetoothError::DecodeError(e));
289 }
290 }
291 }
292 }
293 trim_packets(&mut self.incoming_packets);
294 self.receive_one_packet().await?;
295 }
296 } => result,
297 _ = sleep(timeout) => Err(BluetoothError::Timeout)
298 }
299 }
300
301 async fn read_user(&mut self, buf: &mut [u8]) -> Result<usize, BluetoothError> {
302 let value = self.peripheral.read(&self.user_tx).await?;
303 let n = value.len().min(buf.len());
304 buf[..n].copy_from_slice(&value[..n]);
305
306 Ok(n)
307 }
308
309 async fn write_user(&mut self, buf: &[u8]) -> Result<usize, BluetoothError> {
310 self.peripheral
311 .write(&self.user_rx, buf, WriteType::WithoutResponse)
312 .await?;
313 Ok(buf.len())
314 }
315}
316
317#[derive(Error, Debug)]
318pub enum BluetoothError {
319 #[error("IO Error: {0}")]
320 IoError(#[from] std::io::Error),
321 #[error("Packet decoding error: {0}")]
322 DecodeError(#[from] DecodeError),
323 #[error("Packet timeout")]
324 Timeout,
325 #[error("NACK received: {0:?}")]
326 Nack(#[from] Cdc2Ack),
327 #[error("Bluetooth Error")]
328 Btleplug(#[from] btleplug::Error),
329 #[error("No response received over bluetooth")]
330 NoResponse,
331 #[error("No Bluetooth Adapter Found")]
332 NoBluetoothAdapter,
333 #[error("Expected a Bluetooth characteristic that didn't exist")]
334 MissingCharacteristic,
335 #[error("Authentication PIN code was incorrect")]
336 IncorrectPin,
337 #[error("Pairing is required")]
338 PairingRequired,
339 #[error(transparent)]
340 FixedStringSizeError(#[from] FixedStringSizeError),
341}