forked from M-Labs/humpback-dds
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::ClockSource as UrukulClockSource;
|
||||||
use crate::urukul::Urukul;
|
use crate::urukul::Urukul;
|
||||||
use crate::urukul::Error;
|
use crate::urukul::Error;
|
||||||
|
use crate::flash_store::{ FlashStore, update_flash };
|
||||||
|
use crate::flash::Flash;
|
||||||
|
use crate::config::{ UrukulConfig, ChannelConfig };
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum MqttTopic {
|
pub enum MqttTopic {
|
||||||
@ -29,6 +32,8 @@ pub enum MqttTopic {
|
|||||||
SingletoneAmplitude(u8, u8),
|
SingletoneAmplitude(u8, u8),
|
||||||
SingletonePhase(u8, u8),
|
SingletonePhase(u8, u8),
|
||||||
Profile,
|
Profile,
|
||||||
|
Save,
|
||||||
|
Load,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prossible change: Make this enum public to all comm protocol (if any)
|
// Prossible change: Make this enum public to all comm protocol (if any)
|
||||||
@ -48,26 +53,131 @@ pub enum MqttCommand {
|
|||||||
SingletoneFrequency(u8, u8, f64),
|
SingletoneFrequency(u8, u8, f64),
|
||||||
SingletoneAmplitude(u8, u8, f64),
|
SingletoneAmplitude(u8, u8, f64),
|
||||||
SingletonePhase(u8, u8, f64),
|
SingletonePhase(u8, u8, f64),
|
||||||
Profile(u8)
|
Profile(u8),
|
||||||
|
Save,
|
||||||
|
Load,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MqttMux<'s, SPI> {
|
pub struct MqttMux<'s, SPI> {
|
||||||
urukul: Urukul<SPI>,
|
urukul: Urukul<SPI>,
|
||||||
yet_to_respond: Option<MqttCommand>,
|
yet_to_respond: Option<MqttCommand>,
|
||||||
|
flash_controller: Flash,
|
||||||
|
flash_store: FlashStore,
|
||||||
name: &'s str,
|
name: &'s str,
|
||||||
float_buffer: ryu::Buffer,
|
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> {
|
impl<'s, SPI, E> MqttMux<'s, SPI> where SPI: Transfer<u8, Error = E> {
|
||||||
pub fn new(urukul: Urukul<SPI>, name: &'s str) -> Self {
|
pub fn new(urukul: Urukul<SPI>, flash_controller: Flash, flash_store: FlashStore, name: &'s str) -> Self {
|
||||||
MqttMux {
|
Self {
|
||||||
urukul: urukul,
|
urukul: urukul,
|
||||||
yet_to_respond: None,
|
yet_to_respond: None,
|
||||||
|
flash_controller,
|
||||||
|
flash_store,
|
||||||
name: name,
|
name: name,
|
||||||
float_buffer: ryu::Buffer::new(),
|
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
|
// 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
|
// Invoke process_mqtt_egress to get a response after invoking ingress handler
|
||||||
pub fn process_mqtt_ingress(&mut self, topic: &str, message: &[u8]) {
|
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)?;
|
)).map_err(|_| Error::VectorOutOfSpace)?;
|
||||||
Ok(vec)
|
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),
|
None => Ok(vec),
|
||||||
}
|
}
|
||||||
@ -455,6 +591,20 @@ impl<'s, SPI, E> MqttMux<'s, SPI> where SPI: Transfer<u8, Error = E> {
|
|||||||
return Ok(MqttTopic::Profile);
|
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),
|
_ => 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::SingletoneAmplitude(ch, prof) => singletone_amplitude_message(ch, prof, message),
|
||||||
MqttTopic::SingletonePhase(ch, prof) => singletone_phase_message(ch, prof, message),
|
MqttTopic::SingletonePhase(ch, prof) => singletone_phase_message(ch, prof, message),
|
||||||
MqttTopic::Profile => profile_message(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::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::SingletonePhase(ch, prof, deg) => self.urukul.set_channel_single_tone_profile_phase(ch, prof, deg),
|
||||||
MqttCommand::Profile(prof) => self.urukul.set_profile(prof),
|
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,
|
blocking::spi::Transfer,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use serde::{ Serialize, Deserialize };
|
||||||
|
|
||||||
use crate::config_register::ConfigRegister;
|
use crate::config_register::ConfigRegister;
|
||||||
use crate::config_register::CFGMask;
|
use crate::config_register::CFGMask;
|
||||||
use crate::config_register::StatusMask;
|
use crate::config_register::StatusMask;
|
||||||
@ -30,7 +32,7 @@ pub enum Error<E> {
|
|||||||
StringOutOfSpace,
|
StringOutOfSpace,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum ClockSource {
|
pub enum ClockSource {
|
||||||
OSC,
|
OSC,
|
||||||
SMA,
|
SMA,
|
||||||
|
Loading…
Reference in New Issue
Block a user