diff --git a/Cargo.lock b/Cargo.lock index 9c42b6a..f77e0bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -710,7 +710,7 @@ dependencies = [ [[package]] name = "smoltcp-nal" version = "0.1.0" -source = "git+https://github.com/quartiq/smoltcp-nal.git?rev=56519012d7#56519012d7c6a382eaa0d7ecb26f2701771d9ce8" +source = "git+https://github.com/quartiq/smoltcp-nal.git?rev=7572183#757218316620e074405bc38ff5502ef17a8a3499" dependencies = [ "embedded-nal", "heapless 0.6.1", diff --git a/Cargo.toml b/Cargo.toml index 8d7ae92..0c4ab0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,7 +60,7 @@ rev = "314fa5587d" [dependencies.smoltcp-nal] git = "https://github.com/quartiq/smoltcp-nal.git" -rev = "56519012d7" +rev = "7572183" [patch.crates-io.minimq] git = "https://github.com/quartiq/minimq.git" diff --git a/src/bin/dual-iir.rs b/src/bin/dual-iir.rs index d51ca56..8f07cf1 100644 --- a/src/bin/dual-iir.rs +++ b/src/bin/dual-iir.rs @@ -2,17 +2,19 @@ #![no_std] #![no_main] -use stabilizer::hardware; +use stabilizer::{hardware, net}; -use miniconf::{minimq, Miniconf, MqttInterface}; +use miniconf::Miniconf; use serde::Deserialize; use dsp::iir; use hardware::{ - Adc0Input, Adc1Input, AfeGain, CycleCounter, Dac0Output, Dac1Output, - DigitalInput1, InputPin, NetworkStack, AFE0, AFE1, + Adc0Input, Adc1Input, AfeGain, Dac0Output, Dac1Output, DigitalInput1, + InputPin, AFE0, AFE1, }; +use net::{Action, MiniconfInterface}; + const SCALE: f32 = i16::MAX as _; // The number of cascaded IIR biquads per channel. Select 1 or 2! @@ -44,9 +46,7 @@ const APP: () = { digital_input1: DigitalInput1, adcs: (Adc0Input, Adc1Input), dacs: (Dac0Output, Dac1Output), - mqtt_interface: - MqttInterface, - clock: CycleCounter, + mqtt_config: MiniconfInterface, // Format: iir_state[ch][cascade-no][coeff] #[init([[[0.; 5]; IIR_CASCADE_LENGTH]; 2])] @@ -59,23 +59,13 @@ const APP: () = { // Configure the microcontroller let (mut stabilizer, _pounder) = hardware::setup(c.core, c.device); - let mqtt_interface = { - let mqtt_client = { - minimq::MqttClient::new( - hardware::design_parameters::MQTT_BROKER.into(), - "", - stabilizer.net.stack, - ) - .unwrap() - }; - - MqttInterface::new( - mqtt_client, - "dt/sinara/stabilizer", - Settings::default(), - ) - .unwrap() - }; + let mqtt_config = MiniconfInterface::new( + stabilizer.net.stack, + "", + "dt/sinara/stabilizer", + stabilizer.net.phy, + stabilizer.cycle_counter, + ); // Spawn a settings update for default settings. c.spawn.settings_update().unwrap(); @@ -90,11 +80,10 @@ const APP: () = { stabilizer.adc_dac_timer.start(); init::LateResources { - mqtt_interface, afes: stabilizer.afes, adcs: stabilizer.adcs, dacs: stabilizer.dacs, - clock: stabilizer.cycle_counter, + mqtt_config, digital_input1: stabilizer.digital_inputs.1, settings: Settings::default(), } @@ -151,44 +140,26 @@ const APP: () = { } } - #[idle(resources=[mqtt_interface, clock], spawn=[settings_update])] + #[idle(resources=[mqtt_config], spawn=[settings_update])] fn idle(mut c: idle::Context) -> ! { - let clock = c.resources.clock; - loop { - let sleep = c.resources.mqtt_interface.lock(|interface| { - match interface.network_stack().poll(clock.current_ms()) { - Ok(updated) => !updated, - Err(err) => { - log::info!("Network error: {:?}", err); - false - } - } - }); - match c .resources - .mqtt_interface - .lock(|interface| interface.update()) + .mqtt_config + .lock(|config_interface| config_interface.update()) { - Ok(update) => { - if update { - c.spawn.settings_update().unwrap(); - } else if sleep { - cortex_m::asm::wfi(); - } + Some(Action::Sleep) => cortex_m::asm::wfi(), + Some(Action::UpdateSettings) => { + c.spawn.settings_update().unwrap() } - Err(miniconf::MqttError::Network( - smoltcp_nal::NetworkError::NoIpAddress, - )) => {} - Err(error) => log::info!("Unexpected error: {:?}", error), + _ => {} } } } - #[task(priority = 1, resources=[mqtt_interface, afes, settings])] + #[task(priority = 1, resources=[mqtt_config, afes, settings])] fn settings_update(mut c: settings_update::Context) { - let settings = &c.resources.mqtt_interface.settings; + let settings = &c.resources.mqtt_config.mqtt.settings; // Update the IIR channels. c.resources.settings.lock(|current| *current = *settings); diff --git a/src/bin/lockin-external.rs b/src/bin/lockin-external.rs index 7d2db0e..36bd882 100644 --- a/src/bin/lockin-external.rs +++ b/src/bin/lockin-external.rs @@ -4,16 +4,18 @@ use generic_array::typenum::U4; -use miniconf::{minimq, Miniconf, MqttInterface}; use serde::Deserialize; use dsp::{Accu, Complex, ComplexExt, Lockin, RPLL}; use stabilizer::hardware::{ - design_parameters, setup, Adc0Input, Adc1Input, AfeGain, CycleCounter, - Dac0Output, Dac1Output, InputStamper, NetworkStack, AFE0, AFE1, + design_parameters, setup, Adc0Input, Adc1Input, AfeGain, Dac0Output, + Dac1Output, InputStamper, AFE0, AFE1, }; +use miniconf::Miniconf; +use stabilizer::net::{Action, MiniconfInterface}; + #[derive(Copy, Clone, Debug, Deserialize, Miniconf)] enum Conf { PowerPhase, @@ -56,11 +58,7 @@ const APP: () = { afes: (AFE0, AFE1), adcs: (Adc0Input, Adc1Input), dacs: (Dac0Output, Dac1Output), - clock: CycleCounter, - - mqtt_interface: - MqttInterface, - + mqtt_config: MiniconfInterface, settings: Settings, timestamper: InputStamper, @@ -73,21 +71,13 @@ const APP: () = { // Configure the microcontroller let (mut stabilizer, _pounder) = setup(c.core, c.device); - let mqtt_interface = { - let mqtt_client = minimq::MqttClient::new( - design_parameters::MQTT_BROKER.into(), - "", - stabilizer.net.stack, - ) - .unwrap(); - - MqttInterface::new( - mqtt_client, - "dt/sinara/lockin", - Settings::default(), - ) - .unwrap() - }; + let mqtt_config = MiniconfInterface::new( + stabilizer.net.stack, + "", + "dt/sinara/lockin", + stabilizer.net.phy, + stabilizer.cycle_counter, + ); let settings = Settings::default(); @@ -118,10 +108,8 @@ const APP: () = { afes: stabilizer.afes, adcs: stabilizer.adcs, dacs: stabilizer.dacs, + mqtt_config, timestamper: stabilizer.timestamper, - clock: stabilizer.cycle_counter, - - mqtt_interface, settings, @@ -202,44 +190,26 @@ const APP: () = { } } - #[idle(resources=[mqtt_interface, clock], spawn=[settings_update])] + #[idle(resources=[mqtt_config], spawn=[settings_update])] fn idle(mut c: idle::Context) -> ! { - let clock = c.resources.clock; - loop { - let sleep = c.resources.mqtt_interface.lock(|interface| { - match interface.network_stack().poll(clock.current_ms()) { - Ok(updated) => !updated, - Err(err) => { - log::info!("Network error: {:?}", err); - false - } - } - }); - match c .resources - .mqtt_interface - .lock(|interface| interface.update()) + .mqtt_config + .lock(|config_interface| config_interface.update()) { - Ok(update) => { - if update { - c.spawn.settings_update().unwrap(); - } else if sleep { - cortex_m::asm::wfi(); - } + Some(Action::Sleep) => cortex_m::asm::wfi(), + Some(Action::UpdateSettings) => { + c.spawn.settings_update().unwrap() } - Err(miniconf::MqttError::Network( - smoltcp_nal::NetworkError::NoIpAddress, - )) => {} - Err(error) => log::info!("Unexpected error: {:?}", error), + _ => {} } } } - #[task(priority = 1, resources=[mqtt_interface, settings, afes])] + #[task(priority = 1, resources=[mqtt_config, settings, afes])] fn settings_update(mut c: settings_update::Context) { - let settings = &c.resources.mqtt_interface.settings; + let settings = &c.resources.mqtt_config.mqtt.settings; c.resources.afes.0.set_gain(settings.afe[0]); c.resources.afes.1.set_gain(settings.afe[1]); diff --git a/src/hardware/configuration.rs b/src/hardware/configuration.rs index bff3fe6..5fab968 100644 --- a/src/hardware/configuration.rs +++ b/src/hardware/configuration.rs @@ -14,7 +14,7 @@ use embedded_hal::digital::v2::{InputPin, OutputPin}; use super::{ adc, afe, cycle_counter::CycleCounter, dac, design_parameters, digital_input_stamper, eeprom, pounder, timers, DdsOutput, DigitalInput0, - DigitalInput1, NetworkStack, AFE0, AFE1, + DigitalInput1, EthernetPhy, NetworkStack, AFE0, AFE1, }; pub struct NetStorage { @@ -56,7 +56,7 @@ impl NetStorage { /// The available networking devices on Stabilizer. pub struct NetworkDevices { pub stack: NetworkStack, - pub phy: ethernet::phy::LAN8742A, + pub phy: EthernetPhy, } /// The available hardware interfaces on Stabilizer. diff --git a/src/hardware/mod.rs b/src/hardware/mod.rs index 26800fc..d3e5711 100644 --- a/src/hardware/mod.rs +++ b/src/hardware/mod.rs @@ -51,6 +51,8 @@ pub type NetworkStack = smoltcp_nal::NetworkStack< hal::ethernet::EthernetDMA<'static>, >; +pub type EthernetPhy = hal::ethernet::phy::LAN8742A; + pub use configuration::{setup, PounderDevices, StabilizerDevices}; #[inline(never)] diff --git a/src/lib.rs b/src/lib.rs index 0c9bf2a..975b08f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,3 +5,4 @@ extern crate log; pub mod hardware; +pub mod net; diff --git a/src/net/mod.rs b/src/net/mod.rs new file mode 100644 index 0000000..23d712d --- /dev/null +++ b/src/net/mod.rs @@ -0,0 +1,109 @@ +use crate::hardware::{ + design_parameters::MQTT_BROKER, CycleCounter, EthernetPhy, NetworkStack, +}; + +use miniconf::minimq; + +/// Potential actions for firmware to take. +pub enum Action { + /// Indicates that firmware can sleep for the next event. + Sleep, + + /// Indicates that settings have updated and firmware needs to propogate changes. + UpdateSettings, +} + +/// MQTT settings interface. +pub struct MiniconfInterface +where + S: miniconf::Miniconf + Default, +{ + pub mqtt: miniconf::MqttInterface, + clock: CycleCounter, + phy: EthernetPhy, + network_was_reset: bool, +} + +impl MiniconfInterface +where + S: miniconf::Miniconf + Default, +{ + /// Construct a new MQTT settings interface. + /// + /// # Args + /// * `stack` - The network stack to use for communication. + /// * `client_id` - The ID of the MQTT client. May be an empty string for auto-assigning. + /// * `prefix` - The MQTT device prefix to use for this device. + /// * `phy` - The PHY driver for querying the link state. + /// * `clock` - The clock to utilize for querying the current system time. + pub fn new( + stack: NetworkStack, + client_id: &str, + prefix: &str, + phy: EthernetPhy, + clock: CycleCounter, + ) -> Self { + let mqtt = { + let mqtt_client = { + minimq::MqttClient::new(MQTT_BROKER.into(), client_id, stack) + .unwrap() + }; + + miniconf::MqttInterface::new(mqtt_client, prefix, S::default()) + .unwrap() + }; + + Self { + mqtt, + clock, + phy, + network_was_reset: false, + } + } + + /// Update the MQTT interface and service the network + /// + /// # Returns + /// An option containing an action that should be completed as a result of network servicing. + pub fn update(&mut self) -> Option { + let now = self.clock.current_ms(); + + // First, service the network stack to process and inbound and outbound traffic. + let sleep = match self.mqtt.network_stack().poll(now) { + Ok(updated) => !updated, + Err(err) => { + log::info!("Network error: {:?}", err); + false + } + }; + + // If the PHY indicates there's no more ethernet link, reset the DHCP server in the network + // stack. + if self.phy.poll_link() == false { + // Only reset the network stack once per link reconnection. This prevents us from + // sending an excessive number of DHCP requests. + if !self.network_was_reset { + self.network_was_reset = true; + self.mqtt.network_stack().handle_link_reset(); + } + } else { + self.network_was_reset = false; + } + + // Finally, service the MQTT interface and handle any necessary messages. + match self.mqtt.update() { + Ok(true) => Some(Action::UpdateSettings), + Ok(false) if sleep => Some(Action::Sleep), + Ok(_) => None, + + Err(miniconf::MqttError::Network( + smoltcp_nal::NetworkError::NoIpAddress, + )) => None, + + Err(error) => { + log::info!("Unexpected error: {:?}", error); + None + } + } + } +}