Basic image drawing and clustring working (still slow, and still a lot of hardcoded stuff).

This commit is contained in:
Jurn Wubben 2025-11-02 03:18:30 +01:00
commit 348eb6bedf
6 changed files with 1267 additions and 0 deletions

193
src/led.rs Normal file
View 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(())
}
}