diff --git a/Cargo.lock b/Cargo.lock index 27ef690..74df9a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -233,12 +233,30 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "itoa" version = "1.0.1" @@ -266,6 +284,15 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.14" @@ -354,6 +381,47 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + [[package]] name = "pin-project" version = "1.0.10" @@ -410,6 +478,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + [[package]] name = "rust_lum" version = "0.1.0" @@ -425,6 +502,12 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "serde" version = "1.0.134" @@ -456,12 +539,27 @@ dependencies = [ "serde", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + [[package]] name = "strum" version = "0.22.0" @@ -516,7 +614,11 @@ dependencies = [ "libc", "memchr", "mio", + "num_cpus", + "once_cell", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "tokio-macros", "winapi", ] diff --git a/Cargo.toml b/Cargo.toml index aab3aa0..c55078e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,5 +7,5 @@ edition = "2018" [dependencies] bluer = "0.13.1" -tokio = "1.15.0" +tokio = {version = "1.15.0", features = ["full"]} futures = "0.3.19" \ No newline at end of file diff --git a/bluetooth.rs b/bluetooth.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/bluetooth.rs b/src/bluetooth.rs new file mode 100644 index 0000000..dfa521b --- /dev/null +++ b/src/bluetooth.rs @@ -0,0 +1,86 @@ +use std::collections::HashSet; +use std::sync::mpsc::{channel, Sender}; +use std::time::Duration; +use bluer::{Adapter, AdapterEvent, Address, Device, Result, Uuid}; +use futures::{pin_mut, StreamExt}; +use tokio::task::spawn_blocking; +use tokio::sync::mpsc; + +// const CONNECT_RETRIES: u8 = 3; +// const SERVICE_UUID: bluer::Uuid = bluer::Uuid::from_u128(0xadaf0900c33242a893bd25e905756cb8); +// const PIXEL_DATA_UUID: bluer::Uuid = bluer::Uuid::from_u128(0xadaf0903c33242a893bd25e905756cb8); + +pub(crate) async fn create_session() -> Result +{ + let session = bluer::Session::new().await?; + let adapter_names = session.adapter_names().await?; + let adapter_name = adapter_names.first().expect("no Bluetooth adapter found"); + let adapter = session.adapter(adapter_name)?; + + adapter.set_powered(true).await?; + Ok(adapter) +} + +async fn has_service(device: &Device, uuid: &Uuid) -> Result { + + let uuids = device.uuids().await?.unwrap_or_default(); + if uuids.contains(uuid) { + return Ok(true) + } + Ok(false) +} + +async fn bluetooth_scan(tx: mpsc::Sender, adapter: Adapter, uuid: Uuid) -> Result<()> { + println!("start bluetooth scan"); + let discover = adapter.discover_devices().await?; + let mut already_scanned: HashSet
= HashSet::new(); + pin_mut!(discover); + while let Some(evt) = discover.next().await { + match evt { + AdapterEvent::DeviceAdded(addr) => { + println!("device adress {}", addr); + if already_scanned.contains(&addr) { + continue; + } + already_scanned.insert(addr); + let device = adapter.device(addr)?; + match has_service(&device, &uuid).await { + Ok(service_found) => { + if service_found { + tx.send(device).await; + println!("found service in device {}", addr); + } + } + Err(_) => { continue } + } + } + _ => {} + } + } + + println!("ended"); + Ok(()) +} + + +pub(crate) async fn scan_devices(adapter: Adapter, uuid: Uuid) -> Vec { + println!("start scanning devices"); + let (tx, mut rx) = mpsc::channel(4); +; + let timeout = tokio::time::timeout(Duration::from_secs(30), bluetooth_scan(tx, adapter, uuid)).await; + let scan = tokio::spawn(timeout); + println!("good"); + let mut devices: Vec = Vec::new(); + while let Some(device)= rx.recv().await { + println!("new device received {}", device.address()); + devices.push(device); + println!("len {}", devices.len()); + if devices.len() >= 2 { + println!("drop"); + drop(scan); + break; + } + } + + devices +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 56d9a48..fa0aee5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,129 +1,111 @@ mod patterns; +mod bluetooth; use patterns::{PixelColor, Strip}; -use bluer::{AdapterEvent, Device}; +use bluer::Device; use bluer::gatt::remote::Characteristic; -use futures::{pin_mut, StreamExt}; use std::time::Duration; use tokio::time::sleep; +use crate::patterns::RainbowPattern; const SERVICE_UUID: bluer::Uuid = bluer::Uuid::from_u128(0xadaf0900c33242a893bd25e905756cb8); const PIXEL_DATA_UUID: bluer::Uuid = bluer::Uuid::from_u128(0xadaf0903c33242a893bd25e905756cb8); +const STRIP_SIZE: usize = 25; +const BASE_STRIP_DATA : [u8; 3] = [0x00, 0x00, 0x01]; #[tokio::main(flavor = "current_thread")] async fn main() -> bluer::Result<()> { - let session = bluer::Session::new().await?; - let adapter_names = session.adapter_names().await?; - let adapter_name = adapter_names.first().expect("no Bluetooth adapter found"); - let adapter = session.adapter(adapter_name)?; - adapter.set_powered(true).await?; + let adapter = bluetooth::create_session().await?; - let discover = adapter.discover_devices().await?; - pin_mut!(discover); - - while let Some(evt) = discover.next().await { - match evt { - AdapterEvent::DeviceAdded(addr) => { - let device = adapter.device(addr)?; - match find_neopixel_service(&device).await { - Ok(Some(char)) => { - println!("found characteristic"); - match send_seq(&char).await { - // this loop should never stop - _ => () - } - - } - Ok(None) => { - println!("characteristic not found") - } - Err(err) => { - println!("device error : {}", &err); - let _ = adapter.remove_device(device.address()).await; - } - } - } - AdapterEvent::DeviceRemoved(addr) => { - println!("Device removed {}", addr); - } - _ => (), - } + let devices = bluetooth::scan_devices(adapter, SERVICE_UUID).await; + for device in devices { + println!("{:?}", device); } + Ok(()) } -async fn send_seq(char: &Characteristic) -> bluer::Result<()> { - println!(" Characteristic flags : {:?}, ", char.flags().await?); +// async fn send_seq(char: &Characteristic) -> bluer::Result<()> { +// println!(" Characteristic flags : {:?}, ", char.flags().await?); +// +// let mut strip_red : Strip<10> = Strip::default(); +// strip_red.strip.fill(PixelColor { red: 255, green: 0, blue: 0}); +// +// let mut strip_green: Strip<10> = Strip::default(); +// strip_green.strip.fill(PixelColor { red: 0, green: 255, blue: 0}); +// +// let pattern = RainbowPattern:: { current_iteration: 0,}; +// +// +// for strip in pattern { +// write_strip(strip, char).await?; +// sleep(Duration::from_millis(50)).await; +// } +// +// Ok(()) +// } - let mut strip_red : Strip<10>; - strip_red.strip.fill(PixelColor { red: 255, green: 0, blue: 0}); +// pub async fn write_strip(data: Strip, char: &Characteristic) -> bluer::Result<()> { +// let frame = [BASE_STRIP_DATA.to_vec(), data.to_array()].concat(); +// print!("{:?}",frame); +// char.write(&*frame).await?; +// Ok(()) +// } - let mut strip_green: Strip<10>; - strip_green.strip.fill(PixelColor { red: 0, green: 255, blue: 0}); +// async fn connect(device: &Device, retries: u8) -> bluer::Result<()> { +// if device.is_connected().await? { +// return Ok(()) +// } +// for i in 0..retries { +// match device.connect().await { +// Ok(()) => return Ok(()), +// Err(_) => { +// println!("connection error ({}), retry…", i); +// } +// } +// +// } +// +// Err(bluer::Error { +// kind: bluer::ErrorKind::ConnectionAttemptFailed, +// message: "all attempts fail".parse().unwrap(), +// }) +// } - loop { - char.write(&*strip_green.to_array()).await?; - sleep(Duration::from_secs(1)).await; - char.write(&*strip_red.to_array()).await?; - sleep(Duration::from_secs(1)).await; - } - -} - -async fn connect(device: &Device, retries: u8) -> bluer::Result<()> { - if device.is_connected().await? { - return Ok(()) - } - for i in 0..retries { - match device.connect().await { - Ok(()) => return Ok(()), - Err(_) => { - println!("connection error ({}), retry…", i); - } - } - - } - - Err(bluer::Error { - kind: bluer::ErrorKind::ConnectionAttemptFailed, - message: "all attempts fail".parse().unwrap(), - }) -} - -async fn find_neopixel_service(device: &Device) -> bluer::Result> { - let addr = device.address(); - let uuids = device.uuids().await?.unwrap_or_default(); - - if uuids.contains(&SERVICE_UUID) { - println!("service neopixel found for device {}", &addr); - match connect(device, 3).await { - Ok(()) => { - println!("successefully connected"); - } - Err(err) => { - println!("unable to connect, trying new device, {}", err); - return Err(err) - } - } - for service in device.services().await? { - if SERVICE_UUID == service.uuid().await? { - for char in service.characteristics().await? { - println!("uuid : {}", &char.uuid().await?); - if PIXEL_DATA_UUID == char.uuid().await? { - - return Ok(Some(char)); - } - - } - } - } - - } - - Ok(None) -} \ No newline at end of file +// async fn find_neopixel_service(device: &Device) -> bluer::Result> { +// let addr = device.address(); +// let uuids = device.uuids().await?.unwrap_or_default(); +// +// if uuids.contains(&SERVICE_UUID) { +// println!("service neopixel found for device {}", &addr); +// match connect(device, 3).await { +// Ok(()) => { +// println!("successefully connected"); +// } +// Err(err) => { +// println!("unable to connect, trying new device, {}", err); +// return Err(err) +// } +// } +// for service in device.services().await? { +// if SERVICE_UUID == service.uuid().await? { +// for char in service.characteristics().await? { +// println!("uuid : {}", &char.uuid().await?); +// if PIXEL_DATA_UUID == char.uuid().await? { +// +// return Ok(Some(char)); +// } +// +// } +// } +// } +// +// } +// +// Ok(None) +// } \ No newline at end of file