diff --git a/src/bin/dual-iir.rs b/src/bin/dual-iir.rs index 5c27e50d4..8dad0af5b 100644 --- a/src/bin/dual-iir.rs +++ b/src/bin/dual-iir.rs @@ -208,7 +208,7 @@ mod app { let clock = SystemTimer::new(|| monotonics::now().ticks() as u32); // Configure the microcontroller - let (stabilizer, _pounder) = hardware::setup::setup( + let (mut stabilizer, _pounder) = hardware::setup::setup( c.core, c.device, clock, @@ -222,7 +222,8 @@ mod app { clock, env!("CARGO_BIN_NAME"), stabilizer.net.mac_address, - option_env!("BROKER").unwrap_or("mqtt"), + stabilizer.usb_serial.flash().fetch_item("broker"), + stabilizer.usb_serial.flash().fetch_item("id"), ); let generator = network.configure_streaming(StreamFormat::AdcDacData); diff --git a/src/bin/lockin.rs b/src/bin/lockin.rs index a116b4da1..4efc9c883 100644 --- a/src/bin/lockin.rs +++ b/src/bin/lockin.rs @@ -262,7 +262,8 @@ mod app { clock, env!("CARGO_BIN_NAME"), stabilizer.net.mac_address, - option_env!("BROKER").unwrap_or("mqtt"), + stabilizer.usb_serial.flash().fetch_item("broker"), + stabilizer.usb_serial.flash().fetch_item("id"), ); let generator = network.configure_streaming(StreamFormat::AdcDacData); diff --git a/src/hardware/flash.rs b/src/hardware/flash.rs index 5afb162a5..80f15493c 100644 --- a/src/hardware/flash.rs +++ b/src/hardware/flash.rs @@ -1,5 +1,5 @@ -use stm32h7xx_hal::flash::{UnlockedFlashBank, LockedFlashBank}; use embedded_storage::nor_flash::NorFlash; +use stm32h7xx_hal::flash::{LockedFlashBank, UnlockedFlashBank}; #[derive(Debug)] pub enum StorageError { JsonDe(serde_json_core::de::Error), @@ -22,11 +22,21 @@ impl sequential_storage::map::StorageItemError for StorageError { fn is_buffer_too_small(&self) -> bool { match self { Self::JsonSer(serde_json_core::ser::Error::BufferFull) => true, - Self::JsonDe(serde_json_core::de::Error::EofWhileParsingString) => true, - Self::JsonDe(serde_json_core::de::Error::EofWhileParsingList) => true, - Self::JsonDe(serde_json_core::de::Error::EofWhileParsingObject) => true, - Self::JsonDe(serde_json_core::de::Error::EofWhileParsingNumber) => true, - Self::JsonDe(serde_json_core::de::Error::EofWhileParsingValue) => true, + Self::JsonDe(serde_json_core::de::Error::EofWhileParsingString) => { + true + } + Self::JsonDe(serde_json_core::de::Error::EofWhileParsingList) => { + true + } + Self::JsonDe(serde_json_core::de::Error::EofWhileParsingObject) => { + true + } + Self::JsonDe(serde_json_core::de::Error::EofWhileParsingNumber) => { + true + } + Self::JsonDe(serde_json_core::de::Error::EofWhileParsingValue) => { + true + } _ => false, } } @@ -40,22 +50,27 @@ macro_rules! storage_item { type Key = &'static str; type Error = StorageError; - fn serialize_into(&self, buffer: &mut [u8]) -> Result { + fn serialize_into( + &self, + buffer: &mut [u8], + ) -> Result { Ok(serde_json_core::to_slice(&self.0, buffer)?) } - fn deserialize_from(buffer: &[u8]) -> Result<(Self, usize), Self::Error> { - Ok(serde_json_core::from_slice(buffer).map(|(item, size)| (Self(item), size))?) + fn deserialize_from( + buffer: &[u8], + ) -> Result<(Self, usize), Self::Error> { + Ok(serde_json_core::from_slice(buffer) + .map(|(item, size)| (Self(item), size))?) } fn key(&self) -> Self::Key { $key } } - } + }; } - storage_item!(23, "id", MqttIdentifier); storage_item!(255, "broker", BrokerAddress); @@ -68,15 +83,30 @@ impl FlashSettings { Self { flash } } - pub fn store_item(&mut self, item: impl sequential_storage::map::StorageItem) { + pub fn store_item( + &mut self, + item: impl sequential_storage::map::StorageItem, + ) { let mut bank = self.flash.unlocked(); - let range = (bank.address() as u32)..((bank.address() + bank.len()) as u32); - sequential_storage::map::store_item::<_, _, { ::ERASE_SIZE } >(&mut bank, range, item).unwrap(); + let range = + (bank.address() as u32)..((bank.address() + bank.len()) as u32); + sequential_storage::map::store_item::< + _, + _, + { ::ERASE_SIZE }, + >(&mut bank, range, item) + .unwrap(); } - pub fn fetch_item>(&mut self, key: &'static str) -> Option { + pub fn fetch_item< + I: sequential_storage::map::StorageItem, + >( + &mut self, + key: &'static str, + ) -> Option { let mut bank = self.flash.unlocked(); - let range = (bank.address() as u32)..((bank.address() + bank.len()) as u32); + let range = + (bank.address() as u32)..((bank.address() + bank.len()) as u32); sequential_storage::map::fetch_item(&mut bank, range, key).unwrap() } } diff --git a/src/hardware/mod.rs b/src/hardware/mod.rs index 9cd12f24b..a75cd1f39 100644 --- a/src/hardware/mod.rs +++ b/src/hardware/mod.rs @@ -9,6 +9,7 @@ pub mod cpu_temp_sensor; pub mod dac; pub mod delay; pub mod design_parameters; +pub mod flash; pub mod input_stamper; pub mod pounder; pub mod serial_terminal; @@ -16,7 +17,6 @@ pub mod setup; pub mod shared_adc; pub mod signal_generator; pub mod timers; -pub mod flash; mod eeprom; diff --git a/src/hardware/serial_terminal.rs b/src/hardware/serial_terminal.rs index 17a73dcb3..b48a20e00 100644 --- a/src/hardware/serial_terminal.rs +++ b/src/hardware/serial_terminal.rs @@ -1,6 +1,6 @@ use super::UsbBus; +use crate::hardware::flash::{BrokerAddress, FlashSettings, MqttIdentifier}; use core::fmt::Write; -use crate::hardware::flash::{MqttIdentifier, BrokerAddress, FlashSettings}; struct Context { output: OutputBuffer, @@ -91,7 +91,9 @@ fn handle_property_read( args: &[&str], context: &mut Context, ) { - let props: heapless::Vec<&'_ str, 2> = if let Some(prop) = menu::argument_finder(item, args, "property").unwrap() { + let props: heapless::Vec<&'_ str, 2> = if let Some(prop) = + menu::argument_finder(item, args, "property").unwrap() + { heapless::Vec::from_slice(&[prop]).unwrap() } else { heapless::Vec::from_slice(&["id", "broker"]).unwrap() @@ -101,12 +103,20 @@ fn handle_property_read( write!(&mut context.output, "{prop}: ").unwrap(); match prop { "id" => { - let value = context.flash.fetch_item::("mqtt-id").unwrap().0; + let value = context + .flash + .fetch_item::("mqtt-id") + .map(|inner| inner.0) + .unwrap_or_else(|| "".into()); writeln!(&mut context.output, "{value}").unwrap(); } "broker" => { - let value = context.flash.fetch_item::("broker").unwrap().0; + let value = context + .flash + .fetch_item::("broker") + .map(|inner| inner.0) + .unwrap_or_else(|| "mqtt".into()); writeln!(&mut context.output, "{value}").unwrap(); } @@ -132,8 +142,12 @@ fn handle_property_write( // Now, write the new value into memory. // TODO: Validate it first? match property { - "id" => context.flash.store_item(MqttIdentifier(heapless::String::from(value))), - "broker" => context.flash.store_item(BrokerAddress(heapless::String::from(value))), + "id" => context + .flash + .store_item(MqttIdentifier(heapless::String::from(value))), + "broker" => context + .flash + .store_item(BrokerAddress(heapless::String::from(value))), other => { writeln!(&mut context.output, "Unknown property: {other}").unwrap(); return; @@ -163,7 +177,8 @@ impl SerialTerminal { ) -> Self { let (producer, consumer) = OUTPUT_BUFFER.try_split().unwrap(); - let input_buffer = cortex_m::singleton!(: [u8; 255] = [0; 255]).unwrap(); + let input_buffer = + cortex_m::singleton!(: [u8; 255] = [0; 255]).unwrap(); let context = Context { output: OutputBuffer { producer }, flash, @@ -176,6 +191,10 @@ impl SerialTerminal { } } + pub fn flash(&mut self) -> &mut FlashSettings { + &mut self.menu.context.flash + } + fn flush(&mut self) { let read = match self.output.read() { Ok(grant) => grant, @@ -215,10 +234,13 @@ impl SerialTerminal { Err(usbd_serial::UsbError::WouldBlock) => {} Err(_) => { self.menu.prompt(true); - self.output.read().map(|grant| { - let len = grant.buf().len(); - grant.release(len); - }).ok(); + self.output + .read() + .map(|grant| { + let len = grant.buf().len(); + grant.release(len); + }) + .ok(); } } } diff --git a/src/hardware/setup.rs b/src/hardware/setup.rs index 4f10cccaf..bbf3e2518 100644 --- a/src/hardware/setup.rs +++ b/src/hardware/setup.rs @@ -14,12 +14,11 @@ use smoltcp_nal::smoltcp; use super::{ adc, afe, cpu_temp_sensor::CpuTempSensor, dac, delay, design_parameters, - eeprom, input_stamper::InputStamper, pounder, + eeprom, flash::FlashSettings, input_stamper::InputStamper, pounder, pounder::dds_output::DdsOutput, serial_terminal::SerialTerminal, shared_adc::SharedAdc, timers, DigitalInput0, DigitalInput1, EemDigitalInput0, EemDigitalInput1, EemDigitalOutput0, EemDigitalOutput1, EthernetPhy, NetworkStack, SystemTimer, Systick, UsbBus, AFE0, AFE1, - flash::FlashSettings, }; const NUM_TCP_SOCKETS: usize = 4; @@ -1062,7 +1061,11 @@ pub fn setup( usb_bus.as_ref().unwrap(), usb_device::device::UsbVidPid(0x1209, 0x392F), ) - .strings(&[usb_device::device::StringDescriptors::default().manufacturer("ARTIQ/Sinara").product("Stabilizer").serial_number(serial_number.as_ref().unwrap())]).unwrap() + .strings(&[usb_device::device::StringDescriptors::default() + .manufacturer("ARTIQ/Sinara") + .product("Stabilizer") + .serial_number(serial_number.as_ref().unwrap())]) + .unwrap() .device_class(usbd_serial::USB_CLASS_CDC) .build(); diff --git a/src/net/mod.rs b/src/net/mod.rs index 7e3b7b66b..cf8d6e118 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -13,7 +13,10 @@ pub mod data_stream; pub mod network_processor; pub mod telemetry; -use crate::hardware::{EthernetPhy, NetworkManager, NetworkStack, SystemTimer}; +use crate::hardware::{ + flash::{BrokerAddress, MqttIdentifier}, + EthernetPhy, NetworkManager, NetworkStack, SystemTimer, +}; use data_stream::{DataStream, FrameGenerator}; use network_processor::NetworkProcessor; use telemetry::TelemetryClient; @@ -95,23 +98,34 @@ where clock: SystemTimer, app: &str, mac: smoltcp_nal::smoltcp::wire::EthernetAddress, - broker: &str, + broker: Option, + id: Option, ) -> Self { let stack_manager = cortex_m::singleton!(: NetworkManager = NetworkManager::new(stack)) .unwrap(); + let id = if let Some(id) = id { + id.0 + } else { + let mut id: String<23> = String::new(); + write!(&mut id, "{mac}").unwrap(); + id + }; + + let broker = broker.map(|b| b.0).unwrap_or_else(|| "mqtt".into()); + let processor = NetworkProcessor::new(stack_manager.acquire_stack(), phy); - let prefix = get_device_prefix(app, mac); + let prefix = get_device_prefix(app, &id); let store = cortex_m::singleton!(: MqttStorage = MqttStorage::default()) .unwrap(); let named_broker = miniconf::minimq::broker::NamedBroker::new( - broker, + &broker, stack_manager.acquire_stack(), ) .unwrap(); @@ -124,13 +138,13 @@ where named_broker, &mut store.settings, ) - .client_id(&get_client_id(app, "settings", mac)) + .client_id(&id) .unwrap(), ) .unwrap(); let named_broker = minimq::broker::NamedBroker::new( - broker, + &broker, stack_manager.acquire_stack(), ) .unwrap(); @@ -141,7 +155,7 @@ where // The telemetry client doesn't receive any messages except MQTT control packets. // As such, we don't need much of the buffer for RX. .rx_buffer(minimq::config::BufferConfig::Maximum(100)) - .client_id(&get_client_id(app, "tlm", mac)) + .client_id(&id) .unwrap(), ); @@ -215,41 +229,19 @@ where } } -/// Get an MQTT client ID for a client. -/// -/// # Args -/// * `app` - The name of the application -/// * `client` - The unique tag of the client -/// * `mac` - The MAC address of the device. -/// -/// # Returns -/// A client ID that may be used for MQTT client identification. -fn get_client_id( - app: &str, - client: &str, - mac: smoltcp_nal::smoltcp::wire::EthernetAddress, -) -> String<64> { - let mut identifier = String::new(); - write!(&mut identifier, "{app}-{mac}-{client}").unwrap(); - identifier -} - /// Get the MQTT prefix of a device. /// /// # Args /// * `app` - The name of the application that is executing. -/// * `mac` - The ethernet MAC address of the device. +/// * `id` - The MQTT ID of the device. /// /// # Returns /// The MQTT prefix used for this device. -pub fn get_device_prefix( - app: &str, - mac: smoltcp_nal::smoltcp::wire::EthernetAddress, -) -> String<128> { +pub fn get_device_prefix(app: &str, id: &str) -> String<128> { // Note(unwrap): The mac address + binary name must be short enough to fit into this string. If // they are defined too long, this will panic and the device will fail to boot. let mut prefix: String<128> = String::new(); - write!(&mut prefix, "dt/sinara/{app}/{mac}").unwrap(); + write!(&mut prefix, "dt/sinara/{app}/{id}").unwrap(); prefix }