From ccaf728c754de9bb087385f7acb9f03299ab5b79 Mon Sep 17 00:00:00 2001 From: linuswck Date: Wed, 17 Jan 2024 12:49:57 +0800 Subject: [PATCH] thermostat: Add channel_state mod for PID Control --- src/thermostat/channel_state.rs | 97 +++++++++++++++++++++++++++++++++ src/thermostat/mod.rs | 1 + 2 files changed, 98 insertions(+) create mode 100644 src/thermostat/channel_state.rs diff --git a/src/thermostat/channel_state.rs b/src/thermostat/channel_state.rs new file mode 100644 index 0000000..e154449 --- /dev/null +++ b/src/thermostat/channel_state.rs @@ -0,0 +1,97 @@ +use smoltcp::time::{Duration, Instant}; +use uom::si::{ + f64::{ + ElectricPotential, + ElectricalResistance, + ThermodynamicTemperature, + Time, + }, + electric_potential::volt, + electrical_resistance::ohm, + thermodynamic_temperature::degree_celsius, + time::millisecond, +}; +use crate::thermostat::{ + ad7172, + steinhart_hart as sh, +}; +use crate::pid::pid; + +const R_INNER: f64 = 2.0 * 5100.0; +const VREF_SENS: f64 = 3.3 / 2.0; + +pub struct ChannelState { + pub adc_data: Option, + pub adc_calibration: ad7172::ChannelCalibration, + pub adc_time: Instant, + pub adc_interval: Duration, + /// i_set 0A center point + pub center: ElectricPotential, + pub dac_value: ElectricPotential, + pub pid_engaged: bool, + pub pid: pid::Controller, + pub sh: sh::Parameters, +} + +impl ChannelState { + pub fn new(adc_calibration: ad7172::ChannelCalibration) -> Self { + ChannelState { + adc_data: None, + adc_calibration, + adc_time: Instant::from_secs(0), + // default: 10 Hz + adc_interval: Duration::from_millis(100), + center: ElectricPotential::new::(1.5), + dac_value: ElectricPotential::new::(0.0), + pid_engaged: false, + pid: pid::Controller::new(pid::Parameters::default()), + sh: sh::Parameters::default(), + } + } + + pub fn update(&mut self, now: Instant, adc_data: u32) { + self.adc_data = if adc_data == ad7172::MAX_VALUE { + // this means there is no thermistor plugged into the ADC. + None + } else { + Some(adc_data) + }; + self.adc_interval = now - self.adc_time; + self.adc_time = now; + } + + /// Update PID state on ADC input, calculate new DAC output + pub fn update_pid(&mut self) -> Option { + let temperature = self.get_temperature()? + .get::(); + let pid_output = self.pid.update(temperature); + Some(pid_output) + } + + pub fn get_adc_time(&self) -> Time { + Time::new::(self.adc_time.total_millis() as f64) + } + + pub fn get_adc_interval(&self) -> Time { + Time::new::(self.adc_interval.total_millis() as f64) + } + + pub fn get_adc(&self) -> Option { + Some(self.adc_calibration.convert_data(self.adc_data?)) + } + + /// Get `SENS[01]` input resistance + pub fn get_sens(&self) -> Option { + let r_inner = ElectricalResistance::new::(R_INNER); + let vref = ElectricPotential::new::(VREF_SENS); + let adc_input = self.get_adc()?; + let r = r_inner * adc_input / (vref - adc_input); + Some(r) + } + + pub fn get_temperature(&self) -> Option { + let r = self.get_sens()?; + let temperature = self.sh.get_temperature(r); + Some(temperature) + } +} diff --git a/src/thermostat/mod.rs b/src/thermostat/mod.rs index 63a58c4..6ae79b4 100644 --- a/src/thermostat/mod.rs +++ b/src/thermostat/mod.rs @@ -3,3 +3,4 @@ pub mod max1968; pub mod thermostat; pub mod ad7172; pub mod steinhart_hart; +pub mod channel_state;