use smart_leds_trait::SmartLedsWrite; use crate::lib8::Rgb8Blend; use crate::lib8::interpolate::Fract8Ops; use crate::render::{Framed, Surface, Display, Surfaces}; use crate::buffers::SurfacePool; use crate::power::{brightness_for_mw, AsMilliwatts}; use crate::geometry::*; use crate::mappings::*; use std::fmt::{Debug, Formatter}; use std::io; use rgb::Rgb; use std::ops::IndexMut; pub trait HardwarePixel: Send + Sync + Rgb8Blend + Copy + AsMilliwatts + Default + From> + Fract8Ops {} impl HardwarePixel for T where T: Send + Sync + Rgb8Blend + Copy + AsMilliwatts + Default + From> + Fract8Ops {} pub trait Pixbuf: AsMilliwatts + IndexMut { fn new() -> Self; fn blank(&mut self); fn iter_with_brightness(&self, brightness: u8) -> impl Iterator + Send; } impl Pixbuf for [T; PIXEL_NUM] { fn new() -> Self { [T::default(); PIXEL_NUM] } fn blank(&mut self) { self.fill(T::default()) } fn iter_with_brightness(&self, brightness: u8) -> impl Iterator + Send { self.iter().map(move |x| { x.scale8(brightness)}) } } struct SmartLedDisplay> { surfaces : Option>, pixmap: StrideMapping, target: T, pixbuf: P, max_mw: u32, frame: usize } impl> Debug for SmartLedDisplay { fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> { f.debug_struct("SmartLedDisplay") .field("total_mw", &self.pixbuf.as_milliwatts()) .field("surfaces", &self.surfaces) .finish() } } impl> SmartLedDisplay { fn new(target: T, max_mw: u32, pixmap: StrideMapping, pixbuf: P) -> Self { SmartLedDisplay { pixbuf, surfaces: Some(SurfacePool::new()), target, max_mw, pixmap, frame: 0 } } } impl AsMilliwatts for SmartLedDisplay where T: FastWrite, S: Surface, P: Pixbuf { fn as_milliwatts(&self) -> u32 { self.pixbuf.as_milliwatts() } } impl Surfaces for SmartLedDisplay where T: FastWrite, S: Surface, P: Pixbuf { fn new_surface(&mut self, area: &Rectangle) -> Result { if let Some(ref mut s) = self.surfaces { s.new_surface(area) } else { panic!("Could not grab surface list") } } } impl Display for SmartLedDisplay where T: FastWrite, S: Surface, P: Pixbuf { fn render_frame(&mut self) { let surfaces = self.surfaces.take().unwrap(); for surface in surfaces.iter() { let rect = surface.rect().clone(); let mut sel = self.pixmap.select(&rect); let opacity = surface.opacity(); if opacity > 0 { surface.with_shader(|shader| { while let Some((virt_coords, phys_coords)) = sel.next() { let idx = self.pixmap.to_idx(&phys_coords); self.pixbuf[idx] = self.pixbuf[idx].blend8(shader.draw(&virt_coords, self.frame).into(), opacity); } }) } } self.surfaces = Some(surfaces); } } trait FastWrite { type Target: SmartLedsWrite; type Color: HardwarePixel; type Error; fn fast_write(&mut self, iterator: T) -> Result<(), Self::Error> where T: IntoIterator, I: Into, ::IntoIter: Send; } impl Framed for SmartLedDisplay where T: FastWrite, S: Surface, P: Pixbuf { fn start_frame(&mut self) { self.pixbuf.blank(); } fn end_frame(&mut self) { let b = brightness_for_mw(self.pixbuf.as_milliwatts(), 255, self.max_mw); if let Err(_) = self.target.fast_write(self.pixbuf.iter_with_brightness(b)) { panic!("Could not write frame!"); } self.frame += 1; } } #[cfg(feature="rmt")] pub mod rmt { use esp_idf_svc::hal::prelude::Peripherals; use ws2812_esp32_rmt_driver::driver::color::{LedPixelColor, LedPixelColorGrb24}; use smart_leds::SmartLedsWrite; use rgb::Rgb; use ws2812_esp32_rmt_driver::LedPixelEsp32Rmt; use crate::mappings::StrideMapping; use crate::render::{Display, Surface}; use crate::platform::DisplayInit; use super::{Pixbuf, FastWrite, SmartLedDisplay}; use crate::lib8::Rgb8Blend; use crate::lib8::interpolate::{Fract8, Fract8Ops}; use crate::power::AsMilliwatts; pub type FastWs2812Esp32Rmt<'a> = LedPixelEsp32Rmt<'a, Rgb, LedPixelColorGrb24>; impl Fract8Ops for LedPixelColorGrb24 { fn blend8(self, other: Self, scale: Fract8) -> Self { self } fn scale8(self, scale: Fract8) -> Self { self } } impl AsMilliwatts for LedPixelColorGrb24 { fn as_milliwatts(&self) -> u32 { Rgb::new(self.r(), self.g(), self.b()).as_milliwatts() } } impl Rgb8Blend for LedPixelColorGrb24 { fn saturating_add>(self, b: T) -> Self where Self: Sized { let s = b.into(); LedPixelColorGrb24::new_with_rgb(s.r(), s.g(), s.b()) } } impl FastWrite for FastWs2812Esp32Rmt<'_> { type Color = ::Color; type Error = ::Error; type Target = Self; fn fast_write(&mut self, iterator: T) -> Result<(), Self::Error> where T: IntoIterator, I: Into, ::IntoIter: Send { self.write_nocopy(iterator) } } impl DisplayInit for FastWs2812Esp32Rmt<'_> { fn new_display() -> impl Display { let peripherals = Peripherals::take().unwrap(); let led_pin = peripherals.pins.gpio14; //let led_pin = peripherals.pins.gpio5; 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 pixbuf: [Rgb; 310] = Pixbuf::new(); let pixbuf: [::Color; 310] = Pixbuf::new(); let pixmap = StrideMapping::new_jar(); assert!(pixmap.pixel_count <= pixbuf.len(), "map needs {} pixels, I only have PIXEL_NUM={}", pixmap.pixel_count, pixbuf.len()); let target = Self::new(channel, led_pin).unwrap(); return SmartLedDisplay::new(target, MAX_POWER_MW, pixmap, pixbuf); } } } #[cfg(feature="spi")] pub mod spi { use smart_leds::SmartLedsWrite; use ws2812_spi::prerendered::Ws2812; use crate::render::{Display, Surface}; use crate::platform::smart_leds_lib::SmartLedDisplay; use crate::DisplayInit; use super::FastWrite; use esp_idf_svc::hal::{ prelude::*, gpio::AnyIOPin, spi::{ config::{Config, DriverConfig}, Dma, SpiBusDriver, SpiDriver, } }; impl<'a> FastWrite for Ws2812<'_, SpiBusDriver<'a, SpiDriver<'a>>> { type Color = ::Color; type Error = ::Error; type Target = Self; fn fast_write(&mut self, iterator: T) -> Result<(), Self::Error> where T: IntoIterator, I: Into, ::IntoIter: Send { let resp = self.write(iterator); if let Err(e) = resp { panic!("Could not write SPI frame! {:?}", e) } else { resp } } } static mut STATIC_BUFFER: [u8; 400 * 12] = [0; 400 * 12]; pub struct SPIDisplay {} impl DisplayInit for SPIDisplay { fn new_display() -> impl Display { let peripherals = Peripherals::take().unwrap(); let driver = SpiDriver::new_without_sclk( peripherals.spi2, peripherals.pins.gpio5, Option::::None, &DriverConfig::new().dma(Dma::Auto(4092)) ).unwrap(); let cfg = Config::new().baudrate(3.MHz().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; unsafe { let target = Ws2812::new(spi, &mut STATIC_BUFFER); return SmartLedDisplay::>>, S, 310>::new(target, MAX_POWER_MW); } } } }