From 2f7ca2a706b3668fcabee8070ac3d02b5e5fc1d6 Mon Sep 17 00:00:00 2001 From: linuswck Date: Thu, 18 Jan 2024 15:14:24 +0800 Subject: [PATCH] Thermostat: Add fns to report status & settings - Report all system status, PID Settings, NTC parameter, TEC Settings --- src/main.rs | 1 + src/pid/pid.rs | 6 +- src/thermostat/pid_state.rs | 20 +++--- src/thermostat/thermostat.rs | 118 ++++++++++++++++++++++++++++------- 4 files changed, 110 insertions(+), 35 deletions(-) diff --git a/src/main.rs b/src/main.rs index f799147..ddb90e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ use stm32f4xx_hal::pac::{CorePeripherals, Peripherals}; mod device; mod laser_diode; mod thermostat; +mod pid; use device::{boot::bootup, log_setup, sys_timer}; use uom::fmt::DisplayStyle::Abbreviation; diff --git a/src/pid/pid.rs b/src/pid/pid.rs index e491dc5..45c6c2c 100644 --- a/src/pid/pid.rs +++ b/src/pid/pid.rs @@ -2,7 +2,7 @@ use miniconf::Miniconf; use miniconf::serde::{Serialize, Deserialize}; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Miniconf)] pub struct Parameters { /// Gain coefficient for proportional term pub kp: f32, @@ -28,8 +28,9 @@ impl Default for Parameters { } } -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq, Miniconf)] pub struct Controller { + #[miniconf(defer)] pub parameters: Parameters, pub target : f64, u1 : f64, @@ -88,6 +89,7 @@ impl Controller { #[derive(Clone, Debug, Miniconf)] pub struct Summary { + #[miniconf(defer)] parameters: Parameters, target: f64, } diff --git a/src/thermostat/pid_state.rs b/src/thermostat/pid_state.rs index 717bb07..9651340 100644 --- a/src/thermostat/pid_state.rs +++ b/src/thermostat/pid_state.rs @@ -23,8 +23,8 @@ const VREF_SENS: f64 = 3.3 / 2.0; pub struct PidState { pub adc_data: Option, pub adc_calibration: ad7172::ChannelCalibration, - pub sample_ts: Instant, - pub sampling_interval: Duration, + pub update_ts: Time, + pub update_interval: Time, /// i_set 0A center point pub center_point: ElectricPotential, pub dac_volt: ElectricPotential, @@ -45,9 +45,9 @@ impl Default for PidState { PidState { adc_data: None, adc_calibration: ad7172::ChannelCalibration::default(), - sample_ts: Instant::from_secs(0), + update_ts: Time::new::(0.0), // default: 10 Hz - sampling_interval: Duration::from_millis(100), + update_interval: Time::new::(100.0), center_point: ElectricPotential::new::(1.5), dac_volt: ElectricPotential::new::(0.0), pid_engaged: false, @@ -69,8 +69,8 @@ impl PidState { } else { Some(adc_data) }; - self.sampling_interval = now - self.sample_ts; - self.sample_ts = now; + self.update_interval = Time::new::(now.millis() as f64) - self.update_ts; + self.update_ts = Time::new::(now.millis() as f64); } /// Update PID state on ADC input, calculate new DAC output @@ -81,12 +81,12 @@ impl PidState { Some(pid_output) } - pub fn get_sample_ts(&self) -> Time { - Time::new::(self.sample_ts.total_millis() as f64) + pub fn get_update_ts(&self) -> Time { + self.update_ts } - pub fn get_sampling_interval(&self) -> Time { - Time::new::(self.sampling_interval.total_millis() as f64) + pub fn get_update_interval(&self) -> Time { + self.update_interval } pub fn get_adc(&self) -> Option { diff --git a/src/thermostat/thermostat.rs b/src/thermostat/thermostat.rs index d96068d..81fb71a 100644 --- a/src/thermostat/thermostat.rs +++ b/src/thermostat/thermostat.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; use smoltcp::time::Instant; -use crate::sys_timer; +use crate::{sys_timer, pid::pid}; use crate::thermostat::ad5680; use crate::thermostat::max1968::{MAX1968, AdcReadTarget, PwmPinsEnum}; use crate::thermostat::ad7172; @@ -11,7 +11,7 @@ use uom::si::{ electric_current::ampere, electric_potential::volt, electrical_resistance::ohm, - f64::{ElectricCurrent, ElectricPotential, ElectricalResistance, Time}, + f64::{ElectricCurrent, ElectricPotential, ElectricalResistance, Time, ThermodynamicTemperature}, ratio::ratio, }; use miniconf::Miniconf; @@ -23,7 +23,7 @@ pub const R_SENSE: ElectricalResistance = ElectricalResistance { }; #[derive(Clone, Debug, Miniconf)] -pub struct Settings { +pub struct TecSettings { pub center_pt: ElectricPotential, pub max_v_set: ElectricPotential, pub max_i_pos_set: ElectricCurrent, @@ -31,7 +31,7 @@ pub struct Settings { pub i_set: ElectricCurrent, } -impl Settings{ +impl TecSettings{ // FixMe: Rev0_2 is 3.3V max while Rev0_3 is 3.0V max pub const DAC_OUT_V_MAX: ElectricPotential = ElectricPotential { dimension: PhantomData, @@ -47,6 +47,11 @@ impl Settings{ // Kirdy Design Specs: // MaxV = 5.0V // MAX Current = +- 1.0A + const MAX_I_SET : ElectricCurrent = ElectricCurrent { + dimension: PhantomData, + units: PhantomData, + value: 1.0, + }; const MAX_V_DUTY_TO_CURRENT_RATE: ElectricPotential = ElectricPotential { dimension: PhantomData, units: PhantomData, @@ -57,7 +62,7 @@ impl Settings{ units: PhantomData, value: 5.0, }; - const MAX_V_DUTY_MAX: f64 = Settings::MAX_V_MAX.value / Settings::MAX_V_DUTY_TO_CURRENT_RATE.value; + const MAX_V_DUTY_MAX: f64 = TecSettings::MAX_V_MAX.value / TecSettings::MAX_V_DUTY_TO_CURRENT_RATE.value; const MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE: ElectricCurrent = ElectricCurrent { dimension: PhantomData, units: PhantomData, @@ -74,11 +79,11 @@ impl Settings{ value: 1.0, }; // .get::() is not implemented for const - const MAX_I_POS_DUTY_MAX: f64 = Settings::MAX_I_POS_CURRENT.value / Settings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE.value; - const MAX_I_NEG_DUTY_MAX: f64 = Settings::MAX_I_NEG_CURRENT.value / Settings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE.value; + const MAX_I_POS_DUTY_MAX: f64 = TecSettings::MAX_I_POS_CURRENT.value / TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE.value; + const MAX_I_NEG_DUTY_MAX: f64 = TecSettings::MAX_I_NEG_CURRENT.value / TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE.value; } -impl Default for Settings { +impl Default for TecSettings { fn default() -> Self { Self { center_pt: ElectricPotential::new::(1.5), @@ -93,7 +98,7 @@ impl Default for Settings { pub struct Thermostat { max1968: MAX1968, ad7172: ad7172::AdcPhy, - pub tec_setting: Settings, + pub tec_setting: TecSettings, pid_ctrl_ch0: PidState, } @@ -102,7 +107,7 @@ impl Thermostat{ Thermostat{ max1968: max1968, ad7172: ad7172, - tec_setting: Settings::default(), + tec_setting: TecSettings::default(), pid_ctrl_ch0: PidState::default(), } } @@ -115,7 +120,7 @@ impl Thermostat{ fn tec_setup(&mut self) { self.power_down(); - self.tec_setting = Settings::default(); + self.tec_setting = TecSettings::default(); self.set_i(self.tec_setting.i_set); @@ -169,29 +174,29 @@ impl Thermostat{ 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 = self.max1968.set_dac(voltage, Settings::DAC_OUT_V_MAX); + 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 } pub fn set_max_v(&mut self, max_v: ElectricPotential) -> ElectricPotential { - let duty = (max_v / Settings::MAX_V_DUTY_TO_CURRENT_RATE).get::(); - let duty = self.max1968.set_pwm(PwmPinsEnum::MaxV, duty, Settings::MAX_V_DUTY_MAX); - self.tec_setting.max_v_set = duty * Settings::MAX_V_DUTY_TO_CURRENT_RATE; + let duty = (max_v / TecSettings::MAX_V_DUTY_TO_CURRENT_RATE).get::(); + 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 } pub fn set_max_i_pos(&mut self, max_i_pos: ElectricCurrent) -> ElectricCurrent { - let duty = (max_i_pos / Settings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE).get::(); - let duty = self.max1968.set_pwm(PwmPinsEnum::MaxPosI, duty, Settings::MAX_I_POS_DUTY_MAX); - self.tec_setting.max_i_pos_set = duty * Settings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE; + let duty = (max_i_pos / TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE).get::(); + 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 } pub fn set_max_i_neg(&mut self, max_i_neg: ElectricCurrent) -> ElectricCurrent { - let duty = (max_i_neg / Settings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE).get::(); - let duty = self.max1968.set_pwm(PwmPinsEnum::MaxNegI, duty, Settings::MAX_I_NEG_DUTY_MAX); - self.tec_setting.max_i_neg_set = duty * Settings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE; + let duty = (max_i_neg / TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE).get::(); + 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 } @@ -211,7 +216,7 @@ impl Thermostat{ pub fn get_tec_v(&mut self) -> ElectricPotential { // Fixme: Rev0_2 has Analog Input Polarity Reversed // Remove the -ve sign for Rev0_3 - -(self.max1968.adc_read(AdcReadTarget::VTec, 16) - Settings::TEC_VSEC_BIAS_V) * 4.0 + -(self.max1968.adc_read(AdcReadTarget::VTec, 16) - TecSettings::TEC_VSEC_BIAS_V) * 4.0 } /// Calibrates the DAC output to match vref of the MAX driver to reduce zero-current offset of the MAX driver output. @@ -250,7 +255,7 @@ impl Thermostat{ best_error = error; start_value = prev_value; - let vref = (value as f64 / ad5680::MAX_VALUE as f64) * Settings::DAC_OUT_V_MAX; + let vref = (value as f64 / ad5680::MAX_VALUE as f64) * TecSettings::DAC_OUT_V_MAX; self.set_center_pt(vref); } prev_value = value; @@ -266,4 +271,71 @@ impl Thermostat{ false } + pub fn get_status_report(&mut self) -> StatusReport { + StatusReport { + pid_update_ts: self.pid_ctrl_ch0.get_update_ts(), + pid_update_interval: self.pid_ctrl_ch0.get_update_interval(), + pid_engaged: self.pid_engaged(), + temperature: self.pid_ctrl_ch0.get_temperature(), + i_set: self.tec_setting.i_set, + tec_i: self.get_tec_i(), + tec_v: self.get_tec_v(), + tec_vref: self.get_vref(), + } + } + + pub fn get_pid_settings(&mut self) -> pid::Controller { + self.pid_ctrl_ch0.pid.clone() + } + + pub fn get_steinhart_hart(&mut self) -> steinhart_hart::Parameters { + self.pid_ctrl_ch0.sh.clone() + } + + pub 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 }, + } + } +} + +#[derive(Miniconf)] +pub struct StatusReport { + pid_update_ts: Time, + pid_update_interval: Time, + pid_engaged: bool, + temperature: Option, + i_set: ElectricCurrent, + tec_i: ElectricCurrent, + tec_v: ElectricPotential, + tec_vref: ElectricPotential, +} + +#[derive(Miniconf)] +pub struct TecSettingsSummaryField { + value: T, + max: T, +} + +#[derive(Miniconf)] +pub struct TecSettingSummary { + center_point: ElectricPotential, + #[miniconf(defer)] + i_set: TecSettingsSummaryField, + #[miniconf(defer)] + max_v: TecSettingsSummaryField, + #[miniconf(defer)] + max_i_pos: TecSettingsSummaryField, + #[miniconf(defer)] + max_i_neg: TecSettingsSummaryField, +} + +#[derive(Miniconf)] +pub struct SteinhartHartSummary { + #[miniconf(defer)] + params: steinhart_hart::Parameters, }