Compare commits
No commits in common. "c467b9a1b90e0e18d4230c3b75395943a829a8b4" and "fe5ea3486a66d730deb7d8a56490f8fd59e2fb66" have entirely different histories.
c467b9a1b9
...
fe5ea3486a
|
@ -257,7 +257,6 @@ dependencies = [
|
||||||
"nom",
|
"nom",
|
||||||
"panic-halt",
|
"panic-halt",
|
||||||
"panic-itm",
|
"panic-itm",
|
||||||
"ryu",
|
|
||||||
"smoltcp",
|
"smoltcp",
|
||||||
"stm32h7xx-hal",
|
"stm32h7xx-hal",
|
||||||
]
|
]
|
||||||
|
@ -435,12 +434,6 @@ dependencies = [
|
||||||
"semver",
|
"semver",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ryu"
|
|
||||||
version = "1.0.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
|
|
@ -17,7 +17,6 @@ embedded-nal = "0.1.0"
|
||||||
minimq = { git = "https://github.com/quartiq/minimq.git", branch = "master" }
|
minimq = { git = "https://github.com/quartiq/minimq.git", branch = "master" }
|
||||||
heapless = "0.5.6"
|
heapless = "0.5.6"
|
||||||
nom = { version = "5.1.2", default-features = false, features = [] }
|
nom = { version = "5.1.2", default-features = false, features = [] }
|
||||||
ryu = "1.0"
|
|
||||||
|
|
||||||
# Logging and Panicking
|
# Logging and Panicking
|
||||||
panic-itm = "0.4.1"
|
panic-itm = "0.4.1"
|
||||||
|
|
34
src/main.rs
34
src/main.rs
|
@ -1,9 +1,8 @@
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![feature(core_intrinsics)]
|
#![feature(core_intrinsics)]
|
||||||
#![feature(assoc_char_funcs)]
|
|
||||||
|
|
||||||
use log::{ trace, warn };
|
use log::{ trace };
|
||||||
use stm32h7xx_hal::gpio::Speed;
|
use stm32h7xx_hal::gpio::Speed;
|
||||||
use stm32h7xx_hal::{pac, prelude::*, spi};
|
use stm32h7xx_hal::{pac, prelude::*, spi};
|
||||||
use stm32h7xx_hal::ethernet;
|
use stm32h7xx_hal::ethernet;
|
||||||
|
@ -263,28 +262,11 @@ fn main() -> ! {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process MQTT messages about Urukul/Control
|
// Process MQTT messages about Urukul/Control
|
||||||
let connection = match client
|
let connection = client
|
||||||
.poll(|_client, topic, message, _properties| {
|
.poll(|_client, topic, message, _properties| {
|
||||||
// Why is topic a string while message is a slice?
|
// Why is topic a string while message is a slice?
|
||||||
mqtt_mux.process_mqtt_ingress(topic, message);
|
mqtt_mux.process_mqtt(topic, message).unwrap();
|
||||||
}) {
|
}).is_ok();
|
||||||
Ok(_) => true,
|
|
||||||
Err(e) => {
|
|
||||||
warn!("{:?}", e);
|
|
||||||
false
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Process MQTT response messages about Urukul
|
|
||||||
match mqtt_mux.process_mqtt_egress().unwrap() {
|
|
||||||
Some((topic, message)) => client.publish(
|
|
||||||
topic,
|
|
||||||
message.as_bytes(),
|
|
||||||
QoS::AtMostOnce,
|
|
||||||
&[]
|
|
||||||
).unwrap(),
|
|
||||||
None => {},
|
|
||||||
}
|
|
||||||
|
|
||||||
if connection && !has_subscribed && tick {
|
if connection && !has_subscribed && tick {
|
||||||
match client.subscribe("Urukul/Control/#", &[]) {
|
match client.subscribe("Urukul/Control/#", &[]) {
|
||||||
|
@ -294,6 +276,14 @@ fn main() -> ! {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if connection && tick && (time % 3000) == 0 {
|
||||||
|
client.publish("Urukul/Channel1/Switch", mqtt_mux
|
||||||
|
.get_switch_status_message(1)
|
||||||
|
.unwrap()
|
||||||
|
.as_bytes(), QoS::AtMostOnce, &[])
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
// Reset tick flag
|
// Reset tick flag
|
||||||
tick = false;
|
tick = false;
|
||||||
}
|
}
|
||||||
|
|
161
src/mqtt_mux.rs
161
src/mqtt_mux.rs
|
@ -1,3 +1,4 @@
|
||||||
|
use log::info;
|
||||||
use nom::IResult;
|
use nom::IResult;
|
||||||
use nom::combinator::{value, map, map_res, opt, all_consuming};
|
use nom::combinator::{value, map, map_res, opt, all_consuming};
|
||||||
use nom::sequence::{terminated, preceded, pair};
|
use nom::sequence::{terminated, preceded, pair};
|
||||||
|
@ -6,9 +7,7 @@ use nom::character::complete::digit1;
|
||||||
use nom::character::is_space;
|
use nom::character::is_space;
|
||||||
use nom::branch::{permutation, alt};
|
use nom::branch::{permutation, alt};
|
||||||
use nom::number::complete::{float, double};
|
use nom::number::complete::{float, double};
|
||||||
use heapless::String;
|
|
||||||
use heapless::consts::*;
|
|
||||||
use ryu;
|
|
||||||
use embedded_hal::blocking::spi::Transfer;
|
use embedded_hal::blocking::spi::Transfer;
|
||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
use crate::urukul::ClockSource as UrukulClockSource;
|
use crate::urukul::ClockSource as UrukulClockSource;
|
||||||
|
@ -36,7 +35,6 @@ pub enum MqttTopic {
|
||||||
// Such that Urukul accepts the enum directly
|
// Such that Urukul accepts the enum directly
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum MqttCommand {
|
pub enum MqttCommand {
|
||||||
ProcessError,
|
|
||||||
Reset,
|
Reset,
|
||||||
Switch(u8, bool),
|
Switch(u8, bool),
|
||||||
Attenuation(u8, f32),
|
Attenuation(u8, f32),
|
||||||
|
@ -53,158 +51,24 @@ pub enum MqttCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MqttMux<SPI> {
|
pub struct MqttMux<SPI> {
|
||||||
urukul: Urukul<SPI>,
|
urukul: Urukul<SPI>
|
||||||
yet_to_respond: Option<MqttCommand>,
|
|
||||||
str_builder: String<U128>,
|
|
||||||
float_buffer: ryu::Buffer,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<SPI, E> MqttMux<SPI> where SPI: Transfer<u8, Error = E> {
|
impl<SPI, E> MqttMux<SPI> where SPI: Transfer<u8, Error = E> {
|
||||||
pub fn new(urukul: Urukul<SPI>) -> Self {
|
pub fn new(urukul: Urukul<SPI>) -> Self {
|
||||||
MqttMux {
|
MqttMux {
|
||||||
urukul: urukul,
|
urukul
|
||||||
yet_to_respond: None,
|
|
||||||
str_builder: String::new(),
|
|
||||||
float_buffer: ryu::Buffer::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instead of using a return type, the result of processing the command is stored internally
|
pub fn process_mqtt(&mut self, topic: &str, message: &[u8]) -> Result<(), Error<E>> {
|
||||||
// Invoke process_mqtt_egress to get a response after invoking ingress handler
|
let header = self.parse_topic(topic)
|
||||||
pub fn process_mqtt_ingress(&mut self, topic: &str, message: &[u8]) {
|
.map_err(|_| Error::MqttTopicError)?;
|
||||||
let topic = match self.parse_topic(topic) {
|
info!("Parsed command topic: {:?}", header);
|
||||||
Ok(t) => t,
|
let (_, command) = self.parse_message(header, message)
|
||||||
Err(_) => {
|
.map_err(|_| Error::MqttCommandError)?;
|
||||||
self.yet_to_respond = Some(MqttCommand::ProcessError);
|
info!("Parsed comamnd message: {:?}", command);
|
||||||
return;
|
self.execute(command)
|
||||||
}
|
|
||||||
};
|
|
||||||
let command = match self.parse_message(topic, message) {
|
|
||||||
Ok((_, cmd)) => cmd,
|
|
||||||
Err(_) => {
|
|
||||||
self.yet_to_respond = Some(MqttCommand::ProcessError);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.yet_to_respond = match self.execute(command.clone()) {
|
|
||||||
Err(_) => Some(MqttCommand::ProcessError),
|
|
||||||
Ok(()) => Some(command)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Be sure to call egress function after each ingress.
|
|
||||||
// Otherwise, response will be lost if successive valid MQTT messages were captured
|
|
||||||
// without calling egress in between
|
|
||||||
pub fn process_mqtt_egress(&mut self) -> Result<Option<(&str, String<U64>)>, Error<E>> {
|
|
||||||
// Remove previously executed command, and process it afterwards
|
|
||||||
let prev_cmd = self.yet_to_respond.clone();
|
|
||||||
self.yet_to_respond = None;
|
|
||||||
|
|
||||||
match prev_cmd {
|
|
||||||
Some(cmd) => match cmd {
|
|
||||||
MqttCommand::ProcessError => Ok(
|
|
||||||
Some((
|
|
||||||
"Urukul/Feedback/Error",
|
|
||||||
String::from("Cannot parse the previous command.")
|
|
||||||
))
|
|
||||||
),
|
|
||||||
MqttCommand::Reset => Ok(
|
|
||||||
Some((
|
|
||||||
"Urukul/Feedback/Reset",
|
|
||||||
{
|
|
||||||
String::from(
|
|
||||||
match self.urukul.test() {
|
|
||||||
Ok(0) => "Reset successful.",
|
|
||||||
_ => "Reset error!",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
))
|
|
||||||
),
|
|
||||||
MqttCommand::Switch(ch, _) => Ok(
|
|
||||||
Some((
|
|
||||||
{
|
|
||||||
self.str_builder.clear();
|
|
||||||
self.str_builder.push_str("Urukul/Feedback/Channel")
|
|
||||||
.map_err(|_| Error::StringOutOfSpace)?;
|
|
||||||
self.str_builder.push(char::from_digit(ch.into(), 10).unwrap())
|
|
||||||
.map_err(|_| Error::StringOutOfSpace)?;
|
|
||||||
self.str_builder.push_str("/Switch")
|
|
||||||
.map_err(|_| Error::StringOutOfSpace)?;
|
|
||||||
self.str_builder.as_str()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
String::from(
|
|
||||||
if self.urukul.get_channel_switch_status(ch.into())? {
|
|
||||||
"on"
|
|
||||||
} else {
|
|
||||||
"off"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
))
|
|
||||||
),
|
|
||||||
MqttCommand::Attenuation(ch, _) => Ok(
|
|
||||||
Some((
|
|
||||||
{
|
|
||||||
self.str_builder.clear();
|
|
||||||
self.str_builder.push_str("Urukul/Feedback/Channel")
|
|
||||||
.map_err(|_| Error::StringOutOfSpace)?;
|
|
||||||
self.str_builder.push(char::from_digit(ch.into(), 10).unwrap())
|
|
||||||
.map_err(|_| Error::StringOutOfSpace)?;
|
|
||||||
self.str_builder.push_str("/Attenuation")
|
|
||||||
.map_err(|_| Error::StringOutOfSpace)?;
|
|
||||||
self.str_builder.as_str()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
String::from(
|
|
||||||
self.float_buffer.format_finite(
|
|
||||||
self.urukul.get_channel_attenuation(ch)?
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
))
|
|
||||||
),
|
|
||||||
MqttCommand::SystemClock(ch, _) => Ok(
|
|
||||||
Some((
|
|
||||||
{
|
|
||||||
self.str_builder.clear();
|
|
||||||
self.str_builder.push_str("Urukul/Feedback/Channel")
|
|
||||||
.map_err(|_| Error::StringOutOfSpace)?;
|
|
||||||
self.str_builder.push(char::from_digit(ch.into(), 10).unwrap())
|
|
||||||
.map_err(|_| Error::StringOutOfSpace)?;
|
|
||||||
self.str_builder.push_str("/SystemClock")
|
|
||||||
.map_err(|_| Error::StringOutOfSpace)?;
|
|
||||||
self.str_builder.as_str()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
let mut message_str = String::from(
|
|
||||||
self.float_buffer.format_finite(
|
|
||||||
self.urukul.get_channel_sys_clk(ch)?
|
|
||||||
)
|
|
||||||
);
|
|
||||||
message_str.push_str(" Hz")
|
|
||||||
.map_err(|_| Error::StringOutOfSpace)?;
|
|
||||||
message_str
|
|
||||||
}
|
|
||||||
))
|
|
||||||
),
|
|
||||||
MqttCommand::Profile(_) => Ok(
|
|
||||||
Some((
|
|
||||||
"Urukul/Feedback/Profile",
|
|
||||||
{
|
|
||||||
let mut message_str = String::new();
|
|
||||||
let prof = self.urukul.get_profile()?;
|
|
||||||
message_str.push(char::from_digit(prof.into(), 10).unwrap())
|
|
||||||
.map_err(|_| Error::StringOutOfSpace)?;
|
|
||||||
message_str
|
|
||||||
}
|
|
||||||
))
|
|
||||||
),
|
|
||||||
_ => Ok(Some(("Urukul/Feedback/Unimplemented", String::from("test")))),
|
|
||||||
},
|
|
||||||
None => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_topic<'a>(&mut self, topic: &'a str) -> Result<MqttTopic, Error<E>> {
|
fn parse_topic<'a>(&mut self, topic: &'a str) -> Result<MqttTopic, Error<E>> {
|
||||||
|
@ -399,7 +263,6 @@ impl<SPI, E> MqttMux<SPI> where SPI: Transfer<u8, Error = E> {
|
||||||
|
|
||||||
fn execute(&mut self, command: MqttCommand) -> Result<(), Error<E>> {
|
fn execute(&mut self, command: MqttCommand) -> Result<(), Error<E>> {
|
||||||
match command {
|
match command {
|
||||||
MqttCommand::ProcessError => Ok(()),
|
|
||||||
MqttCommand::Reset => self.urukul.reset(),
|
MqttCommand::Reset => self.urukul.reset(),
|
||||||
MqttCommand::Switch(ch, state) => self.urukul.set_channel_switch(ch.into(), state),
|
MqttCommand::Switch(ch, state) => self.urukul.set_channel_switch(ch.into(), state),
|
||||||
MqttCommand::Attenuation(ch, ampl) => self.urukul.set_channel_attenuation(ch, ampl),
|
MqttCommand::Attenuation(ch, ampl) => self.urukul.set_channel_attenuation(ch, ampl),
|
||||||
|
|
|
@ -12,7 +12,7 @@ use crate::dds::{ DDS, RAMOperationMode };
|
||||||
/*
|
/*
|
||||||
* Enum for structuring error
|
* Enum for structuring error
|
||||||
*/
|
*/
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub enum Error<E> {
|
pub enum Error<E> {
|
||||||
SPI(E),
|
SPI(E),
|
||||||
CSError,
|
CSError,
|
||||||
|
@ -26,8 +26,6 @@ pub enum Error<E> {
|
||||||
ParameterError,
|
ParameterError,
|
||||||
MqttTopicError,
|
MqttTopicError,
|
||||||
MqttCommandError,
|
MqttCommandError,
|
||||||
VectorOutOfSpace,
|
|
||||||
StringOutOfSpace,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -235,10 +233,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_channel_attenuation(&mut self, channel: u8) -> Result<f32, Error<E>> {
|
|
||||||
self.attenuator.get_channel_attenuation(channel)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_channel_attenuation(&mut self, channel: u8, attenuation: f32) -> Result<(), Error<E>> {
|
pub fn set_channel_attenuation(&mut self, channel: u8, attenuation: f32) -> Result<(), Error<E>> {
|
||||||
if channel >= 4 || attenuation < 0.0 || attenuation > 31.5 {
|
if channel >= 4 || attenuation < 0.0 || attenuation > 31.5 {
|
||||||
return Err(Error::ParameterError);
|
return Err(Error::ParameterError);
|
||||||
|
@ -246,10 +240,6 @@ where
|
||||||
self.attenuator.set_channel_attenuation(channel, attenuation)
|
self.attenuator.set_channel_attenuation(channel, attenuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_profile(&mut self) -> Result<u8, Error<E>> {
|
|
||||||
Ok(self.config_register.get_configuration(CFGMask::PROFILE))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_profile(&mut self, profile: u8) -> Result<(), Error<E>> {
|
pub fn set_profile(&mut self, profile: u8) -> Result<(), Error<E>> {
|
||||||
if profile >= 8 {
|
if profile >= 8 {
|
||||||
return Err(Error::ParameterError);
|
return Err(Error::ParameterError);
|
||||||
|
@ -302,10 +292,6 @@ where
|
||||||
self.dds[usize::from(channel)].set_sys_clk_frequency(f_sys_clk).map(|_| ())
|
self.dds[usize::from(channel)].set_sys_clk_frequency(f_sys_clk).map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_channel_sys_clk(&mut self, channel: u8) -> Result<f64, Error<E>> {
|
|
||||||
Ok(self.dds[usize::from(channel)].get_f_sys_clk())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Multi-dds channel functions
|
// Multi-dds channel functions
|
||||||
// Do not allow reading of DDS registers
|
// Do not allow reading of DDS registers
|
||||||
// Make sure only 1 SPI transaction is compelted per function call
|
// Make sure only 1 SPI transaction is compelted per function call
|
||||||
|
|
Loading…
Reference in New Issue