Updating Stabilizer to remove compile-time config parameters
This commit is contained in:
parent
251bebdc6d
commit
fb580edcee
|
@ -17,7 +17,6 @@ nav_order: 2
|
||||||
|
|
||||||
There are a number of steps that must be completed when first getting started with Stabilizer.
|
There are a number of steps that must be completed when first getting started with Stabilizer.
|
||||||
1. Update the Stabilizer Application
|
1. Update the Stabilizer Application
|
||||||
1. Set parameters in the firmware source code, such as IP addresses and sampling rate.
|
|
||||||
1. Build the application by compiling the source code.
|
1. Build the application by compiling the source code.
|
||||||
1. Upload the application and programming it onto the device.
|
1. Upload the application and programming it onto the device.
|
||||||
1. Set up MQTT for telemetry and configuration.
|
1. Set up MQTT for telemetry and configuration.
|
||||||
|
@ -31,23 +30,6 @@ Firmware is compiled and loaded onto Stabilizer to program a specific applicatio
|
||||||
After receiving the Stabilizer hardware, you will need to flash firmware onto the device to use your
|
After receiving the Stabilizer hardware, you will need to flash firmware onto the device to use your
|
||||||
desired application.
|
desired application.
|
||||||
|
|
||||||
## Configuring Firmware
|
|
||||||
|
|
||||||
Stabilizer firmware contains compile-time parameters that may need to be changed based on
|
|
||||||
application requirements. Some examples of parameters that may require configuraton:
|
|
||||||
* Sampling interval.
|
|
||||||
* Batch size.
|
|
||||||
* MQTT Broker IP address
|
|
||||||
|
|
||||||
Parameters are configured by editing the file `src/configuration.rs`.
|
|
||||||
|
|
||||||
Refer to the [documentation]({{site.baseurl}}/firmware/stabilizer/configuration/index.html) for more
|
|
||||||
information on parameters.
|
|
||||||
|
|
||||||
When these parameters are updated, firmware must be built and flashed onto Stabilizer before they
|
|
||||||
take effect.
|
|
||||||
|
|
||||||
|
|
||||||
## Building Firmware
|
## Building Firmware
|
||||||
|
|
||||||
1. Clone or download [stabilizer](https://github.com/quartiq/stabilizer)
|
1. Clone or download [stabilizer](https://github.com/quartiq/stabilizer)
|
||||||
|
@ -60,9 +42,9 @@ git clone https://github.com/quartiq/stabilizer
|
||||||
```bash
|
```bash
|
||||||
rustup target add thumbv7em-none-eabihf
|
rustup target add thumbv7em-none-eabihf
|
||||||
```
|
```
|
||||||
1. Build Firmware
|
1. Build Firmware with an optionally-specified MQTT broker IP.
|
||||||
```bash
|
```bash
|
||||||
cargo build --release
|
BROKER="10.34.16.10" cargo build --release
|
||||||
```
|
```
|
||||||
|
|
||||||
## Uploading Firmware
|
## Uploading Firmware
|
||||||
|
|
|
@ -29,10 +29,7 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
use core::{
|
use core::sync::atomic::{fence, Ordering};
|
||||||
convert::TryInto,
|
|
||||||
sync::atomic::{fence, Ordering},
|
|
||||||
};
|
|
||||||
|
|
||||||
use mutex_trait::prelude::*;
|
use mutex_trait::prelude::*;
|
||||||
|
|
||||||
|
@ -43,7 +40,7 @@ use stabilizer::{
|
||||||
adc::{Adc0Input, Adc1Input, AdcCode},
|
adc::{Adc0Input, Adc1Input, AdcCode},
|
||||||
afe::Gain,
|
afe::Gain,
|
||||||
dac::{Dac0Output, Dac1Output, DacCode},
|
dac::{Dac0Output, Dac1Output, DacCode},
|
||||||
design_parameters::SAMPLE_BUFFER_SIZE,
|
design_parameters::DEFAULT_MQTT_BROKER,
|
||||||
embedded_hal::digital::v2::InputPin,
|
embedded_hal::digital::v2::InputPin,
|
||||||
hal,
|
hal,
|
||||||
signal_generator::{self, SignalGenerator},
|
signal_generator::{self, SignalGenerator},
|
||||||
|
@ -64,6 +61,13 @@ const SCALE: f32 = i16::MAX as _;
|
||||||
// The number of cascaded IIR biquads per channel. Select 1 or 2!
|
// The number of cascaded IIR biquads per channel. Select 1 or 2!
|
||||||
const IIR_CASCADE_LENGTH: usize = 1;
|
const IIR_CASCADE_LENGTH: usize = 1;
|
||||||
|
|
||||||
|
// The number of samples in each batch process
|
||||||
|
const SAMPLE_BUFFER_SIZE: usize = 8;
|
||||||
|
|
||||||
|
// The logarithm of the number of 100MHz timer ticks between each sample. With a value of 2^7 =
|
||||||
|
// 128, there is 1.28uS per sample, corresponding to a sampling frequency of 781.25 KHz.
|
||||||
|
const SAMPLE_TICKS_LOG2: u8 = 7;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Miniconf)]
|
#[derive(Clone, Copy, Debug, Deserialize, Miniconf)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
/// Configure the Analog Front End (AFE) gain.
|
/// Configure the Analog Front End (AFE) gain.
|
||||||
|
@ -183,8 +187,12 @@ const APP: () = {
|
||||||
#[init(spawn=[telemetry, settings_update, ethernet_link])]
|
#[init(spawn=[telemetry, settings_update, ethernet_link])]
|
||||||
fn init(c: init::Context) -> init::LateResources {
|
fn init(c: init::Context) -> init::LateResources {
|
||||||
// Configure the microcontroller
|
// Configure the microcontroller
|
||||||
let (mut stabilizer, _pounder) =
|
let (mut stabilizer, _pounder) = hardware::setup::setup(
|
||||||
hardware::setup::setup(c.core, c.device);
|
c.core,
|
||||||
|
c.device,
|
||||||
|
SAMPLE_BUFFER_SIZE,
|
||||||
|
1 << SAMPLE_TICKS_LOG2,
|
||||||
|
);
|
||||||
|
|
||||||
let mut network = NetworkUsers::new(
|
let mut network = NetworkUsers::new(
|
||||||
stabilizer.net.stack,
|
stabilizer.net.stack,
|
||||||
|
@ -192,6 +200,9 @@ const APP: () = {
|
||||||
stabilizer.cycle_counter,
|
stabilizer.cycle_counter,
|
||||||
env!("CARGO_BIN_NAME"),
|
env!("CARGO_BIN_NAME"),
|
||||||
stabilizer.net.mac_address,
|
stabilizer.net.mac_address,
|
||||||
|
option_env!("BROKER")
|
||||||
|
.and_then(|data| data.parse().ok())
|
||||||
|
.unwrap_or(DEFAULT_MQTT_BROKER.into()),
|
||||||
);
|
);
|
||||||
|
|
||||||
let generator = network.configure_streaming(
|
let generator = network.configure_streaming(
|
||||||
|
@ -228,10 +239,14 @@ const APP: () = {
|
||||||
settings,
|
settings,
|
||||||
signal_generator: [
|
signal_generator: [
|
||||||
SignalGenerator::new(
|
SignalGenerator::new(
|
||||||
settings.signal_generator[0].try_into().unwrap(),
|
settings.signal_generator[0]
|
||||||
|
.try_into_config(SAMPLE_TICKS_LOG2)
|
||||||
|
.unwrap(),
|
||||||
),
|
),
|
||||||
SignalGenerator::new(
|
SignalGenerator::new(
|
||||||
settings.signal_generator[1].try_into().unwrap(),
|
settings.signal_generator[1]
|
||||||
|
.try_into_config(SAMPLE_TICKS_LOG2)
|
||||||
|
.unwrap(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
@ -366,7 +381,7 @@ const APP: () = {
|
||||||
|
|
||||||
// Update the signal generators
|
// Update the signal generators
|
||||||
for (i, &config) in settings.signal_generator.iter().enumerate() {
|
for (i, &config) in settings.signal_generator.iter().enumerate() {
|
||||||
match config.try_into() {
|
match config.try_into_config(SAMPLE_TICKS_LOG2) {
|
||||||
Ok(config) => {
|
Ok(config) => {
|
||||||
c.resources
|
c.resources
|
||||||
.signal_generator
|
.signal_generator
|
||||||
|
|
|
@ -37,13 +37,12 @@ use mutex_trait::prelude::*;
|
||||||
|
|
||||||
use dsp::{Accu, Complex, ComplexExt, Lockin, RPLL};
|
use dsp::{Accu, Complex, ComplexExt, Lockin, RPLL};
|
||||||
use stabilizer::{
|
use stabilizer::{
|
||||||
configuration,
|
|
||||||
hardware::{
|
hardware::{
|
||||||
self,
|
self,
|
||||||
adc::{Adc0Input, Adc1Input, AdcCode},
|
adc::{Adc0Input, Adc1Input, AdcCode},
|
||||||
afe::Gain,
|
afe::Gain,
|
||||||
dac::{Dac0Output, Dac1Output, DacCode},
|
dac::{Dac0Output, Dac1Output, DacCode},
|
||||||
design_parameters::SAMPLE_BUFFER_SIZE,
|
design_parameters::DEFAULT_MQTT_BROKER,
|
||||||
embedded_hal::digital::v2::InputPin,
|
embedded_hal::digital::v2::InputPin,
|
||||||
hal,
|
hal,
|
||||||
input_stamper::InputStamper,
|
input_stamper::InputStamper,
|
||||||
|
@ -60,6 +59,15 @@ use stabilizer::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The logarithm of the number of samples in each batch process. This corresponds with 2^3 samples
|
||||||
|
// per batch = 8 samples
|
||||||
|
const SAMPLE_BUFFER_SIZE_LOG2: u8 = 3;
|
||||||
|
|
||||||
|
// The logarithm of the number of 100MHz timer ticks between each sample. This corresponds with a
|
||||||
|
// sampling period of 2^7 = 128 ticks. At 100MHz, 10ns per tick, this corresponds to a sampling
|
||||||
|
// period of 1.28 uS or 781.25 KHz.
|
||||||
|
const ADC_SAMPLE_TICKS_LOG2: u8 = 7;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Deserialize, Miniconf)]
|
#[derive(Copy, Clone, Debug, Deserialize, Miniconf)]
|
||||||
enum Conf {
|
enum Conf {
|
||||||
/// Output the lockin magnitude.
|
/// Output the lockin magnitude.
|
||||||
|
@ -220,8 +228,12 @@ const APP: () = {
|
||||||
#[init(spawn=[settings_update, telemetry, ethernet_link])]
|
#[init(spawn=[settings_update, telemetry, ethernet_link])]
|
||||||
fn init(c: init::Context) -> init::LateResources {
|
fn init(c: init::Context) -> init::LateResources {
|
||||||
// Configure the microcontroller
|
// Configure the microcontroller
|
||||||
let (mut stabilizer, _pounder) =
|
let (mut stabilizer, _pounder) = hardware::setup::setup(
|
||||||
hardware::setup::setup(c.core, c.device);
|
c.core,
|
||||||
|
c.device,
|
||||||
|
1 << SAMPLE_BUFFER_SIZE_LOG2,
|
||||||
|
1 << ADC_SAMPLE_TICKS_LOG2,
|
||||||
|
);
|
||||||
|
|
||||||
let mut network = NetworkUsers::new(
|
let mut network = NetworkUsers::new(
|
||||||
stabilizer.net.stack,
|
stabilizer.net.stack,
|
||||||
|
@ -229,19 +241,19 @@ const APP: () = {
|
||||||
stabilizer.cycle_counter,
|
stabilizer.cycle_counter,
|
||||||
env!("CARGO_BIN_NAME"),
|
env!("CARGO_BIN_NAME"),
|
||||||
stabilizer.net.mac_address,
|
stabilizer.net.mac_address,
|
||||||
|
option_env!("BROKER")
|
||||||
|
.and_then(|data| data.parse().ok())
|
||||||
|
.unwrap_or(DEFAULT_MQTT_BROKER.into()),
|
||||||
);
|
);
|
||||||
|
|
||||||
let generator = network.configure_streaming(
|
let generator = network.configure_streaming(
|
||||||
StreamFormat::AdcDacData,
|
StreamFormat::AdcDacData,
|
||||||
SAMPLE_BUFFER_SIZE as u8,
|
1u8 << SAMPLE_BUFFER_SIZE_LOG2,
|
||||||
);
|
);
|
||||||
|
|
||||||
let settings = Settings::default();
|
let settings = Settings::default();
|
||||||
|
|
||||||
let pll = RPLL::new(
|
let pll = RPLL::new(ADC_SAMPLE_TICKS_LOG2 + SAMPLE_BUFFER_SIZE_LOG2);
|
||||||
configuration::ADC_SAMPLE_TICKS_LOG2
|
|
||||||
+ configuration::SAMPLE_BUFFER_SIZE_LOG2,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Spawn a settings and telemetry update for default settings.
|
// Spawn a settings and telemetry update for default settings.
|
||||||
c.spawn.settings_update().unwrap();
|
c.spawn.settings_update().unwrap();
|
||||||
|
@ -267,7 +279,7 @@ const APP: () = {
|
||||||
|
|
||||||
let signal_config = {
|
let signal_config = {
|
||||||
let frequency_tuning_word =
|
let frequency_tuning_word =
|
||||||
(1u64 << (32 - configuration::SAMPLE_BUFFER_SIZE_LOG2)) as u32;
|
(1u64 << (32 - SAMPLE_BUFFER_SIZE_LOG2)) as u32;
|
||||||
|
|
||||||
signal_generator::Config {
|
signal_generator::Config {
|
||||||
// Same frequency as batch size.
|
// Same frequency as batch size.
|
||||||
|
@ -334,18 +346,11 @@ const APP: () = {
|
||||||
settings.pll_tc[0],
|
settings.pll_tc[0],
|
||||||
settings.pll_tc[1],
|
settings.pll_tc[1],
|
||||||
);
|
);
|
||||||
(
|
(pll_phase, (pll_frequency >> SAMPLE_BUFFER_SIZE_LOG2) as i32)
|
||||||
pll_phase,
|
|
||||||
(pll_frequency >> configuration::SAMPLE_BUFFER_SIZE_LOG2)
|
|
||||||
as i32,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
LockinMode::Internal => {
|
LockinMode::Internal => {
|
||||||
// Reference phase and frequency are known.
|
// Reference phase and frequency are known.
|
||||||
(
|
(1i32 << 30, 1i32 << (32 - SAMPLE_BUFFER_SIZE_LOG2))
|
||||||
1i32 << 30,
|
|
||||||
1i32 << (32 - configuration::SAMPLE_BUFFER_SIZE_LOG2),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -399,7 +404,8 @@ const APP: () = {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stream the data.
|
// Stream the data.
|
||||||
const N: usize = SAMPLE_BUFFER_SIZE * core::mem::size_of::<u16>();
|
const N: usize =
|
||||||
|
(1 << SAMPLE_BUFFER_SIZE_LOG2) * core::mem::size_of::<u16>();
|
||||||
generator.add::<_, { N * 4 }>(|buf| {
|
generator.add::<_, { N * 4 }>(|buf| {
|
||||||
for (data, buf) in adc_samples
|
for (data, buf) in adc_samples
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
//! This module contains any compile-time configuration parameters for the Stabilizer firmware.
|
|
||||||
|
|
||||||
/// MQTT broker IPv4 address
|
|
||||||
///
|
|
||||||
/// In the default configuration, the IP address is defined as 10.34.16.10.
|
|
||||||
pub const MQTT_BROKER: [u8; 4] = [10, 34, 16, 10];
|
|
||||||
|
|
||||||
/// Sampling Frequency
|
|
||||||
///
|
|
||||||
/// Define the frequency at which ADCs (and DACs) are sampled at.
|
|
||||||
///
|
|
||||||
/// # Units
|
|
||||||
/// The units of this parameter are specified as a logarithmic number of ticks of the internal
|
|
||||||
/// timer, which runs at 100 MHz.
|
|
||||||
///
|
|
||||||
/// ## Example
|
|
||||||
/// With a value of 7, this corresponds to 2^7 = 128 ticks. Each tick of the 100 MHz timer requires
|
|
||||||
/// 10ns.
|
|
||||||
///
|
|
||||||
/// Sampling Period = 10ns * 128 = 1.28 us
|
|
||||||
/// Sampling Frequency = 781.25 KHz
|
|
||||||
///
|
|
||||||
/// Or more succinctly:
|
|
||||||
/// `F_s = 100 MHz / (2 ^ ADC_SAMPLE_TICKS_LOG2)`
|
|
||||||
pub const ADC_SAMPLE_TICKS_LOG2: u8 = 7;
|
|
||||||
|
|
||||||
/// Sample Batch Sizing
|
|
||||||
///
|
|
||||||
/// The sample batch size defines how many samples are collected before the DSP routines are
|
|
||||||
/// executed.
|
|
||||||
///
|
|
||||||
/// # Note
|
|
||||||
/// Smaller batch sizes result in less input -> output latency, but come at the cost of reduced
|
|
||||||
/// maximum sampling frequency.
|
|
||||||
///
|
|
||||||
/// # Units
|
|
||||||
/// The units of the batch size are specified logarithmically.
|
|
||||||
///
|
|
||||||
/// ## Example
|
|
||||||
/// With a value of 3, the number of samples per batch is 2^3 = 8.
|
|
||||||
pub const SAMPLE_BUFFER_SIZE_LOG2: u8 = 3;
|
|
|
@ -69,7 +69,7 @@ use stm32h7xx_hal as hal;
|
||||||
|
|
||||||
use mutex_trait::Mutex;
|
use mutex_trait::Mutex;
|
||||||
|
|
||||||
use super::design_parameters::{SampleBuffer, SAMPLE_BUFFER_SIZE};
|
use super::design_parameters::{SampleBuffer, MAX_SAMPLE_BUFFER_SIZE};
|
||||||
use super::timers;
|
use super::timers;
|
||||||
|
|
||||||
use hal::dma::{
|
use hal::dma::{
|
||||||
|
@ -142,7 +142,8 @@ static mut SPI_EOT_CLEAR: [u32; 1] = [0x00];
|
||||||
// processed). Note that the contents of AXI SRAM is uninitialized, so the buffer contents on
|
// processed). Note that the contents of AXI SRAM is uninitialized, so the buffer contents on
|
||||||
// startup are undefined. The dimensions are `ADC_BUF[adc_index][ping_pong_index][sample_index]`.
|
// startup are undefined. The dimensions are `ADC_BUF[adc_index][ping_pong_index][sample_index]`.
|
||||||
#[link_section = ".axisram.buffers"]
|
#[link_section = ".axisram.buffers"]
|
||||||
static mut ADC_BUF: [[SampleBuffer; 2]; 2] = [[[0; SAMPLE_BUFFER_SIZE]; 2]; 2];
|
static mut ADC_BUF: [[SampleBuffer; 2]; 2] =
|
||||||
|
[[[0; MAX_SAMPLE_BUFFER_SIZE]; 2]; 2];
|
||||||
|
|
||||||
macro_rules! adc_input {
|
macro_rules! adc_input {
|
||||||
($name:ident, $index:literal, $trigger_stream:ident, $data_stream:ident, $clear_stream:ident,
|
($name:ident, $index:literal, $trigger_stream:ident, $data_stream:ident, $clear_stream:ident,
|
||||||
|
@ -218,7 +219,7 @@ macro_rules! adc_input {
|
||||||
hal::dma::dma::$data_stream<hal::stm32::DMA1>,
|
hal::dma::dma::$data_stream<hal::stm32::DMA1>,
|
||||||
hal::spi::Spi<hal::stm32::$spi, hal::spi::Disabled, u16>,
|
hal::spi::Spi<hal::stm32::$spi, hal::spi::Disabled, u16>,
|
||||||
PeripheralToMemory,
|
PeripheralToMemory,
|
||||||
&'static mut SampleBuffer,
|
&'static mut [u16],
|
||||||
hal::dma::DBTransfer,
|
hal::dma::DBTransfer,
|
||||||
>,
|
>,
|
||||||
trigger_transfer: Transfer<
|
trigger_transfer: Transfer<
|
||||||
|
@ -258,6 +259,7 @@ macro_rules! adc_input {
|
||||||
clear_stream: hal::dma::dma::$clear_stream<hal::stm32::DMA1>,
|
clear_stream: hal::dma::dma::$clear_stream<hal::stm32::DMA1>,
|
||||||
trigger_channel: timers::tim2::$trigger_channel,
|
trigger_channel: timers::tim2::$trigger_channel,
|
||||||
clear_channel: timers::tim3::$clear_channel,
|
clear_channel: timers::tim3::$clear_channel,
|
||||||
|
sample_buffer_size: usize,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// The flag clear DMA transfer always clears the EOT flag in the SPI
|
// The flag clear DMA transfer always clears the EOT flag in the SPI
|
||||||
// peripheral. It has the highest priority to ensure it is completed before the
|
// peripheral. It has the highest priority to ensure it is completed before the
|
||||||
|
@ -357,8 +359,8 @@ macro_rules! adc_input {
|
||||||
spi,
|
spi,
|
||||||
// Note(unsafe): The ADC_BUF[$index] is "owned" by this peripheral.
|
// Note(unsafe): The ADC_BUF[$index] is "owned" by this peripheral.
|
||||||
// It shall not be used anywhere else in the module.
|
// It shall not be used anywhere else in the module.
|
||||||
unsafe { &mut ADC_BUF[$index][0] },
|
unsafe { &mut ADC_BUF[$index][0][..sample_buffer_size] },
|
||||||
unsafe { Some(&mut ADC_BUF[$index][1]) },
|
unsafe { Some(&mut ADC_BUF[$index][1][..sample_buffer_size]) },
|
||||||
data_config,
|
data_config,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -390,8 +392,8 @@ macro_rules! adc_input {
|
||||||
/// NOTE(unsafe): Memory safety and access ordering is not guaranteed
|
/// NOTE(unsafe): Memory safety and access ordering is not guaranteed
|
||||||
/// (see the HAL DMA docs).
|
/// (see the HAL DMA docs).
|
||||||
pub fn with_buffer<F, R>(&mut self, f: F) -> Result<R, DMAError>
|
pub fn with_buffer<F, R>(&mut self, f: F) -> Result<R, DMAError>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut SampleBuffer) -> R,
|
F: FnOnce(&mut &'static mut [u16]) -> R,
|
||||||
{
|
{
|
||||||
unsafe { self.transfer.next_dbm_transfer_with(|buf, _current| f(buf)) }
|
unsafe { self.transfer.next_dbm_transfer_with(|buf, _current| f(buf)) }
|
||||||
}
|
}
|
||||||
|
@ -400,7 +402,7 @@ macro_rules! adc_input {
|
||||||
// This is not actually a Mutex. It only re-uses the semantics and macros of mutex-trait
|
// This is not actually a Mutex. It only re-uses the semantics and macros of mutex-trait
|
||||||
// to reduce rightward drift when jointly calling `with_buffer(f)` on multiple DAC/ADCs.
|
// to reduce rightward drift when jointly calling `with_buffer(f)` on multiple DAC/ADCs.
|
||||||
impl Mutex for $name {
|
impl Mutex for $name {
|
||||||
type Data = SampleBuffer;
|
type Data = &'static mut [u16];
|
||||||
fn lock<R>(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R {
|
fn lock<R>(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R {
|
||||||
self.with_buffer(f).unwrap()
|
self.with_buffer(f).unwrap()
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ use stm32h7xx_hal as hal;
|
||||||
|
|
||||||
use mutex_trait::Mutex;
|
use mutex_trait::Mutex;
|
||||||
|
|
||||||
use super::design_parameters::{SampleBuffer, SAMPLE_BUFFER_SIZE};
|
use super::design_parameters::{SampleBuffer, MAX_SAMPLE_BUFFER_SIZE};
|
||||||
use super::timers;
|
use super::timers;
|
||||||
|
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
|
@ -70,7 +70,8 @@ use hal::dma::{
|
||||||
// processed). Note that the contents of AXI SRAM is uninitialized, so the buffer contents on
|
// processed). Note that the contents of AXI SRAM is uninitialized, so the buffer contents on
|
||||||
// startup are undefined. The dimensions are `ADC_BUF[adc_index][ping_pong_index][sample_index]`.
|
// startup are undefined. The dimensions are `ADC_BUF[adc_index][ping_pong_index][sample_index]`.
|
||||||
#[link_section = ".axisram.buffers"]
|
#[link_section = ".axisram.buffers"]
|
||||||
static mut DAC_BUF: [[SampleBuffer; 2]; 2] = [[[0; SAMPLE_BUFFER_SIZE]; 2]; 2];
|
static mut DAC_BUF: [[SampleBuffer; 2]; 2] =
|
||||||
|
[[[0; MAX_SAMPLE_BUFFER_SIZE]; 2]; 2];
|
||||||
|
|
||||||
/// Custom type for referencing DAC output codes.
|
/// Custom type for referencing DAC output codes.
|
||||||
/// The internal integer is the raw code written to the DAC output register.
|
/// The internal integer is the raw code written to the DAC output register.
|
||||||
|
@ -176,7 +177,7 @@ macro_rules! dac_output {
|
||||||
hal::dma::dma::$data_stream<hal::stm32::DMA1>,
|
hal::dma::dma::$data_stream<hal::stm32::DMA1>,
|
||||||
$spi,
|
$spi,
|
||||||
MemoryToPeripheral,
|
MemoryToPeripheral,
|
||||||
&'static mut SampleBuffer,
|
&'static mut [u16],
|
||||||
hal::dma::DBTransfer,
|
hal::dma::DBTransfer,
|
||||||
>,
|
>,
|
||||||
}
|
}
|
||||||
|
@ -192,6 +193,7 @@ macro_rules! dac_output {
|
||||||
spi: hal::spi::Spi<hal::stm32::$spi, hal::spi::Enabled, u16>,
|
spi: hal::spi::Spi<hal::stm32::$spi, hal::spi::Enabled, u16>,
|
||||||
stream: hal::dma::dma::$data_stream<hal::stm32::DMA1>,
|
stream: hal::dma::dma::$data_stream<hal::stm32::DMA1>,
|
||||||
trigger_channel: timers::tim2::$trigger_channel,
|
trigger_channel: timers::tim2::$trigger_channel,
|
||||||
|
sample_buffer_size: usize,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// Generate DMA events when an output compare of the timer hitting zero (timer roll over)
|
// Generate DMA events when an output compare of the timer hitting zero (timer roll over)
|
||||||
// occurs.
|
// occurs.
|
||||||
|
@ -225,9 +227,13 @@ macro_rules! dac_output {
|
||||||
stream,
|
stream,
|
||||||
$spi::new(trigger_channel, spi),
|
$spi::new(trigger_channel, spi),
|
||||||
// Note(unsafe): This buffer is only used once and provided for the DMA transfer.
|
// Note(unsafe): This buffer is only used once and provided for the DMA transfer.
|
||||||
unsafe { &mut DAC_BUF[$index][0] },
|
unsafe {
|
||||||
|
&mut DAC_BUF[$index][0][..sample_buffer_size]
|
||||||
|
},
|
||||||
// Note(unsafe): This buffer is only used once and provided for the DMA transfer.
|
// Note(unsafe): This buffer is only used once and provided for the DMA transfer.
|
||||||
unsafe { Some(&mut DAC_BUF[$index][1]) },
|
unsafe {
|
||||||
|
Some(&mut DAC_BUF[$index][1][..sample_buffer_size])
|
||||||
|
},
|
||||||
trigger_config,
|
trigger_config,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -246,7 +252,7 @@ macro_rules! dac_output {
|
||||||
/// (see the HAL DMA docs).
|
/// (see the HAL DMA docs).
|
||||||
pub fn with_buffer<F, R>(&mut self, f: F) -> Result<R, DMAError>
|
pub fn with_buffer<F, R>(&mut self, f: F) -> Result<R, DMAError>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut SampleBuffer) -> R,
|
F: FnOnce(&mut &'static mut [u16]) -> R,
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
self.transfer.next_dbm_transfer_with(|buf, _current| f(buf))
|
self.transfer.next_dbm_transfer_with(|buf, _current| f(buf))
|
||||||
|
@ -257,7 +263,7 @@ macro_rules! dac_output {
|
||||||
// This is not actually a Mutex. It only re-uses the semantics and macros of mutex-trait
|
// This is not actually a Mutex. It only re-uses the semantics and macros of mutex-trait
|
||||||
// to reduce rightward drift when jointly calling `with_buffer(f)` on multiple DAC/ADCs.
|
// to reduce rightward drift when jointly calling `with_buffer(f)` on multiple DAC/ADCs.
|
||||||
impl Mutex for $name {
|
impl Mutex for $name {
|
||||||
type Data = SampleBuffer;
|
type Data = &'static mut [u16];
|
||||||
fn lock<R>(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R {
|
fn lock<R>(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R {
|
||||||
self.with_buffer(f).unwrap()
|
self.with_buffer(f).unwrap()
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,13 +40,10 @@ pub const DDS_SYSTEM_CLK: MegaHertz =
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub const DDS_SYNC_CLK_DIV: u8 = 4;
|
pub const DDS_SYNC_CLK_DIV: u8 = 4;
|
||||||
|
|
||||||
// The number of ticks in the ADC sampling timer. The timer runs at 100MHz, so the step size is
|
/// The maximum ADC/DAC sample processing buffer size.
|
||||||
// equal to 10ns per tick.
|
pub const MAX_SAMPLE_BUFFER_SIZE: usize = 32;
|
||||||
pub const ADC_SAMPLE_TICKS: u16 =
|
|
||||||
1 << crate::configuration::ADC_SAMPLE_TICKS_LOG2;
|
|
||||||
|
|
||||||
// The desired ADC sample processing buffer size.
|
pub type SampleBuffer = [u16; MAX_SAMPLE_BUFFER_SIZE];
|
||||||
pub const SAMPLE_BUFFER_SIZE: usize =
|
|
||||||
1 << crate::configuration::SAMPLE_BUFFER_SIZE_LOG2;
|
|
||||||
|
|
||||||
pub type SampleBuffer = [u16; SAMPLE_BUFFER_SIZE];
|
/// The default MQTT broker IP address if unspecified.
|
||||||
|
pub const DEFAULT_MQTT_BROKER: [u8; 4] = [10, 34, 16, 10];
|
||||||
|
|
|
@ -172,9 +172,14 @@ fn load_itcm() {
|
||||||
|
|
||||||
/// Configure the stabilizer hardware for operation.
|
/// Configure the stabilizer hardware for operation.
|
||||||
///
|
///
|
||||||
|
/// # Note
|
||||||
|
/// Refer to [design_parameters::TIMER_FREQUENCY] to determine the frequency of the sampling timer.
|
||||||
|
///
|
||||||
/// # Args
|
/// # Args
|
||||||
/// * `core` - The RTIC core for configuring the cortex-M core of the device.
|
/// * `core` - The RTIC core for configuring the cortex-M core of the device.
|
||||||
/// * `device` - The microcontroller peripherals to be configured.
|
/// * `device` - The microcontroller peripherals to be configured.
|
||||||
|
/// * `sample_buffer_size` - The size of the ADC/DAC sample buffer.
|
||||||
|
/// * `sample_ticks` - The number of timer ticks between each sample.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// (stabilizer, pounder) where `stabilizer` is a `StabilizerDevices` structure containing all
|
/// (stabilizer, pounder) where `stabilizer` is a `StabilizerDevices` structure containing all
|
||||||
|
@ -184,6 +189,8 @@ fn load_itcm() {
|
||||||
pub fn setup(
|
pub fn setup(
|
||||||
mut core: rtic::Peripherals,
|
mut core: rtic::Peripherals,
|
||||||
device: stm32h7xx_hal::stm32::Peripherals,
|
device: stm32h7xx_hal::stm32::Peripherals,
|
||||||
|
sample_buffer_size: usize,
|
||||||
|
sample_ticks: u32,
|
||||||
) -> (StabilizerDevices, Option<PounderDevices>) {
|
) -> (StabilizerDevices, Option<PounderDevices>) {
|
||||||
let pwr = device.PWR.constrain();
|
let pwr = device.PWR.constrain();
|
||||||
let vos = pwr.freeze();
|
let vos = pwr.freeze();
|
||||||
|
@ -295,8 +302,7 @@ pub fn setup(
|
||||||
timer2.set_tick_freq(design_parameters::TIMER_FREQUENCY);
|
timer2.set_tick_freq(design_parameters::TIMER_FREQUENCY);
|
||||||
|
|
||||||
let mut sampling_timer = timers::SamplingTimer::new(timer2);
|
let mut sampling_timer = timers::SamplingTimer::new(timer2);
|
||||||
sampling_timer
|
sampling_timer.set_period_ticks(sample_ticks - 1);
|
||||||
.set_period_ticks((design_parameters::ADC_SAMPLE_TICKS - 1) as u32);
|
|
||||||
|
|
||||||
// The sampling timer is used as the master timer for the shadow-sampling timer. Thus,
|
// The sampling timer is used as the master timer for the shadow-sampling timer. Thus,
|
||||||
// it generates a trigger whenever it is enabled.
|
// it generates a trigger whenever it is enabled.
|
||||||
|
@ -320,8 +326,7 @@ pub fn setup(
|
||||||
|
|
||||||
let mut shadow_sampling_timer =
|
let mut shadow_sampling_timer =
|
||||||
timers::ShadowSamplingTimer::new(timer3);
|
timers::ShadowSamplingTimer::new(timer3);
|
||||||
shadow_sampling_timer
|
shadow_sampling_timer.set_period_ticks(sample_ticks as u16 - 1);
|
||||||
.set_period_ticks(design_parameters::ADC_SAMPLE_TICKS - 1);
|
|
||||||
|
|
||||||
// The shadow sampling timer is a slave-mode timer to the sampling timer. It should
|
// The shadow sampling timer is a slave-mode timer to the sampling timer. It should
|
||||||
// always be in-sync - thus, we configure it to operate in slave mode using "Trigger
|
// always be in-sync - thus, we configure it to operate in slave mode using "Trigger
|
||||||
|
@ -409,6 +414,7 @@ pub fn setup(
|
||||||
dma_streams.2,
|
dma_streams.2,
|
||||||
sampling_timer_channels.ch1,
|
sampling_timer_channels.ch1,
|
||||||
shadow_sampling_timer_channels.ch1,
|
shadow_sampling_timer_channels.ch1,
|
||||||
|
sample_buffer_size,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -452,6 +458,7 @@ pub fn setup(
|
||||||
dma_streams.5,
|
dma_streams.5,
|
||||||
sampling_timer_channels.ch2,
|
sampling_timer_channels.ch2,
|
||||||
shadow_sampling_timer_channels.ch2,
|
shadow_sampling_timer_channels.ch2,
|
||||||
|
sample_buffer_size,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -539,11 +546,13 @@ pub fn setup(
|
||||||
dac0_spi,
|
dac0_spi,
|
||||||
dma_streams.6,
|
dma_streams.6,
|
||||||
sampling_timer_channels.ch3,
|
sampling_timer_channels.ch3,
|
||||||
|
sample_buffer_size,
|
||||||
);
|
);
|
||||||
let dac1 = dac::Dac1Output::new(
|
let dac1 = dac::Dac1Output::new(
|
||||||
dac1_spi,
|
dac1_spi,
|
||||||
dma_streams.7,
|
dma_streams.7,
|
||||||
sampling_timer_channels.ch4,
|
sampling_timer_channels.ch4,
|
||||||
|
sample_buffer_size,
|
||||||
);
|
);
|
||||||
(dac0, dac1)
|
(dac0, dac1)
|
||||||
};
|
};
|
||||||
|
@ -940,8 +949,7 @@ pub fn setup(
|
||||||
let sample_frequency = {
|
let sample_frequency = {
|
||||||
let timer_frequency: hal::time::Hertz =
|
let timer_frequency: hal::time::Hertz =
|
||||||
design_parameters::TIMER_FREQUENCY.into();
|
design_parameters::TIMER_FREQUENCY.into();
|
||||||
timer_frequency.0 as f32
|
timer_frequency.0 as f32 / sample_ticks as f32
|
||||||
/ design_parameters::ADC_SAMPLE_TICKS as f32
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let sample_period = 1.0 / sample_frequency;
|
let sample_period = 1.0 / sample_frequency;
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
configuration::ADC_SAMPLE_TICKS_LOG2, hardware::dac::DacCode,
|
hardware::dac::DacCode, hardware::design_parameters::TIMER_FREQUENCY,
|
||||||
hardware::design_parameters::TIMER_FREQUENCY,
|
|
||||||
};
|
};
|
||||||
use core::convert::{TryFrom, TryInto};
|
use core::convert::TryFrom;
|
||||||
use miniconf::Miniconf;
|
use miniconf::Miniconf;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
@ -32,7 +31,7 @@ pub struct BasicConfig {
|
||||||
|
|
||||||
/// The normalized symmetry of the signal. At 0% symmetry, the duration of the first half oscillation is minimal.
|
/// The normalized symmetry of the signal. At 0% symmetry, the duration of the first half oscillation is minimal.
|
||||||
/// At 25% symmetry, the first half oscillation lasts for 25% of the signal period. For square wave output this
|
/// At 25% symmetry, the first half oscillation lasts for 25% of the signal period. For square wave output this
|
||||||
//// symmetry is the duty cycle.
|
/// symmetry is the duty cycle.
|
||||||
pub symmetry: f32,
|
pub symmetry: f32,
|
||||||
|
|
||||||
/// The amplitude of the output signal in volts.
|
/// The amplitude of the output signal in volts.
|
||||||
|
@ -61,20 +60,25 @@ pub enum Error {
|
||||||
InvalidFrequency,
|
InvalidFrequency,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<BasicConfig> for Config {
|
impl BasicConfig {
|
||||||
type Error = Error;
|
/// Convert configuration into signal generator values.
|
||||||
|
///
|
||||||
fn try_from(config: BasicConfig) -> Result<Config, Error> {
|
/// # Args
|
||||||
let symmetry_complement = 1.0 - config.symmetry;
|
/// * `sample_ticks_log2` - The logarithm of the number of timer sample ticks between each
|
||||||
|
/// sample.
|
||||||
|
pub fn try_into_config(
|
||||||
|
self,
|
||||||
|
sample_ticks_log2: u8,
|
||||||
|
) -> Result<Config, Error> {
|
||||||
|
let symmetry_complement = 1.0 - self.symmetry;
|
||||||
// Validate symmetry
|
// Validate symmetry
|
||||||
if config.symmetry < 0.0 || symmetry_complement < 0.0 {
|
if self.symmetry < 0.0 || symmetry_complement < 0.0 {
|
||||||
return Err(Error::InvalidSymmetry);
|
return Err(Error::InvalidSymmetry);
|
||||||
}
|
}
|
||||||
|
|
||||||
const LSB_PER_HERTZ: f32 = (1u64 << (31 + ADC_SAMPLE_TICKS_LOG2))
|
let lsb_per_hertz: f32 = (1u64 << (31 + sample_ticks_log2)) as f32
|
||||||
as f32
|
|
||||||
/ (TIMER_FREQUENCY.0 * 1_000_000) as f32;
|
/ (TIMER_FREQUENCY.0 * 1_000_000) as f32;
|
||||||
let ftw = config.frequency * LSB_PER_HERTZ;
|
let ftw = self.frequency * lsb_per_hertz;
|
||||||
|
|
||||||
// Validate base frequency tuning word to be below Nyquist.
|
// Validate base frequency tuning word to be below Nyquist.
|
||||||
const NYQUIST: f32 = (1u32 << 31) as _;
|
const NYQUIST: f32 = (1u32 << 31) as _;
|
||||||
|
@ -85,8 +89,8 @@ impl TryFrom<BasicConfig> for Config {
|
||||||
// Calculate the frequency tuning words.
|
// Calculate the frequency tuning words.
|
||||||
// Clip both frequency tuning words to within Nyquist before rounding.
|
// Clip both frequency tuning words to within Nyquist before rounding.
|
||||||
let frequency_tuning_word = [
|
let frequency_tuning_word = [
|
||||||
if config.symmetry * NYQUIST > ftw {
|
if self.symmetry * NYQUIST > ftw {
|
||||||
ftw / config.symmetry
|
ftw / self.symmetry
|
||||||
} else {
|
} else {
|
||||||
NYQUIST
|
NYQUIST
|
||||||
} as u32,
|
} as u32,
|
||||||
|
@ -98,10 +102,10 @@ impl TryFrom<BasicConfig> for Config {
|
||||||
];
|
];
|
||||||
|
|
||||||
Ok(Config {
|
Ok(Config {
|
||||||
amplitude: DacCode::try_from(config.amplitude)
|
amplitude: DacCode::try_from(self.amplitude)
|
||||||
.or(Err(Error::InvalidAmplitude))?
|
.or(Err(Error::InvalidAmplitude))?
|
||||||
.into(),
|
.into(),
|
||||||
signal: config.signal,
|
signal: self.signal,
|
||||||
frequency_tuning_word,
|
frequency_tuning_word,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -119,6 +123,16 @@ pub struct Config {
|
||||||
pub frequency_tuning_word: [u32; 2],
|
pub frequency_tuning_word: [u32; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
signal: Signal::Cosine,
|
||||||
|
amplitude: 0,
|
||||||
|
frequency_tuning_word: [0, 0],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SignalGenerator {
|
pub struct SignalGenerator {
|
||||||
phase_accumulator: u32,
|
phase_accumulator: u32,
|
||||||
|
@ -128,7 +142,7 @@ pub struct SignalGenerator {
|
||||||
impl Default for SignalGenerator {
|
impl Default for SignalGenerator {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
config: BasicConfig::default().try_into().unwrap(),
|
config: Config::default(),
|
||||||
phase_accumulator: 0,
|
phase_accumulator: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ use heapless::String;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
use super::{MqttMessage, NetworkReference, SettingsResponse, UpdateState};
|
use super::{MqttMessage, NetworkReference, SettingsResponse, UpdateState};
|
||||||
use crate::configuration::MQTT_BROKER;
|
use minimq::embedded_nal::IpAddr;
|
||||||
|
|
||||||
/// MQTT settings interface.
|
/// MQTT settings interface.
|
||||||
pub struct MiniconfClient<S>
|
pub struct MiniconfClient<S>
|
||||||
|
@ -38,9 +38,14 @@ where
|
||||||
/// * `stack` - The network stack to use for communication.
|
/// * `stack` - The network stack to use for communication.
|
||||||
/// * `client_id` - The ID of the MQTT client. May be an empty string for auto-assigning.
|
/// * `client_id` - The ID of the MQTT client. May be an empty string for auto-assigning.
|
||||||
/// * `prefix` - The MQTT device prefix to use for this device.
|
/// * `prefix` - The MQTT device prefix to use for this device.
|
||||||
pub fn new(stack: NetworkReference, client_id: &str, prefix: &str) -> Self {
|
/// * `broker` - The IP address of the MQTT broker to use.
|
||||||
let mqtt =
|
pub fn new(
|
||||||
minimq::Minimq::new(MQTT_BROKER.into(), client_id, stack).unwrap();
|
stack: NetworkReference,
|
||||||
|
client_id: &str,
|
||||||
|
prefix: &str,
|
||||||
|
broker: IpAddr,
|
||||||
|
) -> Self {
|
||||||
|
let mqtt = minimq::Minimq::new(broker, client_id, stack).unwrap();
|
||||||
|
|
||||||
let mut response_topic: String<128> = String::from(prefix);
|
let mut response_topic: String<128> = String::from(prefix);
|
||||||
response_topic.push_str("/log").unwrap();
|
response_topic.push_str("/log").unwrap();
|
||||||
|
|
|
@ -20,6 +20,7 @@ use crate::hardware::{cycle_counter::CycleCounter, EthernetPhy, NetworkStack};
|
||||||
use data_stream::{DataStream, FrameGenerator};
|
use data_stream::{DataStream, FrameGenerator};
|
||||||
use messages::{MqttMessage, SettingsResponse};
|
use messages::{MqttMessage, SettingsResponse};
|
||||||
use miniconf_client::MiniconfClient;
|
use miniconf_client::MiniconfClient;
|
||||||
|
use minimq::embedded_nal::IpAddr;
|
||||||
use network_processor::NetworkProcessor;
|
use network_processor::NetworkProcessor;
|
||||||
use shared::NetworkManager;
|
use shared::NetworkManager;
|
||||||
use telemetry::TelemetryClient;
|
use telemetry::TelemetryClient;
|
||||||
|
@ -66,6 +67,7 @@ where
|
||||||
/// * `cycle_counter` - The clock used for measuring time in the network.
|
/// * `cycle_counter` - The clock used for measuring time in the network.
|
||||||
/// * `app` - The name of the application.
|
/// * `app` - The name of the application.
|
||||||
/// * `mac` - The MAC address of the network.
|
/// * `mac` - The MAC address of the network.
|
||||||
|
/// * `broker` - The IP address of the MQTT broker to use.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// A new struct of network users.
|
/// A new struct of network users.
|
||||||
|
@ -75,6 +77,7 @@ where
|
||||||
cycle_counter: CycleCounter,
|
cycle_counter: CycleCounter,
|
||||||
app: &str,
|
app: &str,
|
||||||
mac: smoltcp_nal::smoltcp::wire::EthernetAddress,
|
mac: smoltcp_nal::smoltcp::wire::EthernetAddress,
|
||||||
|
broker: IpAddr,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let stack_manager =
|
let stack_manager =
|
||||||
cortex_m::singleton!(: NetworkManager = NetworkManager::new(stack))
|
cortex_m::singleton!(: NetworkManager = NetworkManager::new(stack))
|
||||||
|
@ -92,12 +95,14 @@ where
|
||||||
stack_manager.acquire_stack(),
|
stack_manager.acquire_stack(),
|
||||||
&get_client_id(app, "settings", mac),
|
&get_client_id(app, "settings", mac),
|
||||||
&prefix,
|
&prefix,
|
||||||
|
broker,
|
||||||
);
|
);
|
||||||
|
|
||||||
let telemetry = TelemetryClient::new(
|
let telemetry = TelemetryClient::new(
|
||||||
stack_manager.acquire_stack(),
|
stack_manager.acquire_stack(),
|
||||||
&get_client_id(app, "tlm", mac),
|
&get_client_id(app, "tlm", mac),
|
||||||
&prefix,
|
&prefix,
|
||||||
|
broker,
|
||||||
);
|
);
|
||||||
|
|
||||||
let (generator, stream) =
|
let (generator, stream) =
|
||||||
|
|
|
@ -15,8 +15,8 @@ use minimq::QoS;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use super::NetworkReference;
|
use super::NetworkReference;
|
||||||
use crate::configuration::MQTT_BROKER;
|
|
||||||
use crate::hardware::{adc::AdcCode, afe::Gain, dac::DacCode};
|
use crate::hardware::{adc::AdcCode, afe::Gain, dac::DacCode};
|
||||||
|
use minimq::embedded_nal::IpAddr;
|
||||||
|
|
||||||
/// The telemetry client for reporting telemetry data over MQTT.
|
/// The telemetry client for reporting telemetry data over MQTT.
|
||||||
pub struct TelemetryClient<T: Serialize> {
|
pub struct TelemetryClient<T: Serialize> {
|
||||||
|
@ -96,12 +96,17 @@ impl<T: Serialize> TelemetryClient<T> {
|
||||||
/// * `stack` - A reference to the (shared) underlying network stack.
|
/// * `stack` - A reference to the (shared) underlying network stack.
|
||||||
/// * `client_id` - The MQTT client ID of the telemetry client.
|
/// * `client_id` - The MQTT client ID of the telemetry client.
|
||||||
/// * `prefix` - The device prefix to use for MQTT telemetry reporting.
|
/// * `prefix` - The device prefix to use for MQTT telemetry reporting.
|
||||||
|
/// * `broker` - The IP address of the MQTT broker to use.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// A new telemetry client.
|
/// A new telemetry client.
|
||||||
pub fn new(stack: NetworkReference, client_id: &str, prefix: &str) -> Self {
|
pub fn new(
|
||||||
let mqtt =
|
stack: NetworkReference,
|
||||||
minimq::Minimq::new(MQTT_BROKER.into(), client_id, stack).unwrap();
|
client_id: &str,
|
||||||
|
prefix: &str,
|
||||||
|
broker: IpAddr,
|
||||||
|
) -> Self {
|
||||||
|
let mqtt = minimq::Minimq::new(broker, client_id, stack).unwrap();
|
||||||
|
|
||||||
let mut telemetry_topic: String<128> = String::from(prefix);
|
let mut telemetry_topic: String<128> = String::from(prefix);
|
||||||
telemetry_topic.push_str("/telemetry").unwrap();
|
telemetry_topic.push_str("/telemetry").unwrap();
|
||||||
|
|
Loading…
Reference in New Issue