mqtt: support sfkv

pull/4/head
occheung 2020-12-18 17:42:26 +08:00
parent d5f9480645
commit adc5807ff1
3 changed files with 232 additions and 4 deletions

72
src/config.rs Normal file
View File

@ -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<U64>,
}
#[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
}

View File

@ -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<SPI>,
yet_to_respond: Option<MqttCommand>,
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<u8, Error = E> {
pub fn new(urukul: Urukul<SPI>, name: &'s str) -> Self {
MqttMux {
pub fn new(urukul: Urukul<SPI>, 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<E>> {
self.urukul.reset()?;
let profile = match self.flash_store
.read_value::<UrukulConfig>("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::<ChannelConfig>(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<E>> {
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<u8, Error = E> {
)).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<u8, Error = E> {
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<u8, Error = E> {
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<u8, Error = E> {
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(),
}
}

View File

@ -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<E> {
StringOutOfSpace,
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ClockSource {
OSC,
SMA,