From 7631bfb669f75eb02458543cf68c502125fa96dd Mon Sep 17 00:00:00 2001 From: Thomas Kolb Date: Mon, 22 Feb 2021 23:24:32 +0100 Subject: [PATCH] Started implementing the UDP protocol --- src/config.rs | 3 ++ src/main.rs | 25 +++++++++- src/udpproto.rs | 119 ++++++++++++++++++++++++++++++++++++++++++++++ src/userscript.rs | 16 ++++--- 4 files changed, 156 insertions(+), 7 deletions(-) create mode 100644 src/udpproto.rs diff --git a/src/config.rs b/src/config.rs index be9da5c..693e997 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,3 +10,6 @@ pub const NUM_STRIPS: usize = 8; pub const NUM_LEDS_PER_STRIP: usize = 16; pub const NUM_LEDS_TOTAL: usize = NUM_STRIPS * NUM_LEDS_PER_STRIP; + +// network configuration +pub const UDP_SERVER_ADDR: &str = "192.168.23.118:2703"; diff --git a/src/main.rs b/src/main.rs index 972fac7..1e1ccee 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,9 +8,11 @@ use byteorder::{NativeEndian, ReadBytesExt}; mod signal_processing; mod config; mod userscript; +mod udpproto; use crate::signal_processing::SignalProcessing; use crate::userscript::UserScript; +use crate::udpproto::UdpProto; use std::rc::Rc; use std::cell::RefCell; @@ -19,6 +21,15 @@ fn main() { let mut stdin = std::io::stdin(); + // set up the UDP protocol + let mut udpproto = match UdpProto::new(config::UDP_SERVER_ADDR) { + Ok(u) => u, + Err(e) => { + println!("Error during UDP client setup:\n{}", e); + exit(1); + } + }; + // set up signal processing println!("Initializing signal processing..."); @@ -30,7 +41,7 @@ fn main() println!("Loading user script..."); - let script = match UserScript::new(sigproc.clone(), "test.lua") { + let mut script = match UserScript::new(sigproc.clone(), "test.lua") { Ok(script) => script, Err(e) => { println!("=== Lua Error ===\n{}\n====> Terminating.", e); @@ -98,6 +109,18 @@ fn main() } }; + // FIXME: only send with 60 FPS! + + for i in 0..script.colorlists[0].len() { + udpproto.set_color((i / config::NUM_LEDS_PER_STRIP) as u8, + (i % config::NUM_LEDS_PER_STRIP) as u8, + (script.colorlists[0][i] * 255.0) as u8, + (script.colorlists[1][i] * 255.0) as u8, + (script.colorlists[2][i] * 255.0) as u8, + (script.colorlists[3][i] * 255.0) as u8).unwrap(); + } + + udpproto.commit().unwrap(); } } diff --git a/src/udpproto.rs b/src/udpproto.rs new file mode 100644 index 0000000..f69f17d --- /dev/null +++ b/src/udpproto.rs @@ -0,0 +1,119 @@ +// vim: noet + +use std::net::UdpSocket; +use std::net::SocketAddrV4; +use std::net::Ipv4Addr; + +const MAX_PACKET_LEN: usize = 1470; + +struct Command +{ + cmd: u8, + strip: u8, + led: u8, + data: [u8; 4], +} + +pub struct UdpProto +{ + socket: UdpSocket, + packet: [u8; MAX_PACKET_LEN], + packet_offset: usize, +} + +impl UdpProto +{ + pub fn new(target_address: &str) -> std::io::Result + { + let u = UdpProto { + socket: UdpSocket::bind(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0))?, + packet: [0u8; MAX_PACKET_LEN], + packet_offset: 0, + }; + + u.socket.connect(target_address)?; + + Ok(u) + } + + fn send_packet(&mut self) -> std::io::Result<()> + { + if self.packet_offset == 0 { + // nothing to do + return Ok( () ); + } + + self.packet_offset = 0; + + self.socket.send(&self.packet[0..self.packet_offset])?; + + Ok( () ) + } + + fn add_command(&mut self, cmd: u8, strip: u8, led: u8, data: &[u8; 4]) -> std::io::Result<()> + { + // put the command into the packet buffer + self.packet[self.packet_offset + 0] = cmd; + self.packet[self.packet_offset + 1] = strip; + self.packet[self.packet_offset + 2] = led; + + for i in 0 .. data.len() { + self.packet[self.packet_offset + i] = data[i]; + } + + self.packet_offset += 7; + + if self.packet_offset >= MAX_PACKET_LEN { + self.send_packet()?; + } + + Ok( () ) + } + + pub fn set_color(&mut self, strip: u8, led: u8, + r: u8, g: u8, b: u8, w: u8) -> std::io::Result<()> + { + let data = [r,g,b,w]; + + self.add_command(0x00, strip, led, &data)?; + + Ok( () ) + } + + pub fn fade_color(&mut self, strip: u8, led: u8, + r: u8, g: u8, b: u8, w: u8) -> std::io::Result<()> + { + let data = [r,g,b,w]; + + self.add_command(0x01, strip, led, &data)?; + + Ok( () ) + } + + pub fn add_color(&mut self, strip: u8, led: u8, + r: u8, g: u8, b: u8, w: u8) -> std::io::Result<()> + { + let data = [r,g,b,w]; + + self.add_command(0x02, strip, led, &data)?; + + Ok( () ) + } + + pub fn set_fadestep(&mut self, fadestep: u8) -> std::io::Result<()> + { + let data = [fadestep, 0, 0, 0]; + + self.add_command(0x03, 0, 0, &data)?; + + Ok( () ) + } + + pub fn commit(&mut self) -> std::io::Result<()> + { + // add the END_OF_UPDATE command + self.add_command(0xFE, 0, 0, &[0u8; 4])?; + + self.send_packet() + } +} diff --git a/src/userscript.rs b/src/userscript.rs index 9b3d4f8..e1ea537 100644 --- a/src/userscript.rs +++ b/src/userscript.rs @@ -33,6 +33,8 @@ use std::cell::RefCell; pub struct UserScript { lua_state: Lua, + + pub colorlists: [ [f32; config::NUM_LEDS_TOTAL]; 4], } impl UserScript @@ -41,6 +43,7 @@ impl UserScript { let s = UserScript { lua_state: Lua::new(), + colorlists: [ [0f32; config::NUM_LEDS_TOTAL]; 4], }; // provide some configuration constants to Lua via a table @@ -74,7 +77,7 @@ impl UserScript Ok(()) } - pub fn periodic(&self) -> std::result::Result<(), mlua::Error> + pub fn periodic(&mut self) -> std::result::Result<(), mlua::Error> { // find the init functions let lua_periodic_func: mlua::Function = self.lua_state.globals().get("periodic")?; @@ -89,8 +92,7 @@ impl UserScript } // convert the Lua Tables to normal vectors - let mut colorlists = Vec::>::new(); - + let mut i = 0; for rval in rvals { let table = mlua::Table::from_lua(rval, &self.lua_state)?; @@ -103,10 +105,12 @@ impl UserScript return Err(Error::RuntimeError("Number of color values returned from 'periodic' must match number of LEDs given in 'init'.".to_string())); } - colorlists.push(v); - } + for j in 0 .. self.colorlists[i].len() { + self.colorlists[i][j] = v[j]; + } - println!("{:?}", colorlists[0][0]); + i += 1; + } Ok(()) }