mqtt: changeable root topic name
This commit is contained in:
parent
95443a2283
commit
b75c83be61
21
src/main.rs
21
src/main.rs
|
@ -18,7 +18,7 @@ use cortex_m;
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use rtic::cyccnt::{Instant, U32Ext};
|
use rtic::cyccnt::{Instant, U32Ext};
|
||||||
|
|
||||||
use heapless::consts;
|
use heapless::{ String, consts, consts::* };
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod bitmask_macro;
|
pub mod bitmask_macro;
|
||||||
|
@ -218,10 +218,10 @@ fn main() -> ! {
|
||||||
let mut urukul = Urukul::new(
|
let mut urukul = Urukul::new(
|
||||||
parts.spi1, parts.spi2, parts.spi3, parts.spi4, parts.spi5, parts.spi6, parts.spi7
|
parts.spi1, parts.spi2, parts.spi3, parts.spi4, parts.spi5, parts.spi6, parts.spi7
|
||||||
);
|
);
|
||||||
|
|
||||||
urukul.reset().unwrap();
|
urukul.reset().unwrap();
|
||||||
|
|
||||||
let mut mqtt_mux = MqttMux::new(urukul);
|
let device_name = "Urukul";
|
||||||
|
let mut mqtt_mux = MqttMux::new(urukul, device_name);
|
||||||
|
|
||||||
// Time unit in ms
|
// Time unit in ms
|
||||||
let mut time: u32 = 0;
|
let mut time: u32 = 0;
|
||||||
|
@ -239,7 +239,7 @@ fn main() -> ! {
|
||||||
|
|
||||||
let mut client = MqttClient::<consts::U256, _>::new(
|
let mut client = MqttClient::<consts::U256, _>::new(
|
||||||
IpAddr::V4(Ipv4Addr::new(192, 168, 1, 125)),
|
IpAddr::V4(Ipv4Addr::new(192, 168, 1, 125)),
|
||||||
"Urukul",
|
device_name,
|
||||||
tcp_stack,
|
tcp_stack,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -276,18 +276,19 @@ fn main() -> ! {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Process MQTT response messages about Urukul
|
// Process MQTT response messages about Urukul
|
||||||
match mqtt_mux.process_mqtt_egress().unwrap() {
|
for (topic, message) in mqtt_mux.process_mqtt_egress().unwrap() {
|
||||||
Some((topic, message)) => client.publish(
|
client.publish(
|
||||||
topic,
|
topic.as_str(),
|
||||||
message.as_bytes(),
|
message.as_bytes(),
|
||||||
QoS::AtMostOnce,
|
QoS::AtMostOnce,
|
||||||
&[]
|
&[]
|
||||||
).unwrap(),
|
).unwrap();
|
||||||
None => {},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if connection && !has_subscribed && tick {
|
if connection && !has_subscribed && tick {
|
||||||
match client.subscribe("Urukul/Control/#", &[]) {
|
let mut str_builder: String<U128> = String::from(device_name);
|
||||||
|
str_builder.push_str("/Control/#").unwrap();
|
||||||
|
match client.subscribe(str_builder.as_str(), &[]) {
|
||||||
Ok(()) => has_subscribed = true,
|
Ok(()) => has_subscribed = true,
|
||||||
Err(minimq::Error::NotReady) => {},
|
Err(minimq::Error::NotReady) => {},
|
||||||
_e => {},
|
_e => {},
|
||||||
|
|
112
src/mqtt_mux.rs
112
src/mqtt_mux.rs
|
@ -6,8 +6,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::{ Vec, String, consts::* };
|
||||||
use heapless::consts::*;
|
|
||||||
use ryu;
|
use ryu;
|
||||||
use embedded_hal::blocking::spi::Transfer;
|
use embedded_hal::blocking::spi::Transfer;
|
||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
|
@ -52,19 +51,19 @@ pub enum MqttCommand {
|
||||||
Profile(u8)
|
Profile(u8)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MqttMux<SPI> {
|
pub struct MqttMux<'s, SPI> {
|
||||||
urukul: Urukul<SPI>,
|
urukul: Urukul<SPI>,
|
||||||
yet_to_respond: Option<MqttCommand>,
|
yet_to_respond: Option<MqttCommand>,
|
||||||
str_builder: String<U128>,
|
name: &'s str,
|
||||||
float_buffer: ryu::Buffer,
|
float_buffer: ryu::Buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<SPI, E> MqttMux<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>) -> Self {
|
pub fn new(urukul: Urukul<SPI>, name: &'s str) -> Self {
|
||||||
MqttMux {
|
MqttMux {
|
||||||
urukul: urukul,
|
urukul: urukul,
|
||||||
yet_to_respond: None,
|
yet_to_respond: None,
|
||||||
str_builder: String::new(),
|
name: name,
|
||||||
float_buffer: ryu::Buffer::new(),
|
float_buffer: ryu::Buffer::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,43 +94,56 @@ impl<SPI, E> MqttMux<SPI> where SPI: Transfer<u8, Error = E> {
|
||||||
// Be sure to call egress function after each ingress.
|
// Be sure to call egress function after each ingress.
|
||||||
// Otherwise, response will be lost if successive valid MQTT messages were captured
|
// Otherwise, response will be lost if successive valid MQTT messages were captured
|
||||||
// without calling egress in between
|
// without calling egress in between
|
||||||
pub fn process_mqtt_egress(&mut self) -> Result<Option<(&str, String<U64>)>, Error<E>> {
|
pub fn process_mqtt_egress(&mut self) -> Result<Vec<(String<U128>, String<U64>), U4>, Error<E>> {
|
||||||
// Remove previously executed command, and process it afterwards
|
// Remove previously executed command, and process it afterwards
|
||||||
let prev_cmd = self.yet_to_respond.clone();
|
let prev_cmd = self.yet_to_respond.clone();
|
||||||
self.yet_to_respond = None;
|
self.yet_to_respond = None;
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
|
||||||
match prev_cmd {
|
match prev_cmd {
|
||||||
Some(cmd) => match cmd {
|
Some(cmd) => match cmd {
|
||||||
MqttCommand::ProcessError => Ok(
|
MqttCommand::ProcessError => {
|
||||||
Some((
|
vec.push((
|
||||||
"Urukul/Feedback/Error",
|
|
||||||
String::from("Cannot parse the previous command.")
|
|
||||||
))
|
|
||||||
),
|
|
||||||
MqttCommand::Reset => Ok(
|
|
||||||
Some((
|
|
||||||
"Urukul/Feedback/Reset",
|
|
||||||
{
|
{
|
||||||
|
let mut topic_string = String::from(self.name);
|
||||||
|
topic_string.push_str("/Feedback/Error")
|
||||||
|
.map_err(|_| Error::StringOutOfSpace)?;
|
||||||
|
topic_string
|
||||||
|
},
|
||||||
|
String::from("Cannot parse the previous command.")
|
||||||
|
)).map_err(|_| Error::VectorOutOfSpace)?;
|
||||||
|
Ok(vec)
|
||||||
|
}
|
||||||
|
|
||||||
|
MqttCommand::Reset => {
|
||||||
|
vec.push((
|
||||||
|
{
|
||||||
|
let mut topic_string = String::from(self.name);
|
||||||
|
topic_string.push_str("/Feedback/Reset")
|
||||||
|
.map_err(|_| Error::StringOutOfSpace)?;
|
||||||
|
topic_string
|
||||||
|
},
|
||||||
String::from(
|
String::from(
|
||||||
match self.urukul.test() {
|
match self.urukul.test() {
|
||||||
Ok(0) => "Reset successful.",
|
Ok(0) => "Reset successful.",
|
||||||
_ => "Reset error!",
|
_ => "Reset error!",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
)).map_err(|_| Error::VectorOutOfSpace)?;
|
||||||
|
Ok(vec)
|
||||||
}
|
}
|
||||||
))
|
|
||||||
),
|
MqttCommand::Switch(ch, _) => {
|
||||||
MqttCommand::Switch(ch, _) => Ok(
|
vec.push((
|
||||||
Some((
|
|
||||||
{
|
{
|
||||||
self.str_builder.clear();
|
let mut topic_string = String::from(self.name);
|
||||||
self.str_builder.push_str("Urukul/Feedback/Channel")
|
topic_string.push_str("/Feedback/Channel")
|
||||||
.map_err(|_| Error::StringOutOfSpace)?;
|
.map_err(|_| Error::StringOutOfSpace)?;
|
||||||
self.str_builder.push(char::from_digit(ch.into(), 10).unwrap())
|
topic_string.push(char::from_digit(ch.into(), 10).unwrap())
|
||||||
.map_err(|_| Error::StringOutOfSpace)?;
|
.map_err(|_| Error::StringOutOfSpace)?;
|
||||||
self.str_builder.push_str("/Switch")
|
topic_string.push_str("/Switch")
|
||||||
.map_err(|_| Error::StringOutOfSpace)?;
|
.map_err(|_| Error::StringOutOfSpace)?;
|
||||||
self.str_builder.as_str()
|
topic_string
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
String::from(
|
String::from(
|
||||||
|
@ -142,19 +154,21 @@ impl<SPI, E> MqttMux<SPI> where SPI: Transfer<u8, Error = E> {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
))
|
)).map_err(|_| Error::VectorOutOfSpace)?;
|
||||||
),
|
Ok(vec)
|
||||||
MqttCommand::Attenuation(ch, _) => Ok(
|
}
|
||||||
Some((
|
|
||||||
|
MqttCommand::Attenuation(ch, _) => {
|
||||||
|
vec.push((
|
||||||
{
|
{
|
||||||
self.str_builder.clear();
|
let mut topic_string = String::from(self.name);
|
||||||
self.str_builder.push_str("Urukul/Feedback/Channel")
|
topic_string.push_str("/Feedback/Channel")
|
||||||
.map_err(|_| Error::StringOutOfSpace)?;
|
.map_err(|_| Error::StringOutOfSpace)?;
|
||||||
self.str_builder.push(char::from_digit(ch.into(), 10).unwrap())
|
topic_string.push(char::from_digit(ch.into(), 10).unwrap())
|
||||||
.map_err(|_| Error::StringOutOfSpace)?;
|
.map_err(|_| Error::StringOutOfSpace)?;
|
||||||
self.str_builder.push_str("/Attenuation")
|
topic_string.push_str("/Attenuation")
|
||||||
.map_err(|_| Error::StringOutOfSpace)?;
|
.map_err(|_| Error::StringOutOfSpace)?;
|
||||||
self.str_builder.as_str()
|
topic_string
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
String::from(
|
String::from(
|
||||||
|
@ -168,14 +182,14 @@ impl<SPI, E> MqttMux<SPI> where SPI: Transfer<u8, Error = E> {
|
||||||
MqttCommand::SystemClock(ch, _) => Ok(
|
MqttCommand::SystemClock(ch, _) => Ok(
|
||||||
Some((
|
Some((
|
||||||
{
|
{
|
||||||
self.str_builder.clear();
|
let mut topic_string = String::from(self.name);
|
||||||
self.str_builder.push_str("Urukul/Feedback/Channel")
|
topic_string.push_str("/Feedback/Channel")
|
||||||
.map_err(|_| Error::StringOutOfSpace)?;
|
.map_err(|_| Error::StringOutOfSpace)?;
|
||||||
self.str_builder.push(char::from_digit(ch.into(), 10).unwrap())
|
topic_string.push(char::from_digit(ch.into(), 10).unwrap())
|
||||||
.map_err(|_| Error::StringOutOfSpace)?;
|
.map_err(|_| Error::StringOutOfSpace)?;
|
||||||
self.str_builder.push_str("/SystemClock")
|
topic_string.push_str("/SystemClock")
|
||||||
.map_err(|_| Error::StringOutOfSpace)?;
|
.map_err(|_| Error::StringOutOfSpace)?;
|
||||||
self.str_builder.as_str()
|
topic_string
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
let mut message_str = String::from(
|
let mut message_str = String::from(
|
||||||
|
@ -213,10 +227,20 @@ impl<SPI, E> MqttMux<SPI> where SPI: Transfer<u8, Error = E> {
|
||||||
let mut channel :u8 = 0;
|
let mut channel :u8 = 0;
|
||||||
let mut profile :u8 = 0;
|
let mut profile :u8 = 0;
|
||||||
|
|
||||||
// Verify that the topic must start with Urukul/Control/ or /Urukul/Control/
|
// Verify that the topic starts with <name>/Control/ or /<name>/Control/
|
||||||
let mut header = topic.strip_prefix("/Urukul/Control/")
|
let mut header = {
|
||||||
.or_else(|| topic.strip_prefix("Urukul/Control/"))
|
let mut topic_builder_with_slash: String<U128> = String::from("/");
|
||||||
.ok_or(Error::MqttCommandError)?;
|
topic_builder_with_slash.push_str(self.name)
|
||||||
|
.map_err(|_| Error::StringOutOfSpace)?;
|
||||||
|
topic_builder_with_slash.push_str("/Control/")
|
||||||
|
.map_err(|_| Error::StringOutOfSpace)?;
|
||||||
|
let topic_builder: &str = topic_builder_with_slash.as_str()
|
||||||
|
.strip_prefix("/")
|
||||||
|
.ok_or(Error::StringOutOfSpace)?;
|
||||||
|
topic.strip_prefix(topic_builder_with_slash.as_str())
|
||||||
|
.or_else(|| topic.strip_prefix(topic_builder))
|
||||||
|
.ok_or(Error::MqttCommandError)?
|
||||||
|
};
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match header {
|
match header {
|
||||||
|
|
Loading…
Reference in New Issue