diff --git a/Cargo.lock b/Cargo.lock index b1c6fbd..78621fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -344,6 +344,7 @@ dependencies = [ [[package]] name = "derive_stringset" version = "0.1.0" +source = "git+https://github.com/vertigo-designs/miniconf.git?branch=rs/cleanup#396a759356ae977d718ef6d30cfa6481f0d40b2f" dependencies = [ "proc-macro2", "quote", @@ -581,6 +582,7 @@ dependencies = [ [[package]] name = "miniconf" version = "0.1.0" +source = "git+https://github.com/vertigo-designs/miniconf.git?branch=rs/cleanup#396a759356ae977d718ef6d30cfa6481f0d40b2f" dependencies = [ "derive_stringset", "serde", @@ -909,7 +911,6 @@ dependencies = [ "panic-semihosting", "paste", "serde", - "serde-json-core 0.1.0", "smoltcp", "stm32h7xx-hal", ] diff --git a/Cargo.toml b/Cargo.toml index bdc73e4..41ca5db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,6 @@ panic-semihosting = { version = "0.5", optional = true } panic-halt = "0.2" serde = { version = "1.0", features = ["derive"], default-features = false } heapless = { version = "0.5", features = ["serde"] } -serde-json-core = "0.1" cortex-m-rtic = "0.5.5" embedded-hal = "0.2.4" nb = "1.0.0" @@ -48,9 +47,8 @@ ad9959 = { path = "ad9959" } minimq = { git = "https://github.com/quartiq/minimq.git" } [dependencies.miniconf] -# git = "https://github.com/vertigo-designs/miniconf.git" -# branch = "james/derive_stringset" -path = "../../vertigo-designs/miniconf" +git = "https://github.com/vertigo-designs/miniconf.git" +branch = "rs/cleanup" [dependencies.mcp23017] git = "https://github.com/mrd0ll4r/mcp23017.git" diff --git a/dsp/Cargo.toml b/dsp/Cargo.toml index 661bf78..55e150e 100644 --- a/dsp/Cargo.toml +++ b/dsp/Cargo.toml @@ -10,9 +10,8 @@ serde = { version = "1.0", features = ["derive"], default-features = false } serde-json-core = "0.1" [dependencies.miniconf] -# git = "https://github.com/vertigo-designs/miniconf.git" -# branch = "james/derive_stringset" -path = "../../../vertigo-designs/miniconf" +git = "https://github.com/vertigo-designs/miniconf.git" +branch = "rs/cleanup" [dev-dependencies] criterion = "0.3" diff --git a/dsp/src/iir.rs b/dsp/src/iir.rs index dbec8d8..398a900 100644 --- a/dsp/src/iir.rs +++ b/dsp/src/iir.rs @@ -3,6 +3,8 @@ use serde::{Deserialize, Serialize}; use super::{abs, copysign, macc, max, min}; use core::f32; +use miniconf::StringSet; + /// IIR state and coefficients type. /// /// To represent the IIR state (input and output memory) during the filter update @@ -38,7 +40,7 @@ pub type IIRState = [f32; 5]; /// Therefore it can trivially implement bump-less transfer. /// * Cascading multiple IIR filters allows stable and robust /// implementation of transfer functions beyond bequadratic terms. -#[derive(Copy, Clone, Deserialize, Serialize)] +#[derive(Copy, Clone, Debug, Default, Deserialize, Serialize, StringSet)] pub struct IIR { pub ba: IIRState, pub y_offset: f32, diff --git a/src/bin/dual-iir.rs b/src/bin/dual-iir.rs index f7bb97b..1ec7d85 100644 --- a/src/bin/dual-iir.rs +++ b/src/bin/dual-iir.rs @@ -3,29 +3,36 @@ #![no_main] #![cfg_attr(feature = "nightly", feature(core_intrinsics))] -use miniconf::StringSet; - use stm32h7xx_hal as hal; use rtic::cyccnt::{Instant, U32Ext}; use stabilizer::hardware; +use miniconf::StringSet; +use serde::Deserialize; + use dsp::iir; use hardware::{Adc0Input, Adc1Input, Dac0Output, Dac1Output, AFE0, AFE1, MqttAction}; const SCALE: f32 = ((1 << 15) - 1) as f32; -const TCP_RX_BUFFER_SIZE: usize = 8192; -const TCP_TX_BUFFER_SIZE: usize = 8192; - // The number of cascaded IIR biquads per channel. Select 1 or 2! const IIR_CASCADE_LENGTH: usize = 1; -#[derive(Default, StringSet)] -struct Settings { - pub afe_gain: [hardware::AfeGain; 2], - //iir: [[iir::IIR; IIR_CASCADE_LENGTH]; 2], +#[derive(Debug, Deserialize, StringSet)] +pub struct Settings { + test: u32, + iir: [[iir::IIR; IIR_CASCADE_LENGTH]; 2], +} + +impl Settings { + pub fn new() -> Self { + Self { + test: 5, + iir: [[iir::IIR::default(); IIR_CASCADE_LENGTH]; 2], + } + } } #[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)] @@ -58,7 +65,7 @@ const APP: () = { stabilizer.adc_dac_timer.start(); init::LateResources { - mqtt_interface: hardware::MqttInterface::new(stabilizer.net.stack, Settings::default()), + mqtt_interface: hardware::MqttInterface::new(stabilizer.net.stack, Settings::new()), afes: stabilizer.afes, adcs: stabilizer.adcs, dacs: stabilizer.dacs, @@ -135,11 +142,9 @@ const APP: () = { } #[task(priority = 1, resources=[mqtt_interface, afes, iir_ch])] - fn settings_update(c: settings_update::Context) { + fn settings_update(mut c: settings_update::Context) { let settings = c.resources.mqtt_interface.settings.borrow(); - //c.resources.iir_ch.lock(|iir_ch| *iir_ch = settings.iir); - c.resources.afes.0.set_gain(settings.afe_gain[0]); - c.resources.afes.1.set_gain(settings.afe_gain[1]); + c.resources.iir_ch.lock(|iir| *iir = settings.iir); } #[task(binds = ETH, priority = 1)] diff --git a/src/bin/lockin.rs b/src/bin/lockin.rs index 304c35e..8793949 100644 --- a/src/bin/lockin.rs +++ b/src/bin/lockin.rs @@ -5,15 +5,10 @@ use stm32h7xx_hal as hal; -#[macro_use] -extern crate log; - use rtic::cyccnt::{Instant, U32Ext}; -use heapless::{consts::*, String}; - use stabilizer::{ - hardware, server, ADC_SAMPLE_TICKS_LOG2, SAMPLE_BUFFER_SIZE_LOG2, + hardware, ADC_SAMPLE_TICKS_LOG2, SAMPLE_BUFFER_SIZE_LOG2, }; use dsp::{iir, iir_int, lockin::Lockin, reciprocal_pll::TimestampHandler}; @@ -23,9 +18,6 @@ use hardware::{ const SCALE: f32 = ((1 << 15) - 1) as f32; -const TCP_RX_BUFFER_SIZE: usize = 8192; -const TCP_TX_BUFFER_SIZE: usize = 8192; - // The number of cascaded IIR biquads per channel. Select 1 or 2! const IIR_CASCADE_LENGTH: usize = 1; @@ -35,7 +27,7 @@ const APP: () = { afes: (AFE0, AFE1), adcs: (Adc0Input, Adc1Input), dacs: (Dac0Output, Dac1Output), - net_interface: hardware::Ethernet, + stack: hardware::NetworkStack, // Format: iir_state[ch][cascade-no][coeff] #[init([[[0.; 5]; IIR_CASCADE_LENGTH]; 2])] @@ -80,7 +72,7 @@ const APP: () = { afes: stabilizer.afes, adcs: stabilizer.adcs, dacs: stabilizer.dacs, - net_interface: stabilizer.net.interface, + stack: stabilizer.net.stack, timestamper: stabilizer.timestamper, pll, @@ -165,26 +157,8 @@ const APP: () = { } } - #[idle(resources=[net_interface, iir_state, iir_ch, afes])] - fn idle(mut c: idle::Context) -> ! { - let mut socket_set_entries: [_; 8] = Default::default(); - let mut sockets = - smoltcp::socket::SocketSet::new(&mut socket_set_entries[..]); - - let mut rx_storage = [0; TCP_RX_BUFFER_SIZE]; - let mut tx_storage = [0; TCP_TX_BUFFER_SIZE]; - let tcp_handle = { - let tcp_rx_buffer = - smoltcp::socket::TcpSocketBuffer::new(&mut rx_storage[..]); - let tcp_tx_buffer = - smoltcp::socket::TcpSocketBuffer::new(&mut tx_storage[..]); - let tcp_socket = - smoltcp::socket::TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer); - sockets.add(tcp_socket) - }; - - let mut server = server::Server::new(); - + #[idle(resources=[stack, iir_state, iir_ch, afes])] + fn idle(c: idle::Context) -> ! { let mut time = 0u32; let mut next_ms = Instant::now(); @@ -199,118 +173,7 @@ const APP: () = { time += 1; } - { - let socket = - &mut *sockets.get::(tcp_handle); - if socket.state() == smoltcp::socket::TcpState::CloseWait { - socket.close(); - } else if !(socket.is_open() || socket.is_listening()) { - socket - .listen(1235) - .unwrap_or_else(|e| warn!("TCP listen error: {:?}", e)); - } else { - server.poll(socket, |req| { - info!("Got request: {:?}", req); - stabilizer::route_request!(req, - readable_attributes: [ - "stabilizer/iir/state": (|| { - let state = c.resources.iir_state.lock(|iir_state| - server::Status { - t: time, - x0: iir_state[0][0][0], - y0: iir_state[0][0][2], - x1: iir_state[1][0][0], - y1: iir_state[1][0][2], - }); - - Ok::(state) - }), - // "_b" means cascades 2nd IIR - "stabilizer/iir_b/state": (|| { let state = c.resources.iir_state.lock(|iir_state| - server::Status { - t: time, - x0: iir_state[0][IIR_CASCADE_LENGTH-1][0], - y0: iir_state[0][IIR_CASCADE_LENGTH-1][2], - x1: iir_state[1][IIR_CASCADE_LENGTH-1][0], - y1: iir_state[1][IIR_CASCADE_LENGTH-1][2], - }); - - Ok::(state) - }), - "stabilizer/afe0/gain": (|| c.resources.afes.0.get_gain()), - "stabilizer/afe1/gain": (|| c.resources.afes.1.get_gain()) - ], - - modifiable_attributes: [ - "stabilizer/iir0/state": server::IirRequest, (|req: server::IirRequest| { - c.resources.iir_ch.lock(|iir_ch| { - if req.channel > 1 { - return Err(()); - } - - iir_ch[req.channel as usize][0] = req.iir; - - Ok::(req) - }) - }), - "stabilizer/iir1/state": server::IirRequest, (|req: server::IirRequest| { - c.resources.iir_ch.lock(|iir_ch| { - if req.channel > 1 { - return Err(()); - } - - iir_ch[req.channel as usize][0] = req.iir; - - Ok::(req) - }) - }), - "stabilizer/iir_b0/state": server::IirRequest, (|req: server::IirRequest| { - c.resources.iir_ch.lock(|iir_ch| { - if req.channel > 1 { - return Err(()); - } - - iir_ch[req.channel as usize][IIR_CASCADE_LENGTH-1] = req.iir; - - Ok::(req) - }) - }), - "stabilizer/iir_b1/state": server::IirRequest,(|req: server::IirRequest| { - c.resources.iir_ch.lock(|iir_ch| { - if req.channel > 1 { - return Err(()); - } - - iir_ch[req.channel as usize][IIR_CASCADE_LENGTH-1] = req.iir; - - Ok::(req) - }) - }), - "stabilizer/afe0/gain": hardware::AfeGain, (|gain| { - c.resources.afes.0.set_gain(gain); - Ok::<(), ()>(()) - }), - "stabilizer/afe1/gain": hardware::AfeGain, (|gain| { - c.resources.afes.1.set_gain(gain); - Ok::<(), ()>(()) - }) - ] - ) - }); - } - } - - let sleep = match c.resources.net_interface.poll( - &mut sockets, - smoltcp::time::Instant::from_millis(time as i64), - ) { - Ok(changed) => !changed, - Err(smoltcp::Error::Unrecognized) => true, - Err(e) => { - info!("iface poll error: {:?}", e); - true - } - }; + let sleep = c.resources.stack.update(time); if sleep { cortex_m::asm::wfi(); diff --git a/src/hardware/mqtt_interface.rs b/src/hardware/mqtt_interface.rs index 6ab5745..3e0f44f 100644 --- a/src/hardware/mqtt_interface.rs +++ b/src/hardware/mqtt_interface.rs @@ -50,7 +50,7 @@ where match self.client.borrow_mut().poll(|client, topic, message, properties| { let mut split = topic.split('/'); // TODO: Verify topic ID against our ID. - let id = split.next().unwrap(); + let _id = split.next().unwrap(); // Process the command let command = split.next().unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 254e626..067c5c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,6 @@ extern crate log; pub mod hardware; -pub mod server; // The number of ticks in the ADC sampling timer. The timer runs at 100MHz, so the step size is // equal to 10ns per tick. diff --git a/src/server.rs b/src/server.rs deleted file mode 100644 index 5e6950d..0000000 --- a/src/server.rs +++ /dev/null @@ -1,278 +0,0 @@ -use core::fmt::Write; -use heapless::{consts::*, String, Vec}; -use serde::{Deserialize, Serialize}; -use serde_json_core::{de::from_slice, ser::to_string}; -use smoltcp as net; - -use dsp::iir; - -#[macro_export] -macro_rules! route_request { - ($request:ident, - readable_attributes: [$($read_attribute:tt: $getter:tt),*], - modifiable_attributes: [$($write_attribute:tt: $TYPE:ty, $setter:tt),*]) => { - match $request.req { - server::AccessRequest::Read => { - match $request.attribute { - $( - $read_attribute => { - #[allow(clippy::redundant_closure_call)] - let value = match $getter() { - Ok(data) => data, - Err(_) => return server::Response::error($request.attribute, - "Failed to read attribute"), - }; - - let encoded_data: String = match serde_json_core::to_string(&value) { - Ok(data) => data, - Err(_) => return server::Response::error($request.attribute, - "Failed to encode attribute value"), - }; - - server::Response::success($request.attribute, &encoded_data) - }, - )* - _ => server::Response::error($request.attribute, "Unknown attribute") - } - }, - server::AccessRequest::Write => { - match $request.attribute { - $( - $write_attribute => { - let new_value = match serde_json_core::from_str::<$TYPE>(&$request.value) { - Ok(data) => data, - Err(_) => return server::Response::error($request.attribute, - "Failed to decode value"), - }; - - #[allow(clippy::redundant_closure_call)] - match $setter(new_value) { - Ok(_) => server::Response::success($request.attribute, &$request.value), - Err(_) => server::Response::error($request.attribute, - "Failed to set attribute"), - } - } - )* - _ => server::Response::error($request.attribute, "Unknown attribute") - } - } - } - } -} - -#[derive(Deserialize, Serialize, Debug)] -pub enum AccessRequest { - Read, - Write, -} - -#[derive(Deserialize, Serialize, Debug)] -pub struct Request<'a> { - pub req: AccessRequest, - pub attribute: &'a str, - pub value: String, -} - -#[derive(Serialize, Deserialize)] -pub struct IirRequest { - pub channel: u8, - pub iir: iir::IIR, -} - -#[derive(Serialize)] -pub struct Response { - code: i32, - attribute: String, - value: String, -} - -impl<'a> Request<'a> { - pub fn restore_value(&mut self) { - let mut new_value: String = String::new(); - for byte in self.value.as_str().chars() { - if byte == '\'' { - new_value.push('"').unwrap(); - } else { - new_value.push(byte).unwrap(); - } - } - - self.value = new_value; - } -} - -impl Response { - /// Remove all double quotation marks from the `value` field of a response. - fn sanitize_value(&mut self) { - let mut new_value: String = String::new(); - for byte in self.value.as_str().chars() { - if byte == '"' { - new_value.push('\'').unwrap(); - } else { - new_value.push(byte).unwrap(); - } - } - - self.value = new_value; - } - - /// Remove all double quotation marks from the `value` field of a response and wrap it in single - /// quotes. - fn wrap_and_sanitize_value(&mut self) { - let mut new_value: String = String::new(); - new_value.push('\'').unwrap(); - for byte in self.value.as_str().chars() { - if byte == '"' { - new_value.push('\'').unwrap(); - } else { - new_value.push(byte).unwrap(); - } - } - new_value.push('\'').unwrap(); - - self.value = new_value; - } - - /// Construct a successful reply. - /// - /// Note: `value` will be sanitized to convert all single quotes to double quotes. - /// - /// Args: - /// * `attrbute` - The attribute of the success. - /// * `value` - The value of the attribute. - pub fn success(attribute: &str, value: &str) -> Self { - let mut res = Self { - code: 200, - attribute: String::from(attribute), - value: String::from(value), - }; - res.sanitize_value(); - res - } - - /// Construct an error reply. - /// - /// Note: `message` will be sanitized to convert all single quotes to double quotes. - /// - /// Args: - /// * `attrbute` - The attribute of the success. - /// * `message` - The message denoting the error. - pub fn error(attribute: &str, message: &str) -> Self { - let mut res = Self { - code: 400, - attribute: String::from(attribute), - value: String::from(message), - }; - res.wrap_and_sanitize_value(); - res - } - - /// Construct a custom reply. - /// - /// Note: `message` will be sanitized to convert all single quotes to double quotes. - /// - /// Args: - /// * `attrbute` - The attribute of the success. - /// * `message` - The message denoting the status. - pub fn custom(code: i32, message: &str) -> Self { - let mut res = Self { - code, - attribute: String::from(""), - value: String::from(message), - }; - res.wrap_and_sanitize_value(); - res - } -} - -#[derive(Serialize)] -pub struct Status { - pub t: u32, - pub x0: f32, - pub y0: f32, - pub x1: f32, - pub y1: f32, -} - -pub fn json_reply(socket: &mut net::socket::TcpSocket, msg: &T) { - let mut u: String = to_string(msg).unwrap(); - u.push('\n').unwrap(); - socket.write_str(&u).unwrap(); -} - -pub struct Server { - data: Vec, - discard: bool, -} - -impl Server { - /// Construct a new server object for managing requests. - pub fn new() -> Self { - Self { - data: Vec::new(), - discard: false, - } - } - - /// Poll the server for potential data updates. - /// - /// Args: - /// * `socket` - The socket to check contents from. - /// * `f` - A closure that can be called if a request has been received on the server. - pub fn poll(&mut self, socket: &mut net::socket::TcpSocket, mut f: F) - where - F: FnMut(&Request) -> Response, - { - while socket.can_recv() { - let found = socket - .recv(|buf| { - let (len, found) = - match buf.iter().position(|&c| c as char == '\n') { - Some(end) => (end + 1, true), - None => (buf.len(), false), - }; - if self.data.len() + len >= self.data.capacity() { - self.discard = true; - self.data.clear(); - } else if !self.discard && len > 0 { - self.data.extend_from_slice(&buf[..len]).unwrap(); - } - (len, found) - }) - .unwrap(); - - if found { - if self.discard { - self.discard = false; - json_reply( - socket, - &Response::custom(520, "command buffer overflow"), - ); - } else { - let r = from_slice::( - &self.data[..self.data.len() - 1], - ); - match r { - Ok(mut res) => { - // Note that serde_json_core doesn't escape quotations within a string. - // To account for this, we manually translate all single quotes to - // double quotes. This occurs because we doubly-serialize this field in - // some cases. - res.restore_value(); - let response = f(&res); - json_reply(socket, &response); - } - Err(err) => { - warn!("parse error {:?}", err); - json_reply( - socket, - &Response::custom(550, "parse error"), - ); - } - } - } - self.data.clear(); - } - } - } -}