Add Temperature Monitor to Thermostat
- Issue an alarm when temperature goes out of user-defined operating range during Pid Controller startup or reading is outside of +-0.5 Degree from temperature set point after Pid Controller becomes stable - If alarm is observed, power down laser and tec controller and disengage Pid Controller - Add the corresponding cmd for configuring the temperature monitor
This commit is contained in:
parent
ed785b7c85
commit
a8787430b1
|
@ -97,6 +97,12 @@ fn main() -> ! {
|
||||||
laser.poll_and_update_output_current();
|
laser.poll_and_update_output_current();
|
||||||
|
|
||||||
if thermostat.poll_adc_and_update_pid() {
|
if thermostat.poll_adc_and_update_pid() {
|
||||||
|
if thermostat.get_temp_mon_status().over_temp_alarm {
|
||||||
|
laser.power_down();
|
||||||
|
thermostat.set_pid_engaged(false);
|
||||||
|
thermostat.power_down();
|
||||||
|
}
|
||||||
|
|
||||||
info!("curr_dac_vfb: {:?}", volt_fmt.with(thermostat.get_dac_vfb()));
|
info!("curr_dac_vfb: {:?}", volt_fmt.with(thermostat.get_dac_vfb()));
|
||||||
info!("curr_vref: {:?}", volt_fmt.with(thermostat.get_vref()));
|
info!("curr_vref: {:?}", volt_fmt.with(thermostat.get_vref()));
|
||||||
info!("curr_tec_i: {:?}", amp_fmt.with(thermostat.get_tec_i()));
|
info!("curr_tec_i: {:?}", amp_fmt.with(thermostat.get_tec_i()));
|
||||||
|
|
|
@ -71,6 +71,10 @@ enum ThermostatCmdEnum {
|
||||||
SetPidOutMin,
|
SetPidOutMin,
|
||||||
SetPidOutMax,
|
SetPidOutMax,
|
||||||
SetPidUpdateInterval, // Update Interval is set based on the sampling rate of ADC
|
SetPidUpdateInterval, // Update Interval is set based on the sampling rate of ADC
|
||||||
|
// TempMon
|
||||||
|
SetTempMonUpperLimit,
|
||||||
|
SetTempMonLowerLimit,
|
||||||
|
ClearAlarm,
|
||||||
// Steinhart-Hart Equation
|
// Steinhart-Hart Equation
|
||||||
SetShT0,
|
SetShT0,
|
||||||
SetShR0,
|
SetShR0,
|
||||||
|
@ -342,6 +346,29 @@ pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, mut laser: LdDrive, mu
|
||||||
Some(ThermostatCmdEnum::SetPidUpdateInterval) => {
|
Some(ThermostatCmdEnum::SetPidUpdateInterval) => {
|
||||||
info!("Not supported Yet")
|
info!("Not supported Yet")
|
||||||
}
|
}
|
||||||
|
Some(ThermostatCmdEnum::SetTempMonUpperLimit) => {
|
||||||
|
match cmd.json.data_f64 {
|
||||||
|
Some(val) => {
|
||||||
|
tec.set_temp_mon_upper_limit(ThermodynamicTemperature::new::<degree_celsius>(val));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
info!("Wrong Data type is received")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(ThermostatCmdEnum::SetTempMonLowerLimit) => {
|
||||||
|
match cmd.json.data_f64 {
|
||||||
|
Some(val) => {
|
||||||
|
tec.set_temp_mon_lower_limit(ThermodynamicTemperature::new::<degree_celsius>(val));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
info!("Wrong Data type is received")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(ThermostatCmdEnum::ClearAlarm) => {
|
||||||
|
tec.clear_temp_mon_alarm();
|
||||||
|
}
|
||||||
Some(ThermostatCmdEnum::SetShT0) => {
|
Some(ThermostatCmdEnum::SetShT0) => {
|
||||||
match cmd.json.data_f64 {
|
match cmd.json.data_f64 {
|
||||||
Some(val) => {
|
Some(val) => {
|
||||||
|
|
|
@ -4,3 +4,4 @@ pub mod thermostat;
|
||||||
pub mod ad7172;
|
pub mod ad7172;
|
||||||
pub mod steinhart_hart;
|
pub mod steinhart_hart;
|
||||||
pub mod pid_state;
|
pub mod pid_state;
|
||||||
|
pub mod temp_mon;
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use miniconf::Tree;
|
||||||
|
use uom::si::{
|
||||||
|
thermodynamic_temperature::degree_celsius,
|
||||||
|
f64::ThermodynamicTemperature
|
||||||
|
};
|
||||||
|
use num_traits::Float;
|
||||||
|
#[derive(PartialEq, Deserialize, Serialize, Copy, Clone, Default, Debug)]
|
||||||
|
pub enum TempStatusEnum {
|
||||||
|
#[default]
|
||||||
|
Off,
|
||||||
|
OverTemp,
|
||||||
|
Unstable,
|
||||||
|
Stable,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
|
||||||
|
pub struct TempStatus {
|
||||||
|
pub status: TempStatusEnum,
|
||||||
|
pub over_temp_alarm: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TempMonSettings {
|
||||||
|
pub upper_limit: ThermodynamicTemperature,
|
||||||
|
pub lower_limit: ThermodynamicTemperature,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TempMon {
|
||||||
|
pub upper_limit: ThermodynamicTemperature,
|
||||||
|
pub lower_limit: ThermodynamicTemperature,
|
||||||
|
pub set_point: ThermodynamicTemperature,
|
||||||
|
pub status: TempStatus,
|
||||||
|
state: State,
|
||||||
|
count: u32,
|
||||||
|
is_set_point_changed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TempMon {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
upper_limit: ThermodynamicTemperature::new::<degree_celsius>(45.0),
|
||||||
|
lower_limit: ThermodynamicTemperature::new::<degree_celsius>(0.0),
|
||||||
|
set_point: ThermodynamicTemperature::new::<degree_celsius>(0.0),
|
||||||
|
status: TempStatus {
|
||||||
|
status: TempStatusEnum::Off,
|
||||||
|
over_temp_alarm: false
|
||||||
|
},
|
||||||
|
state: State::default(),
|
||||||
|
count: 0,
|
||||||
|
// stable_temp_count: 0,
|
||||||
|
is_set_point_changed: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, PartialEq, Debug)]
|
||||||
|
enum State {
|
||||||
|
#[default]
|
||||||
|
PidOff,
|
||||||
|
PidStartUp,
|
||||||
|
PidStable,
|
||||||
|
OverTempAlarm,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TempMon {
|
||||||
|
const OVER_TEMP_COUNT_LIMIT: u32 = 25;
|
||||||
|
const TEMP_STABLE_COUNT_LIMIT: u32 = 100;
|
||||||
|
|
||||||
|
pub fn set_upper_limit(&mut self, upper_limit: ThermodynamicTemperature) {
|
||||||
|
self.upper_limit = upper_limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_lower_limit(&mut self, lower_limit: ThermodynamicTemperature) {
|
||||||
|
self.lower_limit = lower_limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_setpoint(&mut self, set_point: ThermodynamicTemperature) {
|
||||||
|
if self.set_point != set_point {
|
||||||
|
self.is_set_point_changed = true;
|
||||||
|
self.count = 0;
|
||||||
|
}
|
||||||
|
self.set_point = set_point;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_alarm(&mut self) {
|
||||||
|
self.status.over_temp_alarm = false;
|
||||||
|
self.state = State::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_status(&mut self, pid_engaged: bool, temp: ThermodynamicTemperature) {
|
||||||
|
match self.state {
|
||||||
|
State::PidOff => {
|
||||||
|
self.is_set_point_changed = false;
|
||||||
|
self.status.status = TempStatusEnum::Off;
|
||||||
|
self.count = 0;
|
||||||
|
|
||||||
|
// State Transition
|
||||||
|
if pid_engaged {
|
||||||
|
self.state = State::PidStartUp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
State::PidStartUp | State::PidStable => {
|
||||||
|
let is_over_temp: bool;
|
||||||
|
if self.state == State::PidStartUp {
|
||||||
|
is_over_temp = temp > self.upper_limit || temp < self.lower_limit;
|
||||||
|
} else {
|
||||||
|
is_over_temp = (temp.value - self.set_point.value).abs() > 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_within_spec: bool = (temp.value - self.set_point.value).abs() < 0.001;
|
||||||
|
if is_over_temp {
|
||||||
|
if self.count > TempMon::OVER_TEMP_COUNT_LIMIT {
|
||||||
|
self.status.status = TempStatusEnum::OverTemp;
|
||||||
|
} else {
|
||||||
|
self.count += 1;
|
||||||
|
}
|
||||||
|
} else if is_within_spec {
|
||||||
|
if self.count > TempMon::TEMP_STABLE_COUNT_LIMIT {
|
||||||
|
self.status.status = TempStatusEnum::Stable;
|
||||||
|
} else {
|
||||||
|
self.count += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.status.status = TempStatusEnum::Unstable;
|
||||||
|
self.count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// State Transition
|
||||||
|
if self.status.status == TempStatusEnum::OverTemp {
|
||||||
|
self.state = State::OverTempAlarm;
|
||||||
|
} else if self.is_set_point_changed {
|
||||||
|
self.is_set_point_changed = false;
|
||||||
|
self.state = State::PidStartUp;
|
||||||
|
} else if self.status.status == TempStatusEnum::Stable {
|
||||||
|
self.state = State::PidStable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
State::OverTempAlarm => {
|
||||||
|
self.is_set_point_changed = false;
|
||||||
|
self.status.over_temp_alarm = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_status(&mut self) -> TempStatus {
|
||||||
|
self.status
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_settings(&mut self) -> TempMonSettings {
|
||||||
|
TempMonSettings {
|
||||||
|
upper_limit: self.upper_limit,
|
||||||
|
lower_limit: self.lower_limit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ use crate::thermostat::max1968::{MAX1968, AdcReadTarget, PwmPinsEnum};
|
||||||
use crate::thermostat::ad7172;
|
use crate::thermostat::ad7172;
|
||||||
use crate::thermostat::pid_state::{PidState, PidSettings};
|
use crate::thermostat::pid_state::{PidState, PidSettings};
|
||||||
use crate::thermostat::steinhart_hart;
|
use crate::thermostat::steinhart_hart;
|
||||||
|
use crate::thermostat::temp_mon::{TempMon, TempStatus, TempMonSettings};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use uom::si::{
|
use uom::si::{
|
||||||
|
@ -103,6 +104,7 @@ pub struct Thermostat {
|
||||||
ad7172: ad7172::AdcPhy,
|
ad7172: ad7172::AdcPhy,
|
||||||
pub tec_setting: TecSettings,
|
pub tec_setting: TecSettings,
|
||||||
pid_ctrl_ch0: PidState,
|
pid_ctrl_ch0: PidState,
|
||||||
|
temp_mon: TempMon,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Thermostat{
|
impl Thermostat{
|
||||||
|
@ -112,6 +114,7 @@ impl Thermostat{
|
||||||
ad7172: ad7172,
|
ad7172: ad7172,
|
||||||
tec_setting: TecSettings::default(),
|
tec_setting: TecSettings::default(),
|
||||||
pid_ctrl_ch0: PidState::default(),
|
pid_ctrl_ch0: PidState::default(),
|
||||||
|
temp_mon: TempMon::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn setup(&mut self){
|
pub fn setup(&mut self){
|
||||||
|
@ -148,7 +151,8 @@ impl Thermostat{
|
||||||
let state: &mut PidState = &mut self.pid_ctrl_ch0;
|
let state: &mut PidState = &mut self.pid_ctrl_ch0;
|
||||||
state.update(data);
|
state.update(data);
|
||||||
debug!("state.get_pid_engaged(): {:?}", state.get_pid_engaged());
|
debug!("state.get_pid_engaged(): {:?}", state.get_pid_engaged());
|
||||||
if state.get_pid_engaged() {
|
let pid_engaged = state.get_pid_engaged();
|
||||||
|
if pid_engaged {
|
||||||
match state.update_pid() {
|
match state.update_pid() {
|
||||||
Some(pid_output) => {
|
Some(pid_output) => {
|
||||||
self.set_i(ElectricCurrent::new::<ampere>(pid_output));
|
self.set_i(ElectricCurrent::new::<ampere>(pid_output));
|
||||||
|
@ -157,12 +161,18 @@ impl Thermostat{
|
||||||
None => { }
|
None => { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug!("Temperature: {:?} degree", self.get_temperature().get::<degree_celsius>());
|
let temp = self.get_temperature();
|
||||||
|
self.temp_mon.update_status(pid_engaged, temp);
|
||||||
|
debug!("Temperature: {:?} degree", temp.get::<degree_celsius>());
|
||||||
data_rdy = true;
|
data_rdy = true;
|
||||||
});
|
});
|
||||||
data_rdy
|
data_rdy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_temp_mon_status(&mut self) -> TempStatus {
|
||||||
|
self.temp_mon.get_status()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn power_up(&mut self){
|
pub fn power_up(&mut self){
|
||||||
self.max1968.power_up();
|
self.max1968.power_up();
|
||||||
}
|
}
|
||||||
|
@ -278,6 +288,7 @@ impl Thermostat{
|
||||||
StatusReport {
|
StatusReport {
|
||||||
ts: sys_timer::now(),
|
ts: sys_timer::now(),
|
||||||
pid_engaged: self.get_pid_engaged(),
|
pid_engaged: self.get_pid_engaged(),
|
||||||
|
temp_mon_status: self.temp_mon.get_status(),
|
||||||
temperature: self.pid_ctrl_ch0.get_temperature(),
|
temperature: self.pid_ctrl_ch0.get_temperature(),
|
||||||
i_set: self.tec_setting.i_set,
|
i_set: self.tec_setting.i_set,
|
||||||
tec_i: self.get_tec_i(),
|
tec_i: self.get_tec_i(),
|
||||||
|
@ -335,13 +346,32 @@ impl Thermostat{
|
||||||
|
|
||||||
pub fn set_temperature_setpoint(&mut self, t: ThermodynamicTemperature) {
|
pub fn set_temperature_setpoint(&mut self, t: ThermodynamicTemperature) {
|
||||||
self.pid_ctrl_ch0.set_pid_setpoint(t);
|
self.pid_ctrl_ch0.set_pid_setpoint(t);
|
||||||
|
self.temp_mon.set_setpoint(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_temp_mon_upper_limit(&mut self, t: ThermodynamicTemperature) {
|
||||||
|
self.temp_mon.set_upper_limit(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_temp_mon_lower_limit(&mut self, t: ThermodynamicTemperature) {
|
||||||
|
self.temp_mon.set_lower_limit(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_temp_mon_alarm(&mut self) {
|
||||||
|
self.temp_mon.clear_alarm();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_temp_mon_settings(&mut self) -> TempMonSettings {
|
||||||
|
self.temp_mon.get_settings()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
|
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
|
||||||
pub struct StatusReport {
|
pub struct StatusReport {
|
||||||
ts: u32,
|
ts: u32,
|
||||||
pid_engaged: bool,
|
pid_engaged: bool,
|
||||||
|
temp_mon_status: TempStatus,
|
||||||
temperature: Option<ThermodynamicTemperature>,
|
temperature: Option<ThermodynamicTemperature>,
|
||||||
i_set: ElectricCurrent,
|
i_set: ElectricCurrent,
|
||||||
tec_i: ElectricCurrent,
|
tec_i: ElectricCurrent,
|
||||||
|
|
Loading…
Reference in New Issue