142 lines
3.5 KiB
Rust
142 lines
3.5 KiB
Rust
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<MCUResponse> {
|
|
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()
|
|
);
|
|
}
|
|
}
|