use smart_leds_trait::SmartLedsWrite; use running_average::RealTimeRunningAverage; use crate::render::{Surface, SurfacePool, Display, Surfaces}; use crate::task::Task; use crate::power::{brightness_for_mw, AsMilliwatts}; use crate::time::Periodically; use crate::geometry::*; use smart_leds::brightness; use std::io; use rgb::Rgb; pub struct SmartLedDisplay>, S: Surface> { surfaces : SurfacePool, target: T, fps: RealTimeRunningAverage, frame: u32, fps_display: Periodically, pixbuf: [T::Color; 255], total_mw: u32, max_mw: u32 } impl>, S: Surface> SmartLedDisplay { pub fn new(target: T, max_mw: u32) -> Self { SmartLedDisplay { 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), pixbuf: [Rgb::new(0, 0, 0); 255] } } } impl>, S: Surface> Task for SmartLedDisplay { fn name(&self) -> &'static str { "Renderer" } fn tick(&mut self) { self.start_frame(); self.render_frame(); self.end_frame(); } } impl Surfaces for SmartLedDisplay where T: SmartLedsWrite>, S: Surface { fn new_surface(&mut self) -> Result { self.surfaces.new_surface() } } impl>, S: Surface> Display for SmartLedDisplay { fn start_frame(&mut self) { self.frame = self.frame.wrapping_add(1); self.total_mw = 0; } fn end_frame(&mut self) { self.fps.insert(1); let b = brightness_for_mw(self.total_mw, 255, self.max_mw); self.fps_display.run(|| { log::info!("FPS: {} frame={} brightness={} mw={}", self.fps.measurement(), self.frame, b, self.total_mw); }); let _ = self.target.write(brightness(self.pixbuf.iter().cloned(), b)); } fn render_frame(&mut self) { for x in 0..self.pixbuf.len() { let virt_coords = VirtualCoordinates::new(x as u8, 0); let mut pixel = Rgb::new(0, 0, 0); for surface in self.surfaces.iter() { surface.with_shader(|shader| { pixel = shader.draw(virt_coords.clone()); }) } self.total_mw += pixel.as_milliwatts(); self.pixbuf[x] = Rgb::new(pixel.r, pixel.g, pixel.b); }; } } #[cfg(feature="rmt")] pub mod rmt { use esp_idf_svc::hal::prelude::Peripherals; use ws2812_esp32_rmt_driver::lib_smart_leds::Ws2812Esp32Rmt; use crate::render::{Display, Surface}; use crate::task::Task; use crate::smart_leds_lib::SmartLedDisplay; use crate::platform::DisplayInit; impl DisplayInit for Ws2812Esp32Rmt<'_> { 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 SmartLedDisplay::new(target, MAX_POWER_MW); } } } #[cfg(feature="spi")] pub mod spi { use ws2812_spi::Ws2812; use crate::render::{Display, Surface}; use crate::task::Task; use crate::smart_leds_lib::SmartLedDisplay; use esp_idf_svc::hal::{ prelude::*, gpio::AnyIOPin, spi::{ config::{Config, DriverConfig}, Dma, SpiBusDriver, SpiDriver, } }; pub struct SPIDisplay {} impl DisplayInit for SPIDisplay { fn new_display() -> impl Display + Task { let peripherals = Peripherals::take().unwrap(); let driver = SpiDriver::new_without_sclk( peripherals.spi2, peripherals.pins.gpio14, Option::::None, &DriverConfig::new().dma(Dma::Auto(512)) ).unwrap(); let cfg = Config::new().baudrate(3_200.kHz().into()); let spi = SpiBusDriver::new(driver, &cfg).unwrap(); const POWER_VOLTS : u32 = 5; const POWER_MA : u32 = 500; const MAX_POWER_MW : u32 = POWER_VOLTS * POWER_MA; let target = Ws2812::new(spi); return SmartLedDisplay::new(target, MAX_POWER_MW) } } }