include display driver,

and use rust_tls to make crosscompile easy
This commit is contained in:
2025-09-03 03:47:52 +02:00
parent a10f466538
commit b1b4356686
17 changed files with 3240 additions and 1534 deletions

274
src/epaper.rs Normal file
View File

@@ -0,0 +1,274 @@
use embedded_graphics::{
image::{Image, ImageRaw}, mono_font::{MonoTextStyleBuilder},prelude::*, text::{Baseline, Text, TextStyleBuilder}
};
use crate::epd7in5_v2::Epd7in5;
use crate::epd7in5_v2::Display7in5;
use crate::graphics::Display;
use linux_embedded_hal::{
spidev::{self, SpidevOptions}, Delay, SPIError, SpidevDevice
};
use crate::traits::{*};
use crate::color::Color;
use embedded_hal::digital::OutputPin;
use embedded_hal::digital::PinState;
use crate::killinfo::KillInfo;
use image::RgbImage;
pub struct EPaper {
spi : SpidevDevice,
epd: Epd7in5<SpidevDevice, gpiocdev_embedded_hal::InputPin, gpiocdev_embedded_hal::OutputPin, gpiocdev_embedded_hal::OutputPin, Delay>,
display: Display<800, 480, false, 48000 , Color>,
delay :Delay,
}
impl EPaper {
pub fn new() -> Result<EPaper, SPIError> {
let mut spi = SpidevDevice::open("/dev/spidev0.0").expect("spidev directory");
let options = SpidevOptions::new()
.bits_per_word(8)
.max_speed_hz(10_000_000)
.mode(spidev::SpiModeFlags::SPI_MODE_0)
.build();
spi.configure(&options).expect("spi configuration");
// setup display pins
let mut cs = gpiocdev_embedded_hal::OutputPin::new("/dev/gpiochip0", 26, PinState::High).unwrap();
cs.set_high().unwrap();
let busy = gpiocdev_embedded_hal::InputPin::new("/dev/gpiochip0", 24).unwrap();
let dc = gpiocdev_embedded_hal::OutputPin::new("/dev/gpiochip0", 25, PinState::High).unwrap();
let rst = gpiocdev_embedded_hal::OutputPin::new("/dev/gpiochip0", 17, PinState::High).unwrap();
let mut delay = Delay {};
println!("START");
let epd7in5 = Epd7in5::new(&mut spi, busy, dc, rst, &mut delay, None).expect("epd new");
let display = Display7in5::default();
println!("Device successfully initialized!");
return Ok(EPaper { spi, epd: epd7in5, display, delay : Delay {}});
}
pub fn flush(&mut self) -> Result<(), SPIError> {
self.epd.update_and_display_frame(&mut self.spi, self.display.buffer(), &mut self.delay)?;
Ok(())
}
pub fn sleep(&mut self) -> Result<(), SPIError> {
self.epd.sleep(&mut self.spi, &mut self.delay)?;
Ok(())
}
pub fn clear(&mut self, on: bool) {
let color = if on {
Color::Black
} else {
Color::White
};
self.display.clear(color).ok();
}
pub fn draw_text(&mut self, x: i32, y: i32, text: &str) {
let style = MonoTextStyleBuilder::new()
.font(&embedded_graphics::mono_font::ascii::FONT_8X13)
.text_color(Color::White)
.background_color(Color::Black)
.build();
let text_style = TextStyleBuilder::new().baseline(Baseline::Top).build();
let _ = Text::with_text_style(text, Point::new(x, y), style, text_style).draw(&mut self.display);
}
pub fn draw_kill_info(&mut self, kill: &KillInfo, x: i32, y: i32) {
let mut current_y = y;
// Draw Alliance logo (64x64) if present
if let Some(alliance) = &kill.victim.alliance {
let bw_vec = rgb_to_bw_dithered(&alliance.logo);
let logo_raw = ImageRaw::<Color>::new(bw_vec.as_slice(), alliance.logo.width() as u32);
Image::new(&logo_raw, Point::new(x, y)).draw(&mut self.display).ok();
}
// Draw Ship icon (64x64) if present
if let Some(ship) = &kill.victim.ship {
let bw_vec = rgb_to_bw_dithered(&ship.icon);
let width = ship.icon.width() as u32;
let raw_image: ImageRaw<'_, Color> = ImageRaw::<Color>::new(&bw_vec, width);
Image::new(&raw_image, Point::new(x + 64 + 4, current_y)).draw(&mut self.display).ok();
}
let text_x = x + 64 + 64 + 8;
let style = MonoTextStyleBuilder::new()
.font(&embedded_graphics::mono_font::ascii::FONT_8X13)
.text_color(Color::White)
.background_color(Color::Black)
.build();
current_y += 8;
// Character name + Alliance short
let alliance_short = kill
.victim
.alliance
.as_ref()
.map(|a| a.short.as_str())
.unwrap_or("");
let char_name = kill
.victim
.character
.as_ref()
.map(|a| a.name.as_str())
.unwrap_or("");
let char_line = format!("{} [{}]", char_name, alliance_short);
Text::new(&char_line, Point::new(text_x, current_y), style).draw(&mut self.display).ok();
current_y += 16;
// Ship name + total value
let ship_name = kill.victim.ship.as_ref().map(|s| s.name.as_str()).unwrap_or("Unknown");
let value_line = format!("{} - {:.2}M ISK", ship_name, kill.total_value / 1_000_000f64);
Text::new(&value_line, Point::new(text_x, current_y), style).draw(&mut self.display).ok();
current_y += 16;
// System name
Text::new(&kill.system_name, Point::new(text_x, current_y), style).draw(&mut self.display).ok();
}
pub fn draw_loss_info(&mut self, kill: &KillInfo, x: i32, y: i32) {
let mut current_y = y;
// Draw Alliance logo (64x64) if present
if let Some(character) = &kill.victim.character {
let bw_vec = rgb_to_bw_dithered(&character.portrait);
let logo_raw = ImageRaw::<Color>::new(&bw_vec, character.portrait.width() as u32);
Image::new(&logo_raw, Point::new(x, y)).draw(&mut self.display).ok();
}
// Draw Ship icon (64x64) if present
if let Some(ship) = &kill.victim.ship {
let bw_vec = rgb_to_bw_dithered(&ship.icon);
let logo_raw = ImageRaw::<Color>::new(&bw_vec, ship.icon.width() as u32);
Image::new(&logo_raw, Point::new(x + 64 + 4, current_y)).draw(&mut self.display).ok();
}
let text_x = x + 64 + 64 + 8;
let style = MonoTextStyleBuilder::new()
.font(&embedded_graphics::mono_font::ascii::FONT_8X13)
.text_color(Color::Black)
.background_color(Color::White)
.build();
current_y += 8;
// Character name + Alliance short
let alliance_short = kill
.victim
.alliance
.as_ref()
.map(|a| a.short.as_str())
.unwrap_or("");
let char_name = kill
.victim
.character
.as_ref()
.map(|a| a.name.as_str())
.unwrap_or("");
let char_line = format!("{} [{}]", char_name, alliance_short);
Text::new(&char_line, Point::new(text_x, current_y), style).draw(&mut self.display).ok();
current_y += 16;
// Ship name + total value
let ship_name = kill.victim.ship.as_ref().map(|s| s.name.as_str()).unwrap_or("Unknown");
let value_line = format!("{} - {:.2}M ISK", ship_name, kill.total_value / 1_000_000f64);
Text::new(&value_line, Point::new(text_x, current_y), style).draw(&mut self.display).ok();
current_y += 16;
// System name
Text::new(&kill.system_name, Point::new(text_x, current_y), style).draw(&mut self.display).ok();
}
}
fn rgb_to_bw_dithered(rgb: &RgbImage) -> Vec<u8> {
let w = rgb.width() as usize;
let h = rgb.height() as usize;
// Convert to grayscale f32 buffer
let mut gray: Vec<f32> = rgb
.pixels()
.map(|p| {
let [r, g, b] = p.0;
0.299 * r as f32 + 0.587 * g as f32 + 0.114 * b as f32
})
.collect();
let mut bits = Vec::new();
let mut byte = 0u8;
let mut bit_idx = 7;
for y in 0..h {
for x in 0..w {
let idx = y * w + x;
let old = gray[idx];
let new = if old >= 128.0 { 255.0 } else { 0.0 };
let error = old - new;
// Quantize to B/W
let bw = new > 127.0;
if bw {
byte |= 1 << bit_idx;
}
bit_idx -= 1;
if bit_idx == -1 {
bits.push(byte);
byte = 0;
bit_idx = 7;
}
// Distribute error (FloydSteinberg)
if x + 1 < w {
gray[idx + 1] += error * 7.0 / 16.0;
}
if y + 1 < h {
if x > 0 {
gray[idx + w - 1] += error * 3.0 / 16.0;
}
gray[idx + w] += error * 5.0 / 16.0;
if x + 1 < w {
gray[idx + w + 1] += error * 1.0 / 16.0;
}
}
}
}
if bit_idx > 0 {
bits.push(byte);
}
bits
}