diff --git a/Cargo.toml b/Cargo.toml index 023ff6b..873e4c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,12 @@ debug = true # Symbols are nice and they don't increase the size on Flash opt-level = "z" [features] -default = ["std", "embassy", "esp-idf-svc/native"] +default = ["std", "embassy", "esp-idf-svc/native", "rmt", "smart-leds"] + +embedded-graphics = ["dep:embedded-graphics", "ws2812-esp32-rmt-driver/embedded-graphics-core"] +smart-leds = ["dep:smart-leds", "dep:smart-leds-trait", "ws2812-esp32-rmt-driver/smart-leds-trait"] +spi = ["dep:ws2812-spi"] +rmt = ["dep:ws2812-esp32-rmt-driver"] pio = ["esp-idf-svc/pio"] std = ["alloc", "esp-idf-svc/binstart", "esp-idf-svc/std"] @@ -30,17 +35,21 @@ embassy = ["esp-idf-svc/embassy-sync", "esp-idf-svc/critical-section", "esp-idf- [dependencies] log = { version = "0.4", default-features = false } esp-idf-svc = { version = "0.49", default-features = false } -ws2812-esp32-rmt-driver = { version = "*", features = ["embedded-graphics-core", "smart-leds-trait"]} -embedded-graphics = { version = "0.8.1", features = ["fixed_point", "defmt"] } hsv = "0.1.1" palette = { version = "0.7.6" } embedded-canvas = "0.3.1" embassy-executor = "0.6.0" running-average = "0.1.0" -ws2812-spi = "0.5.0" -smart-leds-trait = "0.3.0" rgb = "0.8.50" -smart-leds = "0.4.0" + +ws2812-esp32-rmt-driver = { version = "*", optional = true } + +ws2812-spi = { version = "0.5.0", optional = true } + +smart-leds-trait = { version = "0.3.0", optional = true } +smart-leds = { version = "0.4.0", optional = true } + +embedded-graphics = { version = "0.8.1", optional = true, features = ["fixed_point", "defmt"] } [build-dependencies] embuild = "0.32.0" diff --git a/src/TODO.md b/src/TODO.md index eabccb5..dbffb58 100644 --- a/src/TODO.md +++ b/src/TODO.md @@ -1,4 +1,4 @@ -[ ] cfg macros +[x] cfg macros [ ] warnings [ ] rgb crate [ ] Layer blending diff --git a/src/embedded_graphics_lib.rs b/src/embedded_graphics_lib.rs index b64ca43..dcac2d1 100644 --- a/src/embedded_graphics_lib.rs +++ b/src/embedded_graphics_lib.rs @@ -16,21 +16,7 @@ use crate::render::*; use crate::time::Periodically; use crate::task::Task; use crate::geometry::*; - -impl AsMilliwatts for T { - fn as_milliwatts(&self) -> u32 { - const RED_MW : u32 = 16 * 5; //< 16mA @ 5v = 80mW - const GREEN_MW : u32 = 11 * 5; //< 11mA @ 5v = 55mW - const BLUE_MW : u32 = 15 * 5; //< 15mA @ 5v = 75mW - const DARK_MW : u32 = 1 * 5; //< 1mA @ 5v = 5mW - - let red = (self.r() as u32 * RED_MW).wrapping_shr(8); - let green = (self.g() as u32 * GREEN_MW).wrapping_shr(8); - let blue = (self.b() as u32 * BLUE_MW).wrapping_shr(8); - - return red + green + blue + DARK_MW; - } -} +use crate::platform::DisplayInit; pub struct EmbeddedDisplay where @@ -124,3 +110,39 @@ impl Display for EmbeddedDisplay 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>; diff --git a/src/lib8.rs b/src/lib8.rs index 085bbe2..6746789 100644 --- a/src/lib8.rs +++ b/src/lib8.rs @@ -1,9 +1,8 @@ use palette::convert::FromColorUnclamped; use palette::encoding::srgb::Srgb; use palette::Hsv; -use embedded_graphics::pixelcolor::RgbColor; -use embedded_graphics::pixelcolor::PixelColor; -use embedded_graphics::pixelcolor::raw::RawU8; + +use crate::power::AsMilliwatts; #[derive(PartialEq, Debug, Copy, Clone)] pub struct RGB8 { @@ -22,31 +21,6 @@ impl RGB8 { } } -impl RgbColor for RGB8 { - fn r(&self) -> u8 { self.red } - fn g(&self) -> u8 { self.green } - fn b(&self) -> u8 { self.blue } - - const MAX_R: u8 = 255; - const MAX_G: u8 = 255; - const MAX_B: u8 = 255; - - const BLACK: Self = Self::new(0, 0, 0); - const WHITE: Self = Self::new(255, 255, 255); - - const RED: Self = Self::new(255, 0, 0); - const GREEN: Self = Self::new(0, 255, 0); - const BLUE: Self = Self::new(0, 0, 255); - - const YELLOW: Self = Self::new(255, 0, 0); - const CYAN: Self = Self::new(0, 255, 0); - const MAGENTA: Self = Self::new(0, 0, 255); -} - -impl PixelColor for RGB8 { - type Raw = RawU8; -} - impl FromColorUnclamped> for RGB8 { fn from_color_unclamped(hsv: Hsv) -> RGB8 { if hsv.saturation == 0 { @@ -70,3 +44,66 @@ impl FromColorUnclamped> for RGB8 { } } } + +impl AsMilliwatts for RGB8 { + fn as_milliwatts(&self) -> u32 { + const RED_MW : u32 = 16 * 5; //< 16mA @ 5v = 80mW + const GREEN_MW : u32 = 11 * 5; //< 11mA @ 5v = 55mW + const BLUE_MW : u32 = 15 * 5; //< 15mA @ 5v = 75mW + const DARK_MW : u32 = 1 * 5; //< 1mA @ 5v = 5mW + + let red = (self.red as u32 * RED_MW).wrapping_shr(8); + let green = (self.green as u32 * GREEN_MW).wrapping_shr(8); + let blue = (self.blue as u32 * BLUE_MW).wrapping_shr(8); + + return red + green + blue + DARK_MW; + } +} + +#[cfg(feature="embedded-graphics")] +mod embedded_graphics { + use embedded_graphics::pixelcolor::RgbColor; + use embedded_graphics::pixelcolor::PixelColor; + use embedded_graphics::pixelcolor::raw::RawU8; + + impl RgbColor for RGB8 { + fn r(&self) -> u8 { self.red } + fn g(&self) -> u8 { self.green } + fn b(&self) -> u8 { self.blue } + + const MAX_R: u8 = 255; + const MAX_G: u8 = 255; + const MAX_B: u8 = 255; + + const BLACK: Self = Self::new(0, 0, 0); + const WHITE: Self = Self::new(255, 255, 255); + + const RED: Self = Self::new(255, 0, 0); + const GREEN: Self = Self::new(0, 255, 0); + const BLUE: Self = Self::new(0, 0, 255); + + const YELLOW: Self = Self::new(255, 0, 0); + const CYAN: Self = Self::new(0, 255, 0); + const MAGENTA: Self = Self::new(0, 0, 255); + } + + impl PixelColor for RGB8 { + type Raw = RawU8; + } + + + impl AsMilliwatts for T { + fn as_milliwatts(&self) -> u32 { + const RED_MW : u32 = 16 * 5; //< 16mA @ 5v = 80mW + const GREEN_MW : u32 = 11 * 5; //< 11mA @ 5v = 55mW + const BLUE_MW : u32 = 15 * 5; //< 15mA @ 5v = 75mW + const DARK_MW : u32 = 1 * 5; //< 1mA @ 5v = 5mW + + let red = (self.r() as u32 * RED_MW).wrapping_shr(8); + let green = (self.g() as u32 * GREEN_MW).wrapping_shr(8); + let blue = (self.b() as u32 * BLUE_MW).wrapping_shr(8); + + return red + green + blue + DARK_MW; + } + } +} diff --git a/src/main.rs b/src/main.rs index 4579e17..7f22c8f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,24 +1,37 @@ use palette::Hsv; use palette::convert::IntoColorUnclamped; -use ws2812_esp32_rmt_driver::lib_smart_leds::Ws2812Esp32Rmt; -use ws2812_esp32_rmt_driver::lib_embedded_graphics::Ws2812DrawTarget; - mod power; mod lib8; mod render; mod task; mod time; mod geometry; -mod embedded_graphics_lib; -mod smart_leds_lib; mod platform; +#[cfg(feature="embedded-graphics")] +mod embedded_graphics_lib; + +#[cfg(feature="rmt")] +#[cfg(feature="embedded-graphics")] +use ws2812_esp32_rmt_driver::lib_embedded_graphics::PonderjarTarget; + +#[cfg(feature="smart-leds")] +mod smart_leds_lib; + +#[cfg(feature="rmt")] +#[cfg(feature="smart-leds")] +use ws2812_esp32_rmt_driver::lib_smart_leds::Ws2812Esp32Rmt; + +#[cfg(feature="spi")] +#[cfg(feature="smart-leds")] +use crate::smart_leds_lib::spi::SPIDisplay; + use crate::time::Periodically; use crate::geometry::{Coordinates, VirtualCoordinates}; use crate::render::{Shader, Surfaces, Surface, SimpleSurface}; use crate::task::Task; -use crate::platform::{DisplayInit, PonderjarTarget, SPIDisplay}; +use crate::platform::{DisplayInit}; struct IdleTask { frame: u8, @@ -70,8 +83,14 @@ fn main() { esp_idf_svc::log::EspLogger::initialize_default(); log::info!("Setting up display"); - //let mut display = SPIDisplay::new_display::(); - //let mut display = PonderjarTarget::new_display::(); + + #[cfg(feature="spi")] + let mut display = SPIDisplay::new_display::(); + + #[cfg(feature="embedded-graphics")] + let mut display = PonderjarTarget::new_display::(); + + #[cfg(feature="rmt")] let mut display = Ws2812Esp32Rmt::new_display::(); log::info!("Creating runner"); diff --git a/src/platform.rs b/src/platform.rs index c7c730f..1557334 100644 --- a/src/platform.rs +++ b/src/platform.rs @@ -1,102 +1,6 @@ -use esp_idf_svc::hal::{ - prelude::*, - gpio::AnyIOPin, - spi::{ - config::{Config, DriverConfig}, - Dma, - SpiBusDriver, - SpiDriver, - } -}; - -use ws2812_spi::Ws2812; -use ws2812_esp32_rmt_driver::lib_smart_leds::Ws2812Esp32Rmt; -use ws2812_esp32_rmt_driver::lib_embedded_graphics::{LedPixelShape, Ws2812DrawTarget}; - -use embedded_graphics::prelude::{Size, Point}; - -use crate::smart_leds_lib::SmartLedDisplay; -use crate::embedded_graphics_lib::EmbeddedDisplay; - use crate::render::{Surface, Display}; use crate::task::Task; pub trait DisplayInit { fn new_display() -> impl Display + Task; } - -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); - } -} - -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 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) - } -} - - -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>; diff --git a/src/smart_leds_lib.rs b/src/smart_leds_lib.rs index d032a81..7b88fab 100644 --- a/src/smart_leds_lib.rs +++ b/src/smart_leds_lib.rs @@ -3,11 +3,10 @@ use running_average::RealTimeRunningAverage; use crate::render::{Surface, SurfacePool, Display, Surfaces}; use crate::task::Task; -use crate::power; +use crate::power::{brightness_for_mw, AsMilliwatts}; use crate::time::Periodically; use crate::lib8::RGB8; use crate::geometry::*; -use crate::power::AsMilliwatts; use smart_leds::brightness; @@ -68,20 +67,20 @@ impl>, S: Surface> Display for SmartLedDisp fn end_frame(&mut self) { self.fps.insert(1); - let b = power::brightness_for_mw(self.total_mw, 255, self.max_mw); + 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); }); - self.target.write(brightness(self.pixbuf.iter().cloned(), b)); + let _ = self.target.write(brightness(self.pixbuf.iter().cloned(), b)); } fn render_frame(&mut self) { for x in 0..self.pixbuf.len() { - let virtCoords = VirtualCoordinates::new(x as u8, 0); + let virt_coords = VirtualCoordinates::new(x as u8, 0); let mut pixel = RGB8::new(0, 0, 0); for surface in self.surfaces.iter() { surface.with_shader(|shader| { - pixel = shader.draw(virtCoords.clone()); + pixel = shader.draw(virt_coords.clone()); }) } self.total_mw += pixel.as_milliwatts(); @@ -89,3 +88,74 @@ impl>, S: Surface> Display for SmartLedDisp }; } } + +#[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) + } + } +}