diff --git a/Cargo.lock b/Cargo.lock index 623a5cc..fb3e3cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,6 +9,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "anyhow" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" + [[package]] name = "async-channel" version = "1.6.1" @@ -589,8 +595,10 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" name = "lego-powered-up" version = "0.1.0" dependencies = [ + "anyhow", "btleplug", "lazy_static", + "log", "num-derive", "num-traits", "rand", diff --git a/lego-powered-up/Cargo.toml b/lego-powered-up/Cargo.toml index 5bee14e..e73b844 100644 --- a/lego-powered-up/Cargo.toml +++ b/lego-powered-up/Cargo.toml @@ -9,8 +9,10 @@ edition = "2018" # https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1.0" btleplug = "0.7" lazy_static = "1.4" +log = "0.4" num-derive = "0.3" num-traits = "0.2" rand = "0.8" diff --git a/lego-powered-up/src/main.rs b/lego-powered-up/src/lib.rs similarity index 61% rename from lego-powered-up/src/main.rs rename to lego-powered-up/src/lib.rs index b7a9ae6..cb20820 100644 --- a/lego-powered-up/src/main.rs +++ b/lego-powered-up/src/lib.rs @@ -1,11 +1,9 @@ -#![allow(dead_code)] +use anyhow::{Context, Result}; use btleplug::api::PeripheralProperties; -#[allow(unused_imports)] -use rand::{thread_rng, Rng}; - -#[allow(unused_imports)] -use std::thread; - +use num_traits::FromPrimitive; +use std::sync::mpsc::{self, Receiver, Sender}; +use std::sync::{Arc, RwLock}; +use std::thread::{self, JoinHandle}; #[allow(unused_imports)] use btleplug::api::{Central, CentralEvent, Characteristic, Peripheral}; @@ -19,8 +17,6 @@ use btleplug::corebluetooth::{adapter::Adapter, manager::Manager}; #[cfg(target_os = "windows")] use btleplug::winrtble::{adapter::Adapter, manager::Manager}; -use num_traits::FromPrimitive; - use consts::*; mod consts; @@ -43,12 +39,110 @@ fn get_central(manager: &Manager) -> Adapter { adapters.into_iter().next().unwrap() } -/** -If you are getting run time error like that : - thread 'main' panicked at 'Can't scan BLE adapter for connected devices...: PermissionDenied', src/libcore/result.rs:1188:5 - you can try to run app with > sudo ./discover_adapters_peripherals - on linux -**/ +pub enum PoweredUpEvent { + HubDiscovered, +} + +pub struct PoweredUp { + manager: Manager, + adapter: Arc>, + event_rx: Option>, + pu_event_tx: Option>, + pu_event_rx: Option>, + worker_thread: Option>>, + pub hubs: Vec>, +} + +impl PoweredUp { + pub fn init() -> Result { + let manager = Manager::new()?; + let adapters = manager.adapters()?; + let adapter = + adapters.into_iter().next().context("No adapter found")?; + let event_rx = Some( + adapter + .event_receiver() + .context("Unable to access event receiver")?, + ); + + let (pu_event_tx, pu_event_rx) = mpsc::channel(); + + Ok(Self { + manager, + adapter: Arc::new(RwLock::new(adapter)), + event_rx, + pu_event_tx: Some(pu_event_tx), + pu_event_rx: Some(pu_event_rx), + worker_thread: None, + hubs: Vec::new(), + }) + } + + pub fn event_receiver(&mut self) -> Option> { + self.pu_event_rx.take() + } + + pub fn start_scan(&mut self) -> Result<()> { + self.adapter.write().unwrap().start_scan()?; + + let mut worker = Worker { + pu_event_tx: self + .pu_event_tx + .take() + .context("Unable to access event transmitter")?, + event_rx: self + .event_rx + .take() + .context("Unable to access btle event receiver")?, + adapter: self.adapter.clone(), + }; + + let handle = thread::spawn(move || worker.run()); + self.worker_thread = Some(handle); + Ok(()) + } +} + +struct Worker { + pub pu_event_tx: Sender, + pub event_rx: Receiver, + pub adapter: Arc>, +} + +impl Worker { + pub fn run(&mut self) -> Result<()> { + use CentralEvent::*; + loop { + // This is in a loop rather than a while let so that the + // mpsc error gets propagated + let evt = self.event_rx.recv()?; + match evt { + DeviceDiscovered(dev) => { + let adapter = self.adapter.write().unwrap(); + let peripheral = adapter.peripheral(dev).unwrap(); + println!( + "peripheral : {:?} is connected: {:?}", + peripheral.properties().local_name, + peripheral.is_connected() + ); + if peripheral.properties().local_name.is_some() + && !peripheral.is_connected() + { + if let Some(hub_type) = peripheral.identify() { + println!("Looks like a '{:?}' hub!", hub_type); + } else { + println!( + "Device does not look like a PoweredUp Hub" + ); + } + } + } + _ => {} + } + } + } +} + fn main() { let manager = Manager::new().unwrap(); let adapter = get_central(&manager); @@ -79,7 +173,7 @@ fn main() { if let Some(hub_type) = peripheral.identify() { println!("Looks like a '{:?}' hub!", hub_type); } else { - println!("Device does not look like a PoweredUp Hub"); + println!("Device does not look like a PoweredUp Hub"); } //let hub = register_hub(&peripheral.properties()); @@ -149,16 +243,16 @@ pub trait IdentifyHub { } /* -PeripheralProperties -{ +PeripheralProperties +{ address: 90:84:2B:60:3C:B8, address_type: Public, local_name: Some("game"), tx_power_level: Some(-66), manufacturer_data: {919: [0, 128, 6, 0, 97, 0]}, - service_data: {}, - services: [00001623-1212-efde-1623-785feabcd123], - discovery_count: 1, + service_data: {}, + services: [00001623-1212-efde-1623-785feabcd123], + discovery_count: 1, has_scan_response: false } */ diff --git a/pu-util/src/main.rs b/pu-util/src/main.rs index e7a11a9..9b42416 100644 --- a/pu-util/src/main.rs +++ b/pu-util/src/main.rs @@ -1,3 +1,13 @@ +use lego_powered_up::{PoweredUp}; + fn main() { println!("Hello, world!"); + + let mut pu = PoweredUp::init().unwrap(); + let rx = pu.event_receiver().unwrap(); + pu.start_scan(); + + while let Ok(evt) = rx.recv() { + + } } diff --git a/lego-powered-up/rustfmt.toml b/rustfmt.toml similarity index 100% rename from lego-powered-up/rustfmt.toml rename to rustfmt.toml