use std::{ io, thread, time::{Duration, Instant}, }; use anyhow::{anyhow, Result}; use palette::{Hsl, IntoColor, Srgb}; use serialport::SerialPort; mod fixtures; const FPS: u32 = 50; enum MCUResponse { Sync, Ack, Info { num_pkts: u32 }, } fn poll_response(ser: &mut dyn SerialPort) -> Result { let mut read_buffer = vec![0u8; 32]; let bytes_read; loop { match ser.read(read_buffer.as_mut_slice()) { Ok(t) => { bytes_read = t; break; } Err(ref e) if e.kind() == io::ErrorKind::TimedOut => continue, Err(e) => Err(e), }? } let response = std::str::from_utf8(&read_buffer[..bytes_read])?; match response.trim() { "Sync." => Ok(MCUResponse::Sync), "Ack." => Ok(MCUResponse::Ack), s if s.starts_with("Info") => Ok(MCUResponse::Info { num_pkts: 69 }), s => Err(anyhow!("Unknown response: \"{}\"", s)), } } fn main() -> Result<()> { let frame_time = Duration::from_secs_f64(1.0 / FPS as f64); let mut dmx_buffer = [0u8; 512]; let mut movingheads = [ fixtures::MovingHead14CH::new(1), fixtures::MovingHead14CH::new(15), fixtures::MovingHead14CH::new(29), fixtures::MovingHead14CH::new(43), ]; for movinghead in movingheads.iter_mut() { movinghead.dimmer = 134; } let mut ser = serialport::new("/dev/ttyUSB0", 500_000) .timeout(Duration::from_millis(10)) .open()?; println!("open"); // wait for initial sync loop { match poll_response(&mut *ser) { Ok(MCUResponse::Sync) => break, _ => continue, } } println!("sync"); let mut t = 0; 'main: loop { let loop_start = Instant::now(); let dist = FPS as f32 / movingheads.len() as f32; for (i, movinghead) in movingheads.iter_mut().enumerate() { let hsl_color = Hsl::new( 360.0 * ((t % FPS) as f32 / FPS as f32) + i as f32 * dist, 1.0, 0.5, ); movinghead.rgb = hsl_color.into_color(); movinghead.render(&mut dmx_buffer); } // write DMX data let write_result = ser.write(&dmx_buffer); if write_result.is_err() { loop { match poll_response(&mut *ser) { Ok(MCUResponse::Sync) => { continue 'main; } _ => continue, } } } // get response loop { match poll_response(&mut *ser) { Ok(MCUResponse::Ack) => break, Ok(MCUResponse::Info { num_pkts }) => { println!("Info: {}", num_pkts); continue; } Err(e) => { println!("Error! {:?}", e); continue; } Ok(MCUResponse::Sync) => { continue 'main; } } } t += 1; let loop_time = loop_start.elapsed(); if loop_time < frame_time { thread::sleep(frame_time - loop_time); } else { println!("loop took too long!"); } println!( "loop time: {}ms busy, {}ms total", loop_time.as_millis(), loop_start.elapsed().as_millis() ); } }