use embedded_graphics::{ prelude::*, pixelcolor::Rgb888, primitives::Rectangle }; use rgb::RGB8; use ws2812_esp32_rmt_driver::lib_embedded_graphics::{Ws2812DrawTarget, LedPixelShape}; use esp_idf_svc::hal::prelude::Peripherals; use running_average::RealTimeRunningAverage; use std::io; use crate::power; use crate::power::AsMilliwatts; use crate::lib8::*; use crate::render::*; use crate::time::Periodically; use crate::task::Task; use crate::geometry::*; use crate::platform::DisplayInit; pub struct EmbeddedDisplay where T: DrawTarget, S: Surface { surfaces : SurfacePool, target: T, total_mw: u32, max_mw: u32, fps: RealTimeRunningAverage, frame: u32, fps_display: Periodically } impl Task for EmbeddedDisplay, S> { fn start(&mut self) { self.target.set_brightness(0); } fn name(&self) -> &'static str { "Renderer" } fn tick(&mut self) { self.start_frame(); self.render_frame(); self.end_frame(); } } impl EmbeddedDisplay where T: DrawTarget, S: Surface { pub fn new(target: T, max_mw: u32) -> Self { EmbeddedDisplay { surfaces: SurfacePool::new(), target: target, max_mw: max_mw, total_mw: 0, fps: RealTimeRunningAverage::default(), frame: 0, fps_display: Periodically::new_every_n_seconds(5) } } } impl Surfaces for EmbeddedDisplay where T: DrawTarget, S: Surface { fn new_surface(&mut self) -> Result { self.surfaces.new_surface() } } impl Display for EmbeddedDisplay, S> { fn start_frame(&mut self) { self.total_mw = 0; self.frame = self.frame.wrapping_add(1); } fn end_frame(&mut self) { let brightness = power::brightness_for_mw(self.total_mw, 255, self.max_mw); self.target.set_brightness(brightness); self.target.flush().unwrap(); self.fps.insert(1); self.fps_display.run(|| { log::info!("FPS: {} frame={} brightness={} mw={}", self.fps.measurement(), self.frame, brightness, self.total_mw); }); } fn render_frame(&mut self) { let size = T::size(); let xStride: u8 = 255 / (size.width as u8); let yStride: u8 = 255 / (size.height as u8); let area = Rectangle::new(Point::new(0, 0), size); self.target.draw_iter( area.points() .map(|pos| { let virtCoords = VirtualCoordinates::new(pos.x as u8 * xStride, pos.y as u8 * yStride); let mut pixel = RGB8::new(0, 0, 0); for surface in self.surfaces.iter() { surface.with_shader(|shader| { pixel = pixel.saturating_add(shader.draw(virtCoords.clone())); }) } self.total_mw += pixel.as_milliwatts(); return Pixel(pos, Rgb888::new(pixel.r, pixel.g, pixel.b)); }) ).unwrap(); } } impl DisplayInit for Ws2812DrawTarget<'_, Shape> { fn new_display() -> impl Display + Task { let peripherals = Peripherals::take().unwrap(); let led_pin = peripherals.pins.gpio14; let channel = peripherals.rmt.channel0; const POWER_VOLTS : u32 = 5; const POWER_MA : u32 = 500; const MAX_POWER_MW : u32 = POWER_VOLTS * POWER_MA; let target = Self::new(channel, led_pin).unwrap(); return EmbeddedDisplay::::new(target, MAX_POWER_MW); } } pub struct PonderjarMatrix {} impl LedPixelShape for PonderjarMatrix { fn size() -> Size { Size::new(17, 17) } fn pixel_index(point: Point) -> Option { if (0..Self::size().width as i32).contains(&point.x) && (0..Self::size().height as i32).contains(&point.y) { if point.y % 2 == 0 { Some((point.y as u32 * Self::size().width as u32 + point.x as u32).try_into().unwrap()) } else { Some((point.y as u32 * Self::size().width as u32 - point.x as u32).try_into().unwrap()) } } else { None } } } pub type PonderjarTarget<'a> = Ws2812DrawTarget<'a, PonderjarMatrix>;