diff --git a/Cargo.lock b/Cargo.lock index b435a6b..8b66fe7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -707,7 +707,7 @@ dependencies = [ [[package]] name = "smoltcp-nal" version = "0.1.0" -source = "git+https://github.com/quartiq/smoltcp-nal.git?branch=main#56519012d7c6a382eaa0d7ecb26f2701771d9ce8" +source = "git+https://github.com/quartiq/smoltcp-nal.git?branch=feature/reset-support#a6db6579100987502563578fb386109ad08758a7" dependencies = [ "embedded-nal", "heapless 0.6.1", diff --git a/Cargo.toml b/Cargo.toml index 6b14532..f1d88b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,7 @@ git = "https://github.com/quartiq/minimq.git" [patch.crates-io.smoltcp-nal] git = "https://github.com/quartiq/smoltcp-nal.git" -branch = "main" +branch = "feature/reset-support" [patch.crates-io.serde-json-core] git = "https://github.com/rust-embedded-community/serde-json-core.git" diff --git a/src/bin/dual-iir.rs b/src/bin/dual-iir.rs index 4a27d48..6c391c7 100644 --- a/src/bin/dual-iir.rs +++ b/src/bin/dual-iir.rs @@ -4,17 +4,18 @@ use stm32h7xx_hal as hal; -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, - NetworkStack, AFE0, AFE1, + Adc0Input, Adc1Input, AfeGain, Dac0Output, Dac1Output, AFE0, AFE1, }; +use net::{Action, MqttSettings}; + const SCALE: f32 = i16::MAX as _; // The number of cascaded IIR biquads per channel. Select 1 or 2! @@ -41,9 +42,7 @@ const APP: () = { afes: (AFE0, AFE1), adcs: (Adc0Input, Adc1Input), dacs: (Dac0Output, Dac1Output), - mqtt_interface: - MqttInterface, - clock: CycleCounter, + mqtt_settings: MqttSettings, // Format: iir_state[ch][cascade-no][coeff] #[init([[[0.; 5]; IIR_CASCADE_LENGTH]; 2])] @@ -57,23 +56,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_settings = MqttSettings::new( + stabilizer.net.stack, + "", + "dt/sinara/stabilizer", + stabilizer.net.phy, + stabilizer.cycle_counter, + ); // Enable ADC/DAC events stabilizer.adcs.0.start(); @@ -85,11 +74,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_settings, } } @@ -138,44 +126,22 @@ const APP: () = { } } - #[idle(resources=[mqtt_interface, clock], spawn=[settings_update])] + #[idle(resources=[mqtt_settings], 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_settings.lock(|settings| settings.update()) { + Some(Action::Sleep) => cortex_m::asm::wfi(), + Some(Action::UpdateSettings) => { + c.spawn.settings_update().unwrap() } - }); - - match c - .resources - .mqtt_interface - .lock(|interface| interface.update()) - { - Ok(update) => { - if update { - c.spawn.settings_update().unwrap(); - } else if sleep { - cortex_m::asm::wfi(); - } - } - Err(miniconf::MqttError::Network( - smoltcp_nal::NetworkError::NoIpAddress, - )) => {} - Err(error) => log::info!("Unexpected error: {:?}", error), + _ => {} } } } - #[task(priority = 1, resources=[mqtt_interface, afes, iir_ch])] + #[task(priority = 1, resources=[mqtt_settings, afes, iir_ch])] fn settings_update(mut c: settings_update::Context) { - let settings = &c.resources.mqtt_interface.settings; + let settings = &c.resources.mqtt_settings.mqtt_interface.settings; // Update the IIR channels. c.resources.iir_ch.lock(|iir| *iir = settings.iir_ch); diff --git a/src/bin/lockin-external.rs b/src/bin/lockin-external.rs index 7d2db0e..bb84e91 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, MqttSettings}; + #[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_settings: MqttSettings, 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_settings = MqttSettings::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_settings, timestamper: stabilizer.timestamper, - clock: stabilizer.cycle_counter, - - mqtt_interface, settings, @@ -202,44 +190,22 @@ const APP: () = { } } - #[idle(resources=[mqtt_interface, clock], spawn=[settings_update])] + #[idle(resources=[mqtt_settings], 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_settings.lock(|settings| settings.update()) { + Some(Action::Sleep) => cortex_m::asm::wfi(), + Some(Action::UpdateSettings) => { + c.spawn.settings_update().unwrap() } - }); - - match c - .resources - .mqtt_interface - .lock(|interface| interface.update()) - { - Ok(update) => { - if update { - c.spawn.settings_update().unwrap(); - } else if sleep { - cortex_m::asm::wfi(); - } - } - 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_settings, settings, afes])] fn settings_update(mut c: settings_update::Context) { - let settings = &c.resources.mqtt_interface.settings; + let settings = &c.resources.mqtt_settings.mqtt_interface.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 dfab1e0..74307dd 100644 --- a/src/hardware/configuration.rs +++ b/src/hardware/configuration.rs @@ -13,8 +13,8 @@ use embedded_hal::digital::v2::{InputPin, OutputPin}; use super::{ adc, afe, cycle_counter::CycleCounter, dac, design_parameters, - digital_input_stamper, eeprom, pounder, timers, DdsOutput, NetworkStack, - AFE0, AFE1, + digital_input_stamper, eeprom, pounder, timers, DdsOutput, 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 cc7a34a..60e7768 100644 --- a/src/hardware/mod.rs +++ b/src/hardware/mod.rs @@ -40,6 +40,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..a996555 --- /dev/null +++ b/src/net/mod.rs @@ -0,0 +1,89 @@ +use crate::hardware::{ + design_parameters::MQTT_BROKER, CycleCounter, EthernetPhy, NetworkStack, +}; + +use miniconf::{minimq, MqttInterface}; + +pub enum Action { + Sleep, + UpdateSettings, +} + +pub struct MqttSettings +where + S: miniconf::Miniconf + Default, +{ + pub mqtt_interface: MqttInterface, + clock: CycleCounter, + phy: EthernetPhy, + network_was_reset: bool, +} + +impl MqttSettings +where + S: miniconf::Miniconf + Default, +{ + pub fn new( + stack: NetworkStack, + client_id: &str, + prefix: &str, + phy: EthernetPhy, + clock: CycleCounter, + ) -> Self { + let mqtt_interface = { + let mqtt_client = { + minimq::MqttClient::new(MQTT_BROKER.into(), client_id, stack) + .unwrap() + }; + + MqttInterface::new(mqtt_client, prefix, S::default()).unwrap() + }; + + Self { + mqtt_interface, + clock, + phy, + network_was_reset: false, + } + } + + pub fn update(&mut self) -> Option { + let now = self.clock.current_ms(); + + let sleep = match self.mqtt_interface.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 network stack and close all + // sockets. + 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_interface.network_stack().reset(); + } + } else { + self.network_was_reset = false; + } + + match self.mqtt_interface.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 + } + } + } +}