mqtt: support sfkv
This commit is contained in:
parent
d5f9480645
commit
adc5807ff1
72
src/config.rs
Normal file
72
src/config.rs
Normal 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
|
||||
}
|
160
src/mqtt_mux.rs
160
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<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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user