2021-05-06 18:33:07 +08:00
|
|
|
///! Stabilizer Telemetry Capabilities
|
|
|
|
///!
|
|
|
|
///! # Design
|
|
|
|
///! Telemetry is reported regularly using an MQTT client. All telemetry is reported in SI units
|
|
|
|
///! using standard JSON format.
|
|
|
|
///!
|
|
|
|
///! In order to report ADC/DAC codes generated during the DSP routines, a telemetry buffer is
|
|
|
|
///! employed to track the latest codes. Converting these codes to SI units would result in
|
|
|
|
///! repetitive and unnecessary calculations within the DSP routine, slowing it down and limiting
|
|
|
|
///! sampling frequency. Instead, the raw codes are stored and the telemetry is generated as
|
|
|
|
///! required immediately before transmission. This ensures that any slower computation required
|
|
|
|
///! for unit conversion can be off-loaded to lower priority tasks.
|
2021-05-29 00:34:25 +08:00
|
|
|
use heapless::{String, Vec};
|
2021-05-06 18:33:07 +08:00
|
|
|
use minimq::QoS;
|
2021-05-06 18:35:04 +08:00
|
|
|
use serde::Serialize;
|
2021-04-20 20:12:47 +08:00
|
|
|
|
2021-05-05 22:16:54 +08:00
|
|
|
use super::NetworkReference;
|
2021-07-15 19:28:19 +08:00
|
|
|
use crate::configuration::MQTT_BROKER;
|
|
|
|
use crate::hardware::{adc::AdcCode, afe::Gain, dac::DacCode};
|
2021-05-05 22:16:54 +08:00
|
|
|
|
2021-05-06 18:33:07 +08:00
|
|
|
/// The telemetry client for reporting telemetry data over MQTT.
|
|
|
|
pub struct TelemetryClient<T: Serialize> {
|
2021-05-27 19:42:52 +08:00
|
|
|
mqtt: minimq::Minimq<NetworkReference, 256>,
|
2021-05-29 00:34:25 +08:00
|
|
|
telemetry_topic: String<128>,
|
2021-05-06 18:33:07 +08:00
|
|
|
_telemetry: core::marker::PhantomData<T>,
|
|
|
|
}
|
2021-04-29 21:55:36 +08:00
|
|
|
|
2021-05-06 18:33:07 +08:00
|
|
|
/// The telemetry buffer is used for storing sample values during execution.
|
|
|
|
///
|
|
|
|
/// # Note
|
|
|
|
/// These values can be converted to SI units immediately before reporting to save processing time.
|
|
|
|
/// This allows for the DSP process to continually update the values without incurring significant
|
|
|
|
/// run-time overhead during conversion to SI units.
|
2021-04-29 21:55:36 +08:00
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub struct TelemetryBuffer {
|
2021-05-06 18:33:07 +08:00
|
|
|
/// The latest input sample on ADC0/ADC1.
|
2021-05-07 20:11:25 +08:00
|
|
|
pub adcs: [AdcCode; 2],
|
2021-05-06 18:33:07 +08:00
|
|
|
/// The latest output code on DAC0/DAC1.
|
2021-05-07 19:50:34 +08:00
|
|
|
pub dacs: [DacCode; 2],
|
2021-05-06 18:33:07 +08:00
|
|
|
/// The latest digital input states during processing.
|
2021-04-20 20:12:47 +08:00
|
|
|
pub digital_inputs: [bool; 2],
|
|
|
|
}
|
|
|
|
|
2021-05-06 18:33:07 +08:00
|
|
|
/// The telemetry structure is data that is ultimately reported as telemetry over MQTT.
|
|
|
|
///
|
|
|
|
/// # Note
|
|
|
|
/// This structure should be generated on-demand by the buffer when required to minimize conversion
|
|
|
|
/// overhead.
|
2021-04-29 21:55:36 +08:00
|
|
|
#[derive(Serialize)]
|
|
|
|
pub struct Telemetry {
|
2021-07-15 19:28:19 +08:00
|
|
|
/// Most recent input voltage measurement.
|
2021-07-16 20:25:37 +08:00
|
|
|
pub adcs: [f32; 2],
|
2021-07-15 19:28:19 +08:00
|
|
|
|
|
|
|
/// Most recent output voltage.
|
2021-07-16 20:25:37 +08:00
|
|
|
pub dacs: [f32; 2],
|
2021-07-15 19:28:19 +08:00
|
|
|
|
|
|
|
/// Most recent digital input assertion state.
|
2021-07-16 20:25:37 +08:00
|
|
|
pub digital_inputs: [bool; 2],
|
2021-04-29 21:55:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for TelemetryBuffer {
|
2021-04-20 20:12:47 +08:00
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
2021-05-07 20:11:25 +08:00
|
|
|
adcs: [AdcCode(0), AdcCode(0)],
|
2021-05-07 19:50:34 +08:00
|
|
|
dacs: [DacCode(0), DacCode(0)],
|
2021-04-20 20:12:47 +08:00
|
|
|
digital_inputs: [false, false],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-29 21:55:36 +08:00
|
|
|
|
|
|
|
impl TelemetryBuffer {
|
2021-05-06 18:33:07 +08:00
|
|
|
/// Convert the telemetry buffer to finalized, SI-unit telemetry for reporting.
|
|
|
|
///
|
|
|
|
/// # Args
|
|
|
|
/// * `afe0` - The current AFE configuration for channel 0.
|
|
|
|
/// * `afe1` - The current AFE configuration for channel 1.
|
|
|
|
///
|
|
|
|
/// # Returns
|
|
|
|
/// The finalized telemetry structure that can be serialized and reported.
|
2021-06-04 23:02:01 +08:00
|
|
|
pub fn finalize(self, afe0: Gain, afe1: Gain) -> Telemetry {
|
2021-05-07 19:50:34 +08:00
|
|
|
let in0_volts = Into::<f32>::into(self.adcs[0]) / afe0.as_multiplier();
|
|
|
|
let in1_volts = Into::<f32>::into(self.adcs[1]) / afe1.as_multiplier();
|
2021-04-29 21:55:36 +08:00
|
|
|
|
|
|
|
Telemetry {
|
2021-05-07 19:02:14 +08:00
|
|
|
adcs: [in0_volts, in1_volts],
|
2021-05-07 19:50:34 +08:00
|
|
|
dacs: [self.dacs[0].into(), self.dacs[1].into()],
|
2021-04-29 21:55:36 +08:00
|
|
|
digital_inputs: self.digital_inputs,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-05-05 22:16:54 +08:00
|
|
|
|
|
|
|
impl<T: Serialize> TelemetryClient<T> {
|
2021-05-06 18:33:07 +08:00
|
|
|
/// Construct a new telemetry client.
|
|
|
|
///
|
|
|
|
/// # Args
|
|
|
|
/// * `stack` - A reference to the (shared) underlying network stack.
|
|
|
|
/// * `client_id` - The MQTT client ID of the telemetry client.
|
|
|
|
/// * `prefix` - The device prefix to use for MQTT telemetry reporting.
|
|
|
|
///
|
|
|
|
/// # Returns
|
|
|
|
/// A new telemetry client.
|
2021-05-05 22:16:54 +08:00
|
|
|
pub fn new(stack: NetworkReference, client_id: &str, prefix: &str) -> Self {
|
|
|
|
let mqtt =
|
2021-05-27 19:42:52 +08:00
|
|
|
minimq::Minimq::new(MQTT_BROKER.into(), client_id, stack).unwrap();
|
2021-05-05 22:16:54 +08:00
|
|
|
|
2021-05-29 00:34:25 +08:00
|
|
|
let mut telemetry_topic: String<128> = String::from(prefix);
|
2021-05-05 22:16:54 +08:00
|
|
|
telemetry_topic.push_str("/telemetry").unwrap();
|
|
|
|
|
|
|
|
Self {
|
|
|
|
mqtt,
|
|
|
|
telemetry_topic,
|
|
|
|
_telemetry: core::marker::PhantomData::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-06 18:33:07 +08:00
|
|
|
/// Publish telemetry over MQTT
|
|
|
|
///
|
|
|
|
/// # Note
|
|
|
|
/// Telemetry is reported in a "best-effort" fashion. Failure to transmit telemetry will cause
|
|
|
|
/// it to be silently dropped.
|
|
|
|
///
|
|
|
|
/// # Args
|
|
|
|
/// * `telemetry` - The telemetry to report
|
2021-05-05 22:16:54 +08:00
|
|
|
pub fn publish(&mut self, telemetry: &T) {
|
2021-05-29 00:34:25 +08:00
|
|
|
let telemetry: Vec<u8, 256> =
|
2021-05-05 22:16:54 +08:00
|
|
|
serde_json_core::to_vec(telemetry).unwrap();
|
|
|
|
self.mqtt
|
2021-05-27 19:42:52 +08:00
|
|
|
.client
|
2021-05-05 22:16:54 +08:00
|
|
|
.publish(&self.telemetry_topic, &telemetry, QoS::AtMostOnce, &[])
|
|
|
|
.ok();
|
|
|
|
}
|
2021-05-05 22:46:53 +08:00
|
|
|
|
2021-05-06 18:33:07 +08:00
|
|
|
/// Update the telemetry client
|
|
|
|
///
|
|
|
|
/// # Note
|
|
|
|
/// This function is provided to force the underlying MQTT state machine to process incoming
|
|
|
|
/// and outgoing messages. Without this, the client will never connect to the broker. This
|
|
|
|
/// should be called regularly.
|
2021-05-05 22:46:53 +08:00
|
|
|
pub fn update(&mut self) {
|
|
|
|
match self.mqtt.poll(|_client, _topic, _message, _properties| {}) {
|
|
|
|
Err(minimq::Error::Network(
|
|
|
|
smoltcp_nal::NetworkError::NoIpAddress,
|
|
|
|
)) => {}
|
|
|
|
|
|
|
|
Err(error) => log::info!("Unexpected error: {:?}", error),
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
2021-05-05 22:16:54 +08:00
|
|
|
}
|