cmd: Add cmd to get all device settings

This commit is contained in:
linuswck 2024-02-23 11:38:26 +08:00
parent c4135f6ac3
commit c09ccc29cd
8 changed files with 159 additions and 122 deletions

View File

@ -2,6 +2,7 @@ use miniconf::Tree;
use stm32f4xx_hal::timer::CounterUs;
use stm32f4xx_hal::pac::{ADC2, TIM2};
use uom::si::electric_current::ampere;
use uom::si::power::milliwatt;
use crate::laser_diode::ld_ctrl::{LdCtrl, Impedance};
use crate::laser_diode::ld_pwr_exc_protector::{LdPwrExcProtector, self};
use crate::laser_diode::pd_responsitivity;
@ -47,12 +48,12 @@ impl Settings{
};
}
#[derive(Clone, Debug, Tree)]
pub struct Settings {
#[derive(Deserialize, Serialize, Clone, Copy, Debug, Tree)]
struct Settings {
ld_drive_current: ElectricCurrent,
ld_drive_current_limit: ElectricCurrent,
#[tree]
pd_responsitivity: pd_responsitivity::Parameters,
ld_pwr_limit: Power,
}
impl Default for Settings {
@ -61,6 +62,7 @@ impl Default for Settings {
ld_drive_current: ElectricCurrent::new::<milliampere>(0.0),
ld_drive_current_limit: ElectricCurrent::new::<milliampere>(0.0),
pd_responsitivity: pd_responsitivity::Parameters::default(),
ld_pwr_limit: Power::new::<milliwatt>(0.0),
}
}
}
@ -199,4 +201,28 @@ impl LdDrive{
term_status: self.get_term_status(),
}
}
pub fn get_settings_summary(&mut self) -> LdSettingsSummary {
let settings = self.settings;
LdSettingsSummary {
ld_drive_current: LdSettingsSummaryField { value: settings.ld_drive_current, max: Settings::LD_CURRENT_MAX},
ld_drive_current_limit: LdSettingsSummaryField { value: settings.ld_drive_current_limit, max: Settings::LD_CURRENT_MAX},
pd_responsitivity: settings.pd_responsitivity,
ld_pwr_limit: settings.ld_pwr_limit,
}
}
}
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
pub struct LdSettingsSummary {
ld_drive_current: LdSettingsSummaryField<ElectricCurrent>,
ld_drive_current_limit: LdSettingsSummaryField<ElectricCurrent>,
pd_responsitivity: pd_responsitivity::Parameters,
ld_pwr_limit: Power,
}
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
pub struct LdSettingsSummaryField<T> {
value: T,
max: T,
}

View File

@ -1,4 +1,5 @@
use core::{f64::NAN, marker::PhantomData};
use serde::{Deserialize, Serialize};
use uom::si::{
f64::{
ElectricCurrent,
@ -12,7 +13,7 @@ use miniconf::Tree;
// Ampere / Watt
pub type ResponsitivityUnit = Quantity<ISQ<N2, N1, P3, P1, Z0, Z0, Z0>, SI<f64>, f64>;
#[derive(Clone, Debug, PartialEq, Tree)]
#[derive(Deserialize, Serialize, Clone, Copy, Debug, PartialEq, Tree)]
pub struct Parameters {
responsitivity: ResponsitivityUnit,
i_dark: ElectricCurrent,

View File

@ -13,10 +13,7 @@ use device::{boot::bootup, log_setup, sys_timer};
use uom::fmt::DisplayStyle::Abbreviation;
use uom::si::electric_potential::volt;
use uom::si::electric_current::{ampere, milliampere};
use uom::si::thermodynamic_temperature::degree_celsius;
use uom::si::power::milliwatt;
use uom::si::f64::{ElectricPotential, ElectricCurrent, Power, ThermodynamicTemperature};
use serde::{Serialize, Deserialize};
use uom::si::f64::{ElectricPotential, ElectricCurrent};
use stm32f4xx_hal::pac::SCB;
// If RTT is used, print panic info through RTT
@ -80,7 +77,6 @@ fn main() -> ! {
let volt_fmt = ElectricPotential::format_args(volt, Abbreviation);
let amp_fmt = ElectricCurrent::format_args(ampere, Abbreviation);
let milli_amp_fmt = ElectricCurrent::format_args(milliampere, Abbreviation);
let milli_watt_fmt = Power::format_args(milliwatt, Abbreviation);
let mut should_reset = false;
@ -118,8 +114,7 @@ fn main() -> ! {
if net::net::eth_is_socket_active() {
if device_settings.report_readings {
unsafe {
net::cmd_handler::send_ld_readings(&mut ETH_DATA_BUFFER, &mut laser);
net::cmd_handler::send_tec_readings(&mut ETH_DATA_BUFFER, &mut thermostat);
net::cmd_handler::send_status_report(&mut ETH_DATA_BUFFER, &mut laser, &mut thermostat);
}
}
}

View File

@ -9,15 +9,15 @@ use uom::si::{
thermodynamic_temperature::{degree_celsius, ThermodynamicTemperature}
};
use crate::{laser_diode::{laser_diode::{
LdDrive, StatusReport as LdStatusReport},
LdDrive, StatusReport as LdStatusReport, LdSettingsSummary},
pd_responsitivity::ResponsitivityUnit
},
net::net,
thermostat::thermostat::StatusReport as TecStatusReport
};
use crate::thermostat::thermostat::Thermostat;
use crate::thermostat::thermostat::{Thermostat, ThermostatSettingsSummary};
use crate::thermostat::pid_state::PidSettings::*;
use crate::device::dfu;
use crate::device::{dfu, sys_timer};
use log::info;
use crate::DeviceSettings;
@ -25,7 +25,9 @@ use crate::DeviceSettings;
enum DeviceCmd {
#[default]
Reserved,
ReportStatus,
SetActiveReportMode,
GetStatusReport,
GetSettingsSummary,
Dfu,
}
@ -43,13 +45,8 @@ enum LdCmdEnum {
// PD Mon Related
SetPdResponsitivity,
SetPdDarkCurrent,
SetPdILimit,
SetLdPwrLimit,
ClearAlarmStatus,
// Report Related
GetModInTermStatus,
GetLdStatus,
GetAlramStatus,
ClearAlarm,
}
#[derive(Deserialize, Serialize, Copy, Clone, Default, Debug)]
@ -81,10 +78,6 @@ enum ThermostatCmdEnum {
SetShT0,
SetShR0,
SetShBeta,
// Report Related
GetTecStatus,
GetPidStatus,
GetShParams,
}
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Default, Tree)]
@ -102,26 +95,47 @@ pub struct Cmd {
}
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
pub struct TecStatusReportStruct {
json: TecStatusReport
pub struct StatusReport {
ts: u32,
laser: LdStatusReport,
tec: TecStatusReport,
}
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
pub struct LaserStatusReportStruct {
json: LdStatusReport
pub struct StatusReportObj {
json: StatusReport
}
pub fn send_ld_readings(buffer: &mut [u8], laser: &mut LdDrive){
let status_report = LaserStatusReportStruct {
json: laser.get_status_report()
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
pub struct SettingsSummary {
laser: LdSettingsSummary,
thermostat: ThermostatSettingsSummary,
}
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
pub struct SettingsSummaryObj {
json: SettingsSummary
}
pub fn send_settings_summary(buffer: &mut [u8], laser: &mut LdDrive, tec: &mut Thermostat){
let settings_summary = SettingsSummaryObj {
json: SettingsSummary {
laser: laser.get_settings_summary(),
thermostat: tec.get_settings_summary(),
}
};
let num_bytes = status_report.get_json("/json", buffer).unwrap();
let num_bytes = settings_summary.get_json("/json", buffer).unwrap();
net::eth_send(buffer, num_bytes);
}
pub fn send_tec_readings(buffer: &mut [u8], tec: &mut Thermostat){
let status_report = TecStatusReportStruct {
json: tec.get_status_report()
pub fn send_status_report(buffer: &mut [u8], laser: &mut LdDrive, tec: &mut Thermostat){
let status_report = StatusReportObj {
json: StatusReport {
ts: sys_timer::now(),
laser: laser.get_status_report(),
tec: tec.get_status_report(),
}
};
let num_bytes = status_report.get_json("/json", buffer).unwrap();
net::eth_send(buffer, num_bytes);
@ -146,7 +160,7 @@ pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, mut laser: LdDrive, mu
}
should_reset = true;
}
Some(DeviceCmd::ReportStatus) => {
Some(DeviceCmd::SetActiveReportMode) => {
match cmd.json.data_bool{
Some(val) => {
device_settings.report_readings = val;
@ -156,6 +170,12 @@ pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, mut laser: LdDrive, mu
}
}
}
Some(DeviceCmd::GetStatusReport) => {
send_status_report(buffer, &mut laser, &mut tec);
}
Some(DeviceCmd::GetSettingsSummary) => {
send_settings_summary(buffer, &mut laser, &mut tec);
}
None => { /* Do Nothing */}
_ => {
info!("Unimplemented Command")
@ -215,16 +235,6 @@ pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, mut laser: LdDrive, mu
}
}
}
Some(LdCmdEnum::SetPdILimit) => {
match cmd.json.data_f64 {
Some(val) => {
laser.set_pd_i_limit(ElectricCurrent::new::<milliampere>(val))
}
None => {
info!("Wrong Data type is received")
}
}
}
Some(LdCmdEnum::SetLdPwrLimit) => {
match cmd.json.data_f64 {
Some(val) => {
@ -235,18 +245,9 @@ pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, mut laser: LdDrive, mu
}
}
}
Some(LdCmdEnum::ClearAlarmStatus) => {
Some(LdCmdEnum::ClearAlarm) => {
laser.pd_mon_clear_alarm()
}
Some(LdCmdEnum::GetModInTermStatus) => {
info!("Not supported Yet")
}
Some(LdCmdEnum::GetLdStatus) => {
send_ld_readings(buffer, &mut laser);
}
Some(LdCmdEnum::GetAlramStatus) => {
info!("Not supported Yet")
}
None => { /* Do Nothing*/ }
_ => {
info!("Unimplemented Command")
@ -422,15 +423,6 @@ pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, mut laser: LdDrive, mu
}
}
}
Some(ThermostatCmdEnum::GetTecStatus) => {
send_tec_readings(buffer, &mut tec);
}
Some(ThermostatCmdEnum::GetPidStatus) => {
info!("Not supported Yet")
}
Some(ThermostatCmdEnum::GetShParams) => {
info!("Not supported Yet")
}
None => { /* Do Nothing*/ }
_ => {
info!("Unimplemented Command")

View File

@ -1,4 +1,5 @@
use miniconf::Tree;
use serde::{Deserialize, Serialize};
use uom::si::{
electric_potential::volt, electrical_resistance::ohm, f64::{
ElectricPotential, ElectricalResistance, ThermodynamicTemperature
@ -11,7 +12,7 @@ use crate::thermostat::{
const R_INNER: f64 = 2.0 * 5100.0;
const VREF_SENS: f64 = 3.3 / 2.0;
#[derive(Clone, Copy, Debug, PartialEq, Tree)]
#[derive(Deserialize, Serialize, Clone, Copy, Debug, PartialEq, Tree)]
pub struct Parameters {
/// Gain coefficient for proportional term
pub kp: f32,
@ -205,6 +206,6 @@ impl PidState {
}
pub fn get_sh(&mut self) -> sh::Parameters {
unimplemented!()
self.sh
}
}

View File

@ -1,4 +1,5 @@
use num_traits::float::Float;
use serde::{Deserialize, Serialize};
use uom::si::{
f64::{
ElectricalResistance,
@ -11,7 +12,7 @@ use uom::si::{
use miniconf::Tree;
/// Steinhart-Hart equation parameters
#[derive(Clone, Debug, PartialEq, Tree)]
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
pub struct Parameters {
/// Base temperature
pub t0: ThermodynamicTemperature,

View File

@ -1,8 +1,7 @@
use serde::{Deserialize, Serialize};
use miniconf::Tree;
use uom::si::{
thermodynamic_temperature::degree_celsius,
f64::ThermodynamicTemperature
f64::ThermodynamicTemperature, thermodynamic_temperature::degree_celsius
};
use num_traits::Float;
#[derive(PartialEq, Deserialize, Serialize, Copy, Clone, Default, Debug)]
@ -20,9 +19,10 @@ pub struct TempStatus {
pub over_temp_alarm: bool,
}
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
pub struct TempMonSettings {
pub upper_limit: ThermodynamicTemperature,
pub lower_limit: ThermodynamicTemperature,
pub upper_limit: f64,
pub lower_limit: f64,
}
pub struct TempMon {
@ -70,10 +70,18 @@ impl TempMon {
self.upper_limit = upper_limit;
}
pub fn get_upper_limit(&mut self) -> ThermodynamicTemperature {
self.upper_limit
}
pub fn set_lower_limit(&mut self, lower_limit: ThermodynamicTemperature) {
self.lower_limit = lower_limit;
}
pub fn get_lower_limit(&mut self) -> ThermodynamicTemperature {
self.lower_limit
}
pub fn set_setpoint(&mut self, set_point: ThermodynamicTemperature) {
if self.set_point != set_point {
self.is_set_point_changed = true;
@ -148,8 +156,8 @@ impl TempMon {
pub fn get_settings(&mut self) -> TempMonSettings {
TempMonSettings {
upper_limit: self.upper_limit,
lower_limit: self.lower_limit,
upper_limit: self.upper_limit.get::<degree_celsius>(),
lower_limit: self.lower_limit.get::<degree_celsius>(),
}
}
}

View File

@ -4,8 +4,7 @@ use crate::sys_timer;
use crate::thermostat::ad5680;
use crate::thermostat::max1968::{MAX1968, AdcReadTarget, PwmPinsEnum};
use crate::thermostat::ad7172;
use crate::thermostat::pid_state::{PidState, PidSettings};
use crate::thermostat::steinhart_hart;
use crate::thermostat::pid_state::{PidState, PidSettings, Parameters as PidParams};
use crate::thermostat::temp_mon::{TempMon, TempStatus, TempMonSettings};
use serde::{Deserialize, Serialize};
use log::debug;
@ -27,7 +26,7 @@ pub const R_SENSE: ElectricalResistance = ElectricalResistance {
value: 0.05,
};
#[derive(Clone, Debug, Tree)]
#[derive(Deserialize, Serialize, Clone, Copy, Debug, Tree)]
pub struct TecSettings {
pub center_pt: ElectricPotential,
pub max_v_set: ElectricPotential,
@ -102,17 +101,25 @@ impl Default for TecSettings {
pub struct Thermostat {
max1968: MAX1968,
ad7172: ad7172::AdcPhy,
pub tec_setting: TecSettings,
pub tec_settings: TecSettings,
pid_ctrl_ch0: PidState,
temp_mon: TempMon,
}
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
pub struct ThermostatSettingsSummary {
tec_settings: TecSettingSummary,
pid_params: PidParams,
temp_mon_settings: TempMonSettings,
thermistor_params: ThermistorParams,
}
impl Thermostat{
pub fn new (max1968: MAX1968, ad7172: ad7172::AdcPhy) -> Self {
Thermostat{
max1968: max1968,
ad7172: ad7172,
tec_setting: TecSettings::default(),
tec_settings: TecSettings::default(),
pid_ctrl_ch0: PidState::default(),
temp_mon: TempMon::default(),
}
@ -126,13 +133,13 @@ impl Thermostat{
fn tec_setup(&mut self) {
self.power_down();
self.tec_setting = TecSettings::default();
self.tec_settings = TecSettings::default();
self.set_i(self.tec_setting.i_set);
self.set_i(self.tec_settings.i_set);
self.set_max_v(self.tec_setting.max_v_set);
self.set_max_i_pos(self.tec_setting.max_i_pos_set);
self.set_max_i_neg(self.tec_setting.max_i_neg_set);
self.set_max_v(self.tec_settings.max_v_set);
self.set_max_i_pos(self.tec_settings.max_i_pos_set);
self.set_max_i_neg(self.tec_settings.max_i_neg_set);
}
fn t_adc_setup(&mut self)->ad7172::ChannelCalibration{
@ -184,35 +191,35 @@ impl Thermostat{
}
fn set_center_pt(&mut self, value: ElectricPotential){
self.tec_setting.center_pt = value;
self.tec_settings.center_pt = value;
}
pub fn set_i(&mut self, i_tec: ElectricCurrent) -> ElectricCurrent {
let voltage = i_tec * 10.0 * R_SENSE + self.tec_setting.center_pt;
let voltage = i_tec * 10.0 * R_SENSE + self.tec_settings.center_pt;
let voltage = self.max1968.set_dac(voltage, TecSettings::DAC_OUT_V_MAX);
self.tec_setting.i_set = (voltage - self.tec_setting.center_pt) / (10.0 * R_SENSE);
self.tec_setting.i_set
self.tec_settings.i_set = (voltage - self.tec_settings.center_pt) / (10.0 * R_SENSE);
self.tec_settings.i_set
}
pub fn set_max_v(&mut self, max_v: ElectricPotential) -> ElectricPotential {
let duty = (max_v / TecSettings::MAX_V_DUTY_TO_CURRENT_RATE).get::<ratio>();
let duty = self.max1968.set_pwm(PwmPinsEnum::MaxV, duty, TecSettings::MAX_V_DUTY_MAX);
self.tec_setting.max_v_set = duty * TecSettings::MAX_V_DUTY_TO_CURRENT_RATE;
self.tec_setting.max_v_set
self.tec_settings.max_v_set = duty * TecSettings::MAX_V_DUTY_TO_CURRENT_RATE;
self.tec_settings.max_v_set
}
pub fn set_max_i_pos(&mut self, max_i_pos: ElectricCurrent) -> ElectricCurrent {
let duty = (max_i_pos / TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE).get::<ratio>();
let duty = self.max1968.set_pwm(PwmPinsEnum::MaxPosI, duty, TecSettings::MAX_I_POS_DUTY_MAX);
self.tec_setting.max_i_pos_set = duty * TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE;
self.tec_setting.max_i_pos_set
self.tec_settings.max_i_pos_set = duty * TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE;
self.tec_settings.max_i_pos_set
}
pub fn set_max_i_neg(&mut self, max_i_neg: ElectricCurrent) -> ElectricCurrent {
let duty = (max_i_neg / TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE).get::<ratio>();
let duty = self.max1968.set_pwm(PwmPinsEnum::MaxNegI, duty, TecSettings::MAX_I_NEG_DUTY_MAX);
self.tec_setting.max_i_neg_set = duty * TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE;
self.tec_setting.max_i_neg_set
self.tec_settings.max_i_neg_set = duty * TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE;
self.tec_settings.max_i_neg_set
}
pub fn get_dac_vfb(&mut self) -> ElectricPotential {
@ -280,17 +287,16 @@ impl Thermostat{
self.pid_ctrl_ch0.set_pid_engaged(val);
}
pub fn get_pid_engaged(&mut self) -> bool {
fn get_pid_engaged(&mut self) -> bool {
self.pid_ctrl_ch0.get_pid_engaged()
}
pub fn get_status_report(&mut self) -> StatusReport {
StatusReport {
ts: sys_timer::now(),
pid_engaged: self.get_pid_engaged(),
temp_mon_status: self.temp_mon.get_status(),
temperature: self.pid_ctrl_ch0.get_temperature(),
i_set: self.tec_setting.i_set,
i_set: self.tec_settings.i_set,
tec_i: self.get_tec_i(),
tec_v: self.get_tec_v(),
tec_vref: self.get_vref(),
@ -306,21 +312,26 @@ impl Thermostat{
}
}
pub fn get_pid_settings(&mut self) -> pid_state::Parameters {
fn get_pid_settings(&mut self) -> pid_state::Parameters {
self.pid_ctrl_ch0.get_pid_settings()
}
pub fn get_steinhart_hart(&mut self) -> steinhart_hart::Parameters {
self.pid_ctrl_ch0.get_sh()
fn get_steinhart_hart(&mut self) -> ThermistorParams {
let sh = self.pid_ctrl_ch0.get_sh();
ThermistorParams {
t0: sh.t0.get::<degree_celsius>(),
r0: sh.r0,
b: sh.b,
}
pub fn get_tec_settings(&mut self) -> TecSettingSummary {
}
fn get_tec_settings(&mut self) -> TecSettingSummary {
TecSettingSummary {
center_point: self.tec_setting.center_pt,
i_set: TecSettingsSummaryField { value: self.tec_setting.i_set, max: TecSettings::MAX_I_SET },
max_v: TecSettingsSummaryField { value: self.tec_setting.max_v_set, max: TecSettings::MAX_V_MAX },
max_i_pos: TecSettingsSummaryField { value: self.tec_setting.max_i_pos_set, max: TecSettings::MAX_I_POS_CURRENT },
max_i_neg: TecSettingsSummaryField { value: self.tec_setting.max_i_neg_set, max: TecSettings::MAX_I_NEG_CURRENT },
i_set: TecSettingsSummaryField { value: self.tec_settings.i_set, max: TecSettings::MAX_I_SET },
max_v: TecSettingsSummaryField { value: self.tec_settings.max_v_set, max: TecSettings::MAX_V_MAX },
max_i_pos: TecSettingsSummaryField { value: self.tec_settings.max_i_pos_set, max: TecSettings::MAX_I_POS_CURRENT },
max_i_neg: TecSettingsSummaryField { value: self.tec_settings.max_i_neg_set, max: TecSettings::MAX_I_NEG_CURRENT },
}
}
@ -345,8 +356,7 @@ impl Thermostat{
}
pub fn set_temperature_setpoint(&mut self, t: ThermodynamicTemperature) {
let temp_mon_settings = self.temp_mon.get_settings();
let t = t.min(temp_mon_settings.upper_limit).max(temp_mon_settings.lower_limit);
let t = t.min(self.temp_mon.get_upper_limit()).max(self.temp_mon.get_lower_limit());
self.pid_ctrl_ch0.set_pid_setpoint(t);
self.temp_mon.set_setpoint(t);
}
@ -363,15 +373,22 @@ impl Thermostat{
self.temp_mon.clear_alarm();
}
pub fn get_temp_mon_settings(&mut self) -> TempMonSettings {
fn get_temp_mon_settings(&mut self) -> TempMonSettings {
self.temp_mon.get_settings()
}
pub fn get_settings_summary(&mut self) -> ThermostatSettingsSummary {
ThermostatSettingsSummary {
tec_settings: self.get_tec_settings(),
pid_params: self.get_pid_settings(),
temp_mon_settings: self.get_temp_mon_settings(),
thermistor_params: self.get_steinhart_hart(),
}
}
}
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
pub struct StatusReport {
ts: u32,
pid_engaged: bool,
temp_mon_status: TempStatus,
temperature: Option<ThermodynamicTemperature>,
@ -381,27 +398,23 @@ pub struct StatusReport {
tec_vref: ElectricPotential,
}
#[derive(Tree)]
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
pub struct TecSettingsSummaryField<T> {
value: T,
max: T,
}
#[derive(Tree)]
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
pub struct TecSettingSummary {
center_point: ElectricPotential,
#[tree]
i_set: TecSettingsSummaryField<ElectricCurrent>,
#[tree]
max_v: TecSettingsSummaryField<ElectricPotential>,
#[tree]
max_i_pos: TecSettingsSummaryField<ElectricCurrent>,
#[tree]
max_i_neg: TecSettingsSummaryField<ElectricCurrent>,
}
#[derive(Tree)]
pub struct SteinhartHartSummary {
#[tree]
params: steinhart_hart::Parameters,
#[derive(Deserialize, Serialize, Clone, Copy, Debug, Tree)]
pub struct ThermistorParams {
t0: f64,
r0: ElectricalResistance,
b: f64
}