diff --git a/src/bluetooth.rs b/src/bluetooth.rs index dfa521b..9039815 100644 --- a/src/bluetooth.rs +++ b/src/bluetooth.rs @@ -1,9 +1,8 @@ use std::collections::HashSet; -use std::sync::mpsc::{channel, Sender}; use std::time::Duration; use bluer::{Adapter, AdapterEvent, Address, Device, Result, Uuid}; +use bluer::gatt::remote::Characteristic; use futures::{pin_mut, StreamExt}; -use tokio::task::spawn_blocking; use tokio::sync::mpsc; // const CONNECT_RETRIES: u8 = 3; @@ -47,7 +46,7 @@ async fn bluetooth_scan(tx: mpsc::Sender, adapter: Adapter, uuid: Uuid) match has_service(&device, &uuid).await { Ok(service_found) => { if service_found { - tx.send(device).await; + tx.send(device).await.unwrap(); println!("found service in device {}", addr); } } @@ -66,8 +65,8 @@ async fn bluetooth_scan(tx: mpsc::Sender, adapter: Adapter, uuid: Uuid) 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 timeout = tokio::time::timeout(Duration::from_secs(30), bluetooth_scan(tx, adapter, uuid)); let scan = tokio::spawn(timeout); println!("good"); let mut devices: Vec = Vec::new(); @@ -83,4 +82,40 @@ pub(crate) async fn scan_devices(adapter: Adapter, uuid: Uuid) -> Vec { } devices +} + +pub 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(), + }) +} + +pub async fn get_char(device: &Device, service_uuid: Uuid, char_uuid: Uuid) -> Result> { + for service in device.services().await? { + if service_uuid == service.uuid().await? { + for char in service.characteristics().await? { + println!("uuid : {}", &char.uuid().await?); + if char_uuid == char.uuid().await? { + + return Ok(Some(char)); + } + + } + } + } + Ok(None) } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index fa0aee5..5790686 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,9 @@ mod patterns; mod bluetooth; -use patterns::{PixelColor, Strip}; -use bluer::Device; +use patterns::Strip; use bluer::gatt::remote::Characteristic; -use std::time::Duration; -use tokio::time::sleep; -use crate::patterns::RainbowPattern; +use tokio::sync::watch; const SERVICE_UUID: bluer::Uuid = bluer::Uuid::from_u128(0xadaf0900c33242a893bd25e905756cb8); const PIXEL_DATA_UUID: bluer::Uuid = bluer::Uuid::from_u128(0xadaf0903c33242a893bd25e905756cb8); @@ -21,7 +18,21 @@ async fn main() -> bluer::Result<()> { let devices = bluetooth::scan_devices(adapter, SERVICE_UUID).await; + let (tx, rx) = watch::channel(Strip::::default()); + for device in devices { + bluetooth::connect(&device, 3).await?; + let char = bluetooth::get_char(&device, SERVICE_UUID, PIXEL_DATA_UUID).await?; + if let Some(char) = char { + let mut rx = rx.clone(); + tokio::spawn(async move { + while rx.changed().await.is_ok() { + let strip = *rx.borrow(); + write_strip(&strip, &char).await; + } + }).await; + } + println!("{:?}", device); } @@ -30,6 +41,8 @@ async fn main() -> bluer::Result<()> { Ok(()) } + + // async fn send_seq(char: &Characteristic) -> bluer::Result<()> { // println!(" Characteristic flags : {:?}, ", char.flags().await?); // @@ -50,32 +63,14 @@ async fn main() -> bluer::Result<()> { // Ok(()) // } -// 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(()) -// } +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(()) +} + -// 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(); diff --git a/src/patterns.rs b/src/patterns.rs index 143d940..b9f1ba4 100644 --- a/src/patterns.rs +++ b/src/patterns.rs @@ -1,16 +1,33 @@ -use bluer::gatt::remote::Characteristic; +use std::fmt; -const BASE_STRIP_DATA : [u8; 5] = [0x0, 0x0, 0x0, 0x0, 0x1]; - -#[derive(Clone, Copy)] -pub(crate) struct PixelColor { +#[derive(Clone, Copy, Debug)] +pub struct PixelColor { pub(crate) red: u8, pub(crate) green: u8, pub(crate) blue: u8, } +impl PixelColor { + fn new() -> Self { + Default::default() + } +} -pub(crate) struct Strip { +impl fmt::Display for PixelColor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "[{},{},{}]", self.red, self.green, self.blue) + } +} + +impl Default for PixelColor { + fn default() -> Self { + PixelColor { red: 0, green: 0, blue: 0 } + } +} + + +#[derive(Copy, Clone)] +pub struct Strip { pub strip: [PixelColor; N], } @@ -25,9 +42,56 @@ impl Strip { } } -async fn write_strip(data: Strip, char: Characteristic) -> bluer::Result<()> { - let mut frame= BASE_STRIP_DATA.to_vec(); - frame.append(&mut data.to_array()); - char.write(&*frame).await?; - Ok(()) +impl Default for Strip { + fn default() -> Self { + Strip { strip: [PixelColor::new(); N] } + } } + + +pub struct RainbowPattern { + pub(crate) current_iteration: usize, + pub(crate) max_iteration: Option +} + + +impl Iterator for RainbowPattern { + type Item = Strip; + + fn next(&mut self) -> Option { + if let Some(nbr_iteration) = self.max_iteration { + if nbr_iteration == self.current_iteration { + return None + } + } + let mut strip = Strip::default(); + let step = 255 / N; + for i in 0..N { + let pos = (i*step + self.current_iteration) as u8; + strip.strip[i] = wheel(pos) + } + self.current_iteration = self.current_iteration +1 ; + Some(strip) + } +} + +fn wheel(index: u8) -> PixelColor { + let pos = 255 - index; + match pos { + 0..=85 => PixelColor{ + red: 255 - (pos * 3), + green :0, + blue : pos * 3}, + 86..=170 => { + let pos = pos - 85; + PixelColor{ + red: 0, + green :pos*3, + blue : 255 - (pos * 3)} + } + _ => { + let pos = pos - 170; + return PixelColor{red : pos*3, green : 255 - (pos*3), blue : 0}; + } + } +} \ No newline at end of file