From adc5807ff171dcf55cd2f20dd4eca5d62d705238 Mon Sep 17 00:00:00 2001 From: occheung Date: Fri, 18 Dec 2020 17:42:26 +0800 Subject: [PATCH] mqtt: support sfkv --- src/config.rs | 72 ++++++++++++++++++++++ src/mqtt_mux.rs | 160 +++++++++++++++++++++++++++++++++++++++++++++++- src/urukul.rs | 4 +- 3 files changed, 232 insertions(+), 4 deletions(-) create mode 100644 src/config.rs diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..e6954cd --- /dev/null +++ b/src/config.rs @@ -0,0 +1,72 @@ +use smoltcp as net; +use net::wire::IpCidr; +use net::wire::EthernetAddress; + +use embedded_nal as nal; +use nal::IpAddr; + +use heapless::{ String, consts::* }; + +use serde::{ Serialize, Deserialize }; + +use crate::urukul::ClockSource; +use crate::flash_store::FlashStore; + +use core::str::FromStr; + +#[derive(Debug)] +pub struct NetConfig { + pub ip_cidr: IpCidr, + pub eth_addr: EthernetAddress, + pub broker_ip: IpAddr, + pub name: String, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct UrukulConfig { + pub clk_src: ClockSource, + pub clk_freq: f64, + pub clk_div: u8, + pub profile: u8, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ChannelConfig { + pub sw: bool, + pub att: f32, + pub sys_clk: f64, + pub freq: f64, + pub phase: f64, + pub asf: f64, +} + +pub fn get_net_config(store: &mut FlashStore) -> NetConfig { + let net_config = NetConfig { + ip_cidr: { + match store.read_str("CIDR").unwrap() { + Some(cidr) => IpCidr::from_str(cidr).unwrap(), + None => IpCidr::from_str("192.168.1.200/24").unwrap(), + } + }, + eth_addr: { + match store.read_str("MAC").unwrap() { + Some(mac) => EthernetAddress::from_str(mac).unwrap(), + None => EthernetAddress::from_str("AC:6F:7A:DE:D6:C8").unwrap() + } + }, + broker_ip: { + match store.read_str("BrokerIP").unwrap() { + Some(ip) => IpAddr::from_str(ip).unwrap(), + None => IpAddr::from_str("192.168.1.125").unwrap(), + } + }, + name: { + match store.read_str("Name").unwrap() { + Some(name) => String::from(name), + None => String::from("HumpbackDDS") + } + } + }; + log::info!("Net config: {:?}", net_config); + net_config +} diff --git a/src/mqtt_mux.rs b/src/mqtt_mux.rs index 7f01720..918f40e 100644 --- a/src/mqtt_mux.rs +++ b/src/mqtt_mux.rs @@ -13,6 +13,9 @@ use core::convert::TryInto; use crate::urukul::ClockSource as UrukulClockSource; use crate::urukul::Urukul; use crate::urukul::Error; +use crate::flash_store::{ FlashStore, update_flash }; +use crate::flash::Flash; +use crate::config::{ UrukulConfig, ChannelConfig }; #[derive(Debug, Clone)] pub enum MqttTopic { @@ -29,6 +32,8 @@ pub enum MqttTopic { SingletoneAmplitude(u8, u8), SingletonePhase(u8, u8), Profile, + Save, + Load, } // Prossible change: Make this enum public to all comm protocol (if any) @@ -48,26 +53,131 @@ pub enum MqttCommand { SingletoneFrequency(u8, u8, f64), SingletoneAmplitude(u8, u8, f64), SingletonePhase(u8, u8, f64), - Profile(u8) + Profile(u8), + Save, + Load, } pub struct MqttMux<'s, SPI> { urukul: Urukul, yet_to_respond: Option, + flash_controller: Flash, + flash_store: FlashStore, name: &'s str, float_buffer: ryu::Buffer, } +const CHANNELS: [&str; 4] = [ "ch0", "ch1", "ch2", "ch3" ]; +static mut SERDE_BUFFER: [u8; 64] = [0; 64]; + impl<'s, SPI, E> MqttMux<'s, SPI> where SPI: Transfer { - pub fn new(urukul: Urukul, name: &'s str) -> Self { - MqttMux { + pub fn new(urukul: Urukul, flash_controller: Flash, flash_store: FlashStore, name: &'s str) -> Self { + Self { urukul: urukul, yet_to_respond: None, + flash_controller, + flash_store, name: name, float_buffer: ryu::Buffer::new(), } } + fn load_device(&mut self) -> Result<(), Error> { + self.urukul.reset()?; + let profile = match self.flash_store + .read_value::("urukul") + .unwrap() + { + Some(urukul_config) => { + self.urukul.set_clock( + urukul_config.clk_src, + urukul_config.clk_freq, + urukul_config.clk_div + )?; + self.urukul.set_profile(urukul_config.profile)?; + urukul_config.profile + }, + None => 0, + }; + + for (channel, channel_tag) in CHANNELS.iter().enumerate() { + match self.flash_store + .read_value::(channel_tag) + .unwrap() + { + Some(channel_config) => { + self.urukul.set_channel_switch( + channel as u32, + channel_config.sw + )?; + self.urukul.set_channel_attenuation( + channel as u8, + channel_config.att + )?; + self.urukul.set_channel_sys_clk( + channel as u8, + channel_config.sys_clk + )?; + self.urukul.set_channel_single_tone_profile( + channel as u8, + profile, + channel_config.freq, + channel_config.phase, + channel_config.asf, + )?; + }, + None => () + }; + } + Ok(()) + } + + fn save_device(&mut self) -> Result<(), Error> { + let urukul_config = UrukulConfig { + clk_src: { + self.urukul.get_clock_source()? + }, + clk_freq: { + self.urukul.get_clock_frequency() + }, + clk_div: { + self.urukul.get_clock_division()? + }, + profile: { + self.urukul.get_profile()? + } + }; + + unsafe { self.flash_store.write_value("urukul", &urukul_config, &mut SERDE_BUFFER).unwrap(); } + + for channel in 0..4 { + let (freq, phase, asf) = self.urukul.get_channel_single_tone_profile( + channel as u8, + urukul_config.profile + )?; + let channel_config = ChannelConfig { + sw: { + self.urukul.get_channel_switch_status(channel as u32)? + }, + att: { + self.urukul.get_channel_attenuation(channel as u8)? + }, + sys_clk: { + self.urukul.get_channel_sys_clk(channel as u8)? + }, + freq, + phase, + asf + }; + unsafe { + self.flash_store.write_value(CHANNELS[channel], &channel_config, &mut SERDE_BUFFER).unwrap(); + } + } + + update_flash(&mut self.flash_controller, &mut self.flash_store).unwrap(); + Ok(()) + } + // Instead of using a return type, the result of processing the command is stored internally // Invoke process_mqtt_egress to get a response after invoking ingress handler pub fn process_mqtt_ingress(&mut self, topic: &str, message: &[u8]) { @@ -273,6 +383,32 @@ impl<'s, SPI, E> MqttMux<'s, SPI> where SPI: Transfer { )).map_err(|_| Error::VectorOutOfSpace)?; Ok(vec) } + + MqttCommand::Save => { + vec.push(( + { + let mut topic_string = String::from(self.name); + topic_string.push_str("/Feedback/Save") + .map_err(|_| Error::StringOutOfSpace)?; + topic_string + }, + String::from("Saved device.") + )).map_err(|_| Error::VectorOutOfSpace)?; + Ok(vec) + } + + MqttCommand::Load => { + vec.push(( + { + let mut topic_string = String::from(self.name); + topic_string.push_str("/Feedback/Load") + .map_err(|_| Error::StringOutOfSpace)?; + topic_string + }, + String::from("Loaded from flash.") + )).map_err(|_| Error::VectorOutOfSpace)?; + Ok(vec) + } }, None => Ok(vec), } @@ -455,6 +591,20 @@ impl<'s, SPI, E> MqttMux<'s, SPI> where SPI: Transfer { return Ok(MqttTopic::Profile); } + "Save" => { + if assigned_channel || assigned_profile { + return Err(Error::MqttCommandError) + } + return Ok(MqttTopic::Save); + } + + "Load" => { + if assigned_channel || assigned_profile { + return Err(Error::MqttCommandError) + } + return Ok(MqttTopic::Load); + } + _ => return Err(Error::MqttCommandError), }; } @@ -475,6 +625,8 @@ impl<'s, SPI, E> MqttMux<'s, SPI> where SPI: Transfer { MqttTopic::SingletoneAmplitude(ch, prof) => singletone_amplitude_message(ch, prof, message), MqttTopic::SingletonePhase(ch, prof) => singletone_phase_message(ch, prof, message), MqttTopic::Profile => profile_message(message), + MqttTopic::Save => Ok((message, MqttCommand::Save)), + MqttTopic::Load => Ok((message, MqttCommand::Load)), } } @@ -494,6 +646,8 @@ impl<'s, SPI, E> MqttMux<'s, SPI> where SPI: Transfer { MqttCommand::SingletoneAmplitude(ch, prof, ampl) => self.urukul.set_channel_single_tone_profile_amplitude(ch, prof, ampl), MqttCommand::SingletonePhase(ch, prof, deg) => self.urukul.set_channel_single_tone_profile_phase(ch, prof, deg), MqttCommand::Profile(prof) => self.urukul.set_profile(prof), + MqttCommand::Save => self.save_device(), + MqttCommand::Load => self.load_device(), } } diff --git a/src/urukul.rs b/src/urukul.rs index df9d57f..e97157f 100644 --- a/src/urukul.rs +++ b/src/urukul.rs @@ -3,6 +3,8 @@ use embedded_hal::{ blocking::spi::Transfer, }; +use serde::{ Serialize, Deserialize }; + use crate::config_register::ConfigRegister; use crate::config_register::CFGMask; use crate::config_register::StatusMask; @@ -30,7 +32,7 @@ pub enum Error { StringOutOfSpace, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum ClockSource { OSC, SMA,