diff --git a/src/main.rs b/src/main.rs index 95d75d4..d591792 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,9 +39,17 @@ use ui::console_ui_loop; fn main() { let (tx, rx) = channel::(); thread::spawn(move || { - let mut strip = LEDStrip::new(3); + let mut strip = LEDStrip::new(strip::Config { + // I have 89 right now, but start off with 20 + num_lights: 20, + // Skip 14 lights + shift_lights: 14, + // Scaling factor (scale 0..255) + global_brightness_max: 20, + tick_time_ms: strip::DEFAULT_TICK_TIME_MS, + }); strip.strip_loop(&rx); - println!("Dead therad"); + panic!("Dead strip thread. Terminating"); }); console_ui_loop(&tx); diff --git a/src/pattern.rs b/src/pattern.rs index 1b3492f..02a4f3c 100644 --- a/src/pattern.rs +++ b/src/pattern.rs @@ -190,13 +190,6 @@ impl Pattern for Collide { } else { (self.left_color, self.right_color) }; - // let colors = match self.step { - // // If we are in the conjoined bounds region, these will be the same color - // // (self.conjoined_bounds.0)..=(self.conjoined_bounds.1) => (self.conjoined_color, self.conjoined_color), - // l..=r => (self.conjoined_color, self.conjoined_color), - // // If - // _ => (self.left_color, self.right_color), - // }; // Turn off the previous LED lights_buf[usize::from(self.previous_offset)] = color::BLACK; diff --git a/src/strip.rs b/src/strip.rs index 220e05a..88a2c17 100644 --- a/src/strip.rs +++ b/src/strip.rs @@ -1,53 +1,85 @@ use crate::color::{self, RGB}; use crate::pattern::{self, Pattern}; +use std::cmp; use std::ops::Add; +use std::process; use std::sync::mpsc::Receiver; use std::time::{Duration, Instant}; use ws2818_rgb_led_spi_driver::adapter_gen::WS28xxAdapter; use ws2818_rgb_led_spi_driver::adapter_spi::WS28xxSpiAdapter; -const MAX_NUM_LIGHTS: u16 = 32; -const TICK_TIME: u64 = 400; +/// Maximum number of lights allowed +const MAX_NUM_LIGHTS: u16 = 128; +/// Default time per tick +pub const DEFAULT_TICK_TIME_MS: u64 = 50; +/// Minimum time per tick before strip breaks +const MIN_TICK_TIME: u64 = 10; + +#[derive(Debug, Clone)] +pub struct Config { + /// Number of lights + pub num_lights: u16, + /// Number of lights to skip + pub shift_lights: u16, + /// Global brightness multiplier + pub global_brightness_max: u8, + /// Time per tick + pub tick_time_ms: u64, +} pub enum Message { ClearLights, ChangePattern(Box), SetNumLights(u16), + SetTickTime(u64), + Quit, } #[allow(clippy::module_name_repetitions)] pub struct LEDStrip { pub adapter: Box, - pub num_lights: u16, + pub config: Config, pub pattern: Box, pub lights_buf: Vec, } impl LEDStrip { - pub fn new(num_lights: u16) -> Self { + pub fn new(config: Config) -> Self { let adapter = Box::new( WS28xxSpiAdapter::new("/dev/spidev0.0").expect("Cannot locate device /dev/spidev0.0!"), ); - // let pattern = Pattern::default(); let pattern = Box::new(pattern::Solid::new(color::BLACK)); + let num_lights = config.num_lights; let lights_buf = vec![color::BLACK; num_lights.into()]; let mut ret = Self { adapter, pattern, lights_buf, - num_lights, + config, }; ret.set_num_lights(num_lights); ret } fn write_buf(&mut self) { - let data = self - .lights_buf - .as_slice() + let data = vec![color::BLACK] .iter() - .take(self.num_lights.into()) + .cycle() + .take(self.config.shift_lights.into()) + .chain( + self.lights_buf + .as_slice() + .iter() + .take(self.config.num_lights.into()), + ) .map(|c| c.to_tuple()) + .map(|(r, g, b)| { + ( + cmp::min(r, self.config.global_brightness_max), + cmp::min(g, self.config.global_brightness_max), + cmp::min(b, self.config.global_brightness_max), + ) + }) .collect::>(); self.adapter .write_rgb(data.as_slice()) @@ -63,19 +95,23 @@ impl LEDStrip { return; } if self.pattern.init(&mut self.lights_buf, num_lights).is_ok() { - self.num_lights = num_lights; + self.config.num_lights = num_lights; } } pub fn strip_loop(&mut self, rx: &Receiver) { + let mut exit = false; loop { - let target_time = Instant::now().add(Duration::from_millis(TICK_TIME)); + let target_time = Instant::now().add(Duration::from_millis(self.config.tick_time_ms)); if let Ok(message) = rx.try_recv() { match message { Message::ClearLights => { let mut pat = Box::new(pattern::Solid::new(color::BLACK)); - if pat.init(&mut self.lights_buf, self.num_lights).is_ok() { + if pat + .init(&mut self.lights_buf, self.config.num_lights) + .is_ok() + { self.pattern = pat; } else { println!("Clearing light strip: {:?}", pat); @@ -83,7 +119,10 @@ impl LEDStrip { } Message::ChangePattern(pat) => { let mut pat = pat; - if pat.init(&mut self.lights_buf, self.num_lights).is_ok() { + if pat + .init(&mut self.lights_buf, self.config.num_lights) + .is_ok() + { self.pattern = pat; } else { println!("Error with pattern: {:?}", pat); @@ -92,6 +131,24 @@ impl LEDStrip { Message::SetNumLights(num_lights) => { self.set_num_lights(num_lights); } + Message::SetTickTime(tick_time_ms) => { + if tick_time_ms < MIN_TICK_TIME { + println!("Error with tick time: {}", tick_time_ms); + } + self.config.tick_time_ms = tick_time_ms; + } + Message::Quit => { + exit = true; + let mut pat = pattern::Solid::new(color::BLACK); + if pat + .init(&mut self.lights_buf, self.config.num_lights) + .is_ok() + { + self.pattern = Box::new(pat); + } else { + println!("Could not construct clear pattern"); + } + } } } @@ -99,12 +156,15 @@ impl LEDStrip { self.write_buf(); } - // let mut num_iter = 0; + if exit { + println!("Exiting as requested"); + process::exit(0); + } + loop { if Instant::now() >= target_time { break; } - // num_iter += 1; } } } diff --git a/src/ui.rs b/src/ui.rs index e723414..bac603b 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -73,14 +73,19 @@ fn parse_cmd(tx: &Sender, s: &str) -> Result<(), String> { ["x"] => tx .send(strip::Message::ClearLights) .map_err(|e| e.to_string()), - ["q"] => Ok(()), + ["q"] => tx.send(strip::Message::Quit).map_err(|e| e.to_string()), ["s", n] => tx .send(strip::Message::SetNumLights( n.parse::() .map_err(|_| String::from("Could not parse light count"))?, )) .map_err(|e| e.to_string()), - _ => Err(String::from("Unknown command. Available commands: solidColor movingRainbow Bouncing Fade clear(X) Setnumlights")), + ["t", n] => tx.send(strip::Message::SetTickTime( + n.parse::() + .map_err(|_| String::from("Could not parse light count"))?, + )) + .map_err(|e| e.to_string()), + _ => Err(String::from("Unknown command. Available commands: solidColor movingRainbow Bouncing Fade clear(X) Setnumlights setTicktime")), } }