forked from M-Labs/thermostat
Compare commits
3 Commits
631a10938d
...
ae3d8b51d4
Author | SHA1 | Date |
---|---|---|
atse | ae3d8b51d4 | |
atse | 17edae44fb | |
atse | 03b4561142 |
15
README.md
15
README.md
|
@ -271,18 +271,19 @@ with the following keys.
|
|||
| `tec_u_meas` | Volts | Measurement of the voltage across the TEC |
|
||||
| `pid_output` | Amperes | PID control output |
|
||||
|
||||
Note: With Thermostat v2 and below, the voltage and current readouts `i_tec` and `tec_i` are disabled and null due to faulty hardware that introduces a lot of noise in the signal.
|
||||
|
||||
## PID Tuning
|
||||
|
||||
The thermostat implements a PID control loop for each of the TEC channels, more details on setting up the PID control loop can be found [here](./doc/PID%20tuning.md).
|
||||
|
||||
## Fan control
|
||||
|
||||
Fan control is available for the thermostat revisions with integrated fan system. For this purpose four commands are available:
|
||||
Fan control commands are available for thermostat revisions with an integrated fan system:
|
||||
1. `fan` - show fan stats: `fan_pwm`, `abs_max_tec_i`, `auto_mode`, `k_a`, `k_b`, `k_c`.
|
||||
2. `fan auto` - enable auto speed controller mode, which correlates with fan curve `fcurve`.
|
||||
3. `fan <value>` - set the fan power with the value from `1` to `100` and disable auto mode. There is no way to disable the fan.
|
||||
2. `fan auto` - enable auto speed controller mode, where fan speed is controlled by the fan curve `fcurve`.
|
||||
3. `fan <value>` - set the fan power with the value from `1` to `100` and disable auto mode. There is no way to completely disable the fan.
|
||||
Please note that power doesn't correlate with the actual speed linearly.
|
||||
4. `fcurve <a> <b> <c>` - set coefficients of the controlling curve `a*x^2 + b*x + c`, where `x` is `abs_max_tec_i/MAX_TEC_I`,
|
||||
i.e. receives values from 0 to 1 linearly tied to the maximum current. The controlling curve should produce values from 0 to 1,
|
||||
as below and beyond values would be substituted by 0 and 1 respectively.
|
||||
5. `fcurve default` - restore fan curve settings to defaults: `a = 1.0, b = 0.0, c = 0.0`.
|
||||
4. `fcurve <a> <b> <c>` - set coefficients of the controlling curve `a*x^2 + b*x + c`, where `x` is `abs_max_tec_i/MAX_TEC_I`, a normalized value in range [0,1],
|
||||
i.e. the (linear) proportion of current output capacity used, on the channel with the largest current flow. The controlling curve is also clamped to [0,1].
|
||||
5. `fcurve default` - restore fan curve coefficients to defaults: `a = 1.0, b = 0.0, c = 0.0`.
|
||||
|
|
|
@ -20,6 +20,7 @@ use crate::{
|
|||
command_handler::JsonBuffer,
|
||||
pins,
|
||||
steinhart_hart,
|
||||
hw_rev,
|
||||
};
|
||||
|
||||
pub const CHANNELS: usize = 2;
|
||||
|
@ -28,17 +29,18 @@ pub const R_SENSE: f64 = 0.05;
|
|||
const DAC_OUT_V_MAX: f64 = 3.0;
|
||||
|
||||
// TODO: -pub
|
||||
pub struct Channels {
|
||||
pub struct Channels<'a> {
|
||||
channel0: Channel<Channel0>,
|
||||
channel1: Channel<Channel1>,
|
||||
pub adc: ad7172::Adc<pins::AdcSpi, pins::AdcNss>,
|
||||
/// stm32f4 integrated adc
|
||||
pins_adc: pins::PinsAdc,
|
||||
pub pwm: pins::PwmPins,
|
||||
hwrev: &'a hw_rev::HWRev,
|
||||
}
|
||||
|
||||
impl Channels {
|
||||
pub fn new(pins: pins::Pins) -> Self {
|
||||
impl<'a> Channels<'a> {
|
||||
pub fn new(pins: pins::Pins, hwrev: &'a hw_rev::HWRev) -> Self {
|
||||
let mut adc = ad7172::Adc::new(pins.adc_spi, pins.adc_nss).unwrap();
|
||||
// Feature not used
|
||||
adc.set_sync_enable(false).unwrap();
|
||||
|
@ -56,7 +58,7 @@ impl Channels {
|
|||
let channel1 = Channel::new(pins.channel1, adc_calibration1);
|
||||
let pins_adc = pins.pins_adc;
|
||||
let pwm = pins.pwm;
|
||||
let mut channels = Channels { channel0, channel1, adc, pins_adc, pwm };
|
||||
let mut channels = Channels { channel0, channel1, adc, pins_adc, pwm, hwrev };
|
||||
for channel in 0..CHANNELS {
|
||||
channels.calibrate_dac_value(channel);
|
||||
channels.set_i(channel, ElectricCurrent::new::<ampere>(0.0));
|
||||
|
@ -429,8 +431,8 @@ impl Channels {
|
|||
|
||||
fn report(&mut self, channel: usize) -> Report {
|
||||
let i_set = self.get_i(channel);
|
||||
let i_tec = self.read_itec(channel);
|
||||
let tec_i = self.get_tec_i(channel);
|
||||
let i_tec = if self.hwrev.major > 2 {Some(self.read_itec(channel))} else {None};
|
||||
let tec_i = if self.hwrev.major > 2 {Some(self.get_tec_i(channel))} else {None};
|
||||
let dac_value = self.get_dac(channel);
|
||||
let state = self.channel_state(channel);
|
||||
let pid_output = ElectricCurrent::new::<ampere>(state.pid.y1);
|
||||
|
@ -524,9 +526,9 @@ impl Channels {
|
|||
serde_json_core::to_vec(&summaries)
|
||||
}
|
||||
|
||||
pub fn current_abs_max_tec_i(&mut self) -> f64 {
|
||||
max_by(self.get_tec_i(0).abs().get::<ampere>(),
|
||||
self.get_tec_i(1).abs().get::<ampere>(),
|
||||
pub fn current_abs_max_tec_i(&mut self) -> ElectricCurrent {
|
||||
max_by(self.get_tec_i(0).abs(),
|
||||
self.get_tec_i(1).abs(),
|
||||
|a, b| a.partial_cmp(b).unwrap_or(core::cmp::Ordering::Equal))
|
||||
}
|
||||
}
|
||||
|
@ -543,8 +545,8 @@ pub struct Report {
|
|||
i_set: ElectricCurrent,
|
||||
dac_value: ElectricPotential,
|
||||
dac_feedback: ElectricPotential,
|
||||
i_tec: ElectricPotential,
|
||||
tec_i: ElectricCurrent,
|
||||
i_tec: Option<ElectricPotential>,
|
||||
tec_i: Option<ElectricCurrent>,
|
||||
tec_u_meas: ElectricPotential,
|
||||
pid_output: ElectricCurrent,
|
||||
}
|
||||
|
|
|
@ -4,7 +4,10 @@ use stm32f4xx_hal::{
|
|||
pwm::{self, PwmChannels},
|
||||
pac::TIM8,
|
||||
};
|
||||
|
||||
use uom::si::{
|
||||
f64::ElectricCurrent,
|
||||
electric_current::ampere,
|
||||
};
|
||||
use crate::{
|
||||
hw_rev::HWSettings,
|
||||
command_handler::JsonBuffer,
|
||||
|
@ -50,8 +53,8 @@ impl FanCtrl {
|
|||
fan_ctrl
|
||||
}
|
||||
|
||||
pub fn cycle(&mut self, abs_max_tec_i: f32) {
|
||||
self.abs_max_tec_i = abs_max_tec_i;
|
||||
pub fn cycle(&mut self, abs_max_tec_i: ElectricCurrent) {
|
||||
self.abs_max_tec_i = abs_max_tec_i.get::<ampere>() as f32;
|
||||
if self.fan_auto && self.hw_settings.fan_available {
|
||||
let scaled_current = self.abs_max_tec_i / MAX_TEC_I;
|
||||
// do not limit upper bound, as it will be limited in the set_pwm()
|
||||
|
|
|
@ -138,7 +138,7 @@ fn main() -> ! {
|
|||
|
||||
let mut store = flash_store::store(dp.FLASH);
|
||||
|
||||
let mut channels = Channels::new(pins);
|
||||
let mut channels = Channels::new(pins, &hwrev);
|
||||
for c in 0..CHANNELS {
|
||||
match store.read_value::<ChannelConfig>(CHANNEL_CONFIG_KEY[c]) {
|
||||
Ok(Some(config)) =>
|
||||
|
@ -185,7 +185,7 @@ fn main() -> ! {
|
|||
server.for_each(|_, session| session.set_report_pending(channel.into()));
|
||||
}
|
||||
|
||||
fan_ctrl.cycle(channels.current_abs_max_tec_i() as f32);
|
||||
fan_ctrl.cycle(channels.current_abs_max_tec_i());
|
||||
|
||||
if channels.pid_engaged() {
|
||||
leds.g3.on();
|
||||
|
|
Loading…
Reference in New Issue