Basic image drawing and clustring working (still slow, and still a lot of hardcoded stuff).
This commit is contained in:
commit
348eb6bedf
6 changed files with 1267 additions and 0 deletions
193
src/led.rs
Normal file
193
src/led.rs
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
use std::{collections::HashMap, io::Write, str::FromStr, time::Duration};
|
||||
|
||||
use bluer::{
|
||||
Adapter, Address, Device, DiscoveryFilter, Uuid,
|
||||
gatt::{
|
||||
WriteOp,
|
||||
remote::{Characteristic, CharacteristicWriteRequest},
|
||||
},
|
||||
};
|
||||
use futures_lite::StreamExt;
|
||||
use tokio::time::timeout;
|
||||
|
||||
const ADDRESS: &str = "B2:E5:7C:A7:A6:23";
|
||||
const CHAR_UUID: &str = "0000fa02-0000-1000-8000-00805f9b34fb";
|
||||
|
||||
pub struct LEDDevice {
|
||||
pub width: u8,
|
||||
pub height: u8,
|
||||
pub address: Address,
|
||||
pub device: Device,
|
||||
pub characterstic: Characteristic,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Position(pub u8, pub u8);
|
||||
#[derive(Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct RGB(pub u8, pub u8, pub u8);
|
||||
|
||||
pub struct Pixel {
|
||||
pub position: Position,
|
||||
pub color: RGB,
|
||||
}
|
||||
pub struct PixelCluster {
|
||||
pub positions: Vec<Position>,
|
||||
pub color: RGB,
|
||||
}
|
||||
|
||||
pub trait PixelToByte {
|
||||
type Output;
|
||||
fn to_byte(&self) -> Self::Output;
|
||||
}
|
||||
impl PixelToByte for Pixel {
|
||||
type Output = Vec<u8>;
|
||||
|
||||
fn to_byte(&self) -> Self::Output {
|
||||
let rgb = &self.color;
|
||||
let pos = &self.position;
|
||||
vec![
|
||||
0x0a, 0x00, 0x05, 0x01, 0x00, rgb.0, rgb.1, rgb.2, pos.0, pos.1,
|
||||
]
|
||||
}
|
||||
}
|
||||
impl PixelToByte for PixelCluster {
|
||||
type Output = Vec<Vec<u8>>;
|
||||
fn to_byte(&self) -> Vec<Vec<u8>> {
|
||||
let color = &self.color;
|
||||
let chunks = self.positions.as_chunks::<120>();
|
||||
|
||||
let mut buf = Vec::with_capacity(chunks.0.len() + 1);
|
||||
|
||||
let mut push_chunk = |positions: &[Position]| {
|
||||
if positions.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let payload_len = positions.len() * 2;
|
||||
let total = 8 + payload_len;
|
||||
let mut out = Vec::with_capacity(total);
|
||||
|
||||
out.extend_from_slice(&[total as u8, 0, 5, 1, 0, color.0, color.1, color.2]);
|
||||
|
||||
for p in positions {
|
||||
out.push(p.0);
|
||||
out.push(p.1);
|
||||
}
|
||||
buf.push(out);
|
||||
};
|
||||
|
||||
for c in chunks.0 {
|
||||
push_chunk(c);
|
||||
}
|
||||
push_chunk(chunks.1);
|
||||
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PixelsToCluster {
|
||||
fn to_cluster(&self) -> Vec<PixelCluster>;
|
||||
}
|
||||
impl PixelsToCluster for Vec<Pixel> {
|
||||
fn to_cluster(&self) -> Vec<PixelCluster> {
|
||||
let mut map: HashMap<RGB, Vec<Position>> = HashMap::new();
|
||||
for px in self {
|
||||
map.entry(px.color).or_default().push(px.position);
|
||||
}
|
||||
map.into_iter()
|
||||
.map(|(color, positions)| PixelCluster {
|
||||
positions: positions,
|
||||
color,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
impl LEDDevice {
|
||||
pub async fn new(
|
||||
width: u8,
|
||||
height: u8,
|
||||
addr: Address,
|
||||
adapter: Adapter,
|
||||
) -> bluer::Result<LEDDevice> {
|
||||
adapter.set_powered(true).await?;
|
||||
|
||||
adapter
|
||||
.set_discovery_filter(DiscoveryFilter {
|
||||
pattern: Some(addr.to_string()),
|
||||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
|
||||
let mut discover = adapter.discover_devices().await?;
|
||||
|
||||
let device = loop {
|
||||
match timeout(Duration::from_secs(10), discover.next()).await {
|
||||
Ok(Some(bluer::AdapterEvent::DeviceAdded(a))) if a == addr => {
|
||||
break adapter.device(addr)?;
|
||||
}
|
||||
Ok(Some(_)) => continue,
|
||||
Ok(None) | Err(_) => {
|
||||
return Err(bluer::Error {
|
||||
kind: bluer::ErrorKind::DoesNotExist,
|
||||
message: "device not found".to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
device.connect().await?;
|
||||
let uuid = Uuid::from_str(CHAR_UUID).unwrap();
|
||||
|
||||
let chr = 'outer: {
|
||||
for svc in device.services().await? {
|
||||
for ch in svc.characteristics().await? {
|
||||
if ch.uuid().await? == uuid {
|
||||
break 'outer ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
panic!("characteristic {} not found on peripheral", uuid);
|
||||
};
|
||||
|
||||
Ok(LEDDevice {
|
||||
width: width,
|
||||
height: height,
|
||||
address: addr,
|
||||
device: device,
|
||||
characterstic: chr,
|
||||
})
|
||||
}
|
||||
|
||||
async fn write_data(&self, data: &[u8]) -> bluer::Result<()> {
|
||||
let chr = &self.characterstic;
|
||||
|
||||
chr.write_ext(
|
||||
data,
|
||||
&CharacteristicWriteRequest {
|
||||
op_type: WriteOp::Request,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub async fn write_pixel(&self, pixel: &Pixel) -> bluer::Result<()> {
|
||||
self.write_data(&pixel.to_byte()).await
|
||||
}
|
||||
pub async fn write_pixelcluster(&self, pixelcluster: &PixelCluster) -> bluer::Result<()> {
|
||||
for data in pixelcluster.to_byte() {
|
||||
self.write_data(&data).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub async fn write_pixelclusters(&self, pixelclusters: &[PixelCluster]) -> bluer::Result<()> {
|
||||
for cluster in pixelclusters {
|
||||
self.write_pixelcluster(cluster).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue