forked from M-Labs/thermostat
Compare commits
7 Commits
4f4cc13569
...
2e9c1d1467
Author | SHA1 | Date |
---|---|---|
atse | 2e9c1d1467 | |
atse | 2591bff0e2 | |
atse | d470653385 | |
atse | 70d20f3deb | |
atse | 5aaa1edad7 | |
atse | 76547be90a | |
atse | 8b975e656e |
|
@ -107,6 +107,7 @@ formatted as line-delimited JSON.
|
|||
| `pwm <0/1> pid` | Let output current to be controlled by the PID |
|
||||
| `center <0/1> <volt>` | Set the MAX1968 0A-centerpoint to the specified fixed voltage |
|
||||
| `center <0/1> vref` | Set the MAX1968 0A-centerpoint to measure from VREF |
|
||||
| `center <0/1> vref_calib` | Set the MAX1968 0A-centerpoint to a stable calibrated VREF |
|
||||
| `pid` | Show PID configuration |
|
||||
| `pid <0/1> target <deg_celsius>` | Set the PID controller target temperature |
|
||||
| `pid <0/1> kp <value>` | Set proportional gain |
|
||||
|
|
|
@ -20,8 +20,8 @@ pub struct Channel<C: ChannelPins> {
|
|||
pub state: ChannelState,
|
||||
/// for `i_set`
|
||||
pub dac: ad5680::Dac<C::DacSpi, C::DacSync>,
|
||||
/// Measured vref of MAX driver chip
|
||||
pub vref_meas: ElectricPotential,
|
||||
/// Calibrated vref of MAX driver chip
|
||||
pub vref_calib: ElectricPotential,
|
||||
pub shdn: C::Shdn,
|
||||
pub vref_pin: C::VRefPin,
|
||||
pub itec_pin: C::ItecPin,
|
||||
|
@ -36,11 +36,11 @@ impl<C: ChannelPins> Channel<C> {
|
|||
let mut dac = ad5680::Dac::new(pins.dac_spi, pins.dac_sync);
|
||||
let _ = dac.set(0);
|
||||
// sensible dummy preset taken from datasheet. calibrate_dac_value() should be used to override this value.
|
||||
let vref_meas = ElectricPotential::new::<volt>(1.5);
|
||||
let vref_calib = ElectricPotential::new::<volt>(1.5);
|
||||
|
||||
Channel {
|
||||
state,
|
||||
dac, vref_meas,
|
||||
dac, vref_calib,
|
||||
shdn: pins.shdn,
|
||||
vref_pin: pins.vref_pin,
|
||||
itec_pin: pins.itec_pin,
|
||||
|
|
|
@ -2,11 +2,13 @@ use smoltcp::time::{Duration, Instant};
|
|||
use uom::si::{
|
||||
f64::{
|
||||
ElectricPotential,
|
||||
ElectricCurrent,
|
||||
ElectricalResistance,
|
||||
ThermodynamicTemperature,
|
||||
Time,
|
||||
},
|
||||
electric_potential::volt,
|
||||
electric_current::ampere,
|
||||
electrical_resistance::ohm,
|
||||
thermodynamic_temperature::degree_celsius,
|
||||
time::millisecond,
|
||||
|
@ -29,6 +31,7 @@ pub struct ChannelState {
|
|||
/// i_set 0A center point
|
||||
pub center: CenterPoint,
|
||||
pub dac_value: ElectricPotential,
|
||||
pub i_set: ElectricCurrent,
|
||||
pub pid_engaged: bool,
|
||||
pub pid: pid::Controller,
|
||||
pub sh: sh::Parameters,
|
||||
|
@ -42,8 +45,9 @@ impl ChannelState {
|
|||
adc_time: Instant::from_secs(0),
|
||||
// default: 10 Hz
|
||||
adc_interval: Duration::from_millis(100),
|
||||
center: CenterPoint::Vref,
|
||||
center: CenterPoint::VrefCalib,
|
||||
dac_value: ElectricPotential::new::<volt>(0.0),
|
||||
i_set: ElectricCurrent::new::<ampere>(0.0),
|
||||
pid_engaged: false,
|
||||
pid: pid::Controller::new(pid::Parameters::default()),
|
||||
sh: sh::Parameters::default(),
|
||||
|
|
|
@ -21,6 +21,7 @@ use crate::{
|
|||
pins,
|
||||
steinhart_hart,
|
||||
hw_rev,
|
||||
timer,
|
||||
};
|
||||
|
||||
pub const CHANNELS: usize = 2;
|
||||
|
@ -101,6 +102,13 @@ impl<'a> Channels<'a> {
|
|||
match self.channel_state(channel).center {
|
||||
CenterPoint::Vref =>
|
||||
self.read_vref(channel),
|
||||
CenterPoint::VrefCalib => {
|
||||
match channel {
|
||||
0 => self.channel0.vref_calib,
|
||||
1 => self.channel1.vref_calib,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
},
|
||||
CenterPoint::Override(center_point) =>
|
||||
ElectricPotential::new::<volt>(center_point.into()),
|
||||
}
|
||||
|
@ -113,11 +121,8 @@ impl<'a> Channels<'a> {
|
|||
}
|
||||
|
||||
pub fn get_i(&mut self, channel: usize) -> ElectricCurrent {
|
||||
let center_point = self.get_center(channel);
|
||||
let r_sense = ElectricalResistance::new::<ohm>(R_SENSE);
|
||||
let voltage = self.get_dac(channel);
|
||||
let i_tec = (voltage - center_point) / (10.0 * r_sense);
|
||||
i_tec
|
||||
let i_set = self.channel_state(channel).i_set;
|
||||
i_set
|
||||
}
|
||||
|
||||
/// i_set DAC
|
||||
|
@ -132,18 +137,14 @@ impl<'a> Channels<'a> {
|
|||
voltage
|
||||
}
|
||||
|
||||
pub fn set_i(&mut self, channel: usize, i_tec: ElectricCurrent) -> ElectricCurrent {
|
||||
let vref_meas = match channel.into() {
|
||||
0 => self.channel0.vref_meas,
|
||||
1 => self.channel1.vref_meas,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let center_point = vref_meas;
|
||||
pub fn set_i(&mut self, channel: usize, i_set: ElectricCurrent) -> ElectricCurrent {
|
||||
let center_point = self.get_center(channel);
|
||||
let r_sense = ElectricalResistance::new::<ohm>(R_SENSE);
|
||||
let voltage = i_tec * 10.0 * r_sense + center_point;
|
||||
let voltage = i_set * 10.0 * r_sense + center_point;
|
||||
let voltage = self.set_dac(channel, voltage);
|
||||
let i_tec = (voltage - center_point) / (10.0 * r_sense);
|
||||
i_tec
|
||||
let i_set = (voltage - center_point) / (10.0 * r_sense);
|
||||
self.channel_state(channel).i_set = i_set;
|
||||
i_set
|
||||
}
|
||||
|
||||
pub fn read_dac_feedback(&mut self, channel: usize) -> ElectricPotential {
|
||||
|
@ -168,17 +169,6 @@ impl<'a> Channels<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn read_dac_feedback_until_stable(&mut self, channel: usize, tolerance: ElectricPotential) -> ElectricPotential {
|
||||
let mut prev = self.read_dac_feedback(channel);
|
||||
loop {
|
||||
let current = self.read_dac_feedback(channel);
|
||||
if (current - prev).abs() < tolerance {
|
||||
return current;
|
||||
}
|
||||
prev = current;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_itec(&mut self, channel: usize) -> ElectricPotential {
|
||||
match channel {
|
||||
0 => {
|
||||
|
@ -264,15 +254,19 @@ impl<'a> Channels<'a> {
|
|||
/// thermostat.
|
||||
pub fn calibrate_dac_value(&mut self, channel: usize) {
|
||||
let samples = 50;
|
||||
let mut target_voltage = ElectricPotential::new::<volt>(0.0);
|
||||
for _ in 0..samples {
|
||||
target_voltage = target_voltage + self.get_center(channel);
|
||||
}
|
||||
target_voltage = target_voltage / samples as f64;
|
||||
let target_voltage = {
|
||||
let mut target_voltage = ElectricPotential::new::<volt>(0.0);
|
||||
for _ in 0..samples {
|
||||
target_voltage += self.read_vref(channel);
|
||||
}
|
||||
target_voltage /= samples as f64;
|
||||
target_voltage
|
||||
};
|
||||
let mut start_value = 1;
|
||||
let mut best_error = ElectricPotential::new::<volt>(100.0);
|
||||
|
||||
for step in (0..18).rev() {
|
||||
timer::sleep(5);
|
||||
let mut prev_value = start_value;
|
||||
for value in (start_value..=ad5680::MAX_VALUE).step_by(1 << step) {
|
||||
match channel {
|
||||
|
@ -285,7 +279,14 @@ impl<'a> Channels<'a> {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
let dac_feedback = self.read_dac_feedback_until_stable(channel, ElectricPotential::new::<volt>(0.001));
|
||||
let dac_feedback = {
|
||||
let mut dac_feedback = ElectricPotential::new::<volt>(0.0);
|
||||
for _ in 0..samples {
|
||||
dac_feedback += self.read_dac_feedback(channel);
|
||||
}
|
||||
dac_feedback /= samples as f64;
|
||||
dac_feedback
|
||||
};
|
||||
let error = target_voltage - dac_feedback;
|
||||
if error < ElectricPotential::new::<volt>(0.0) {
|
||||
break;
|
||||
|
@ -295,8 +296,8 @@ impl<'a> Channels<'a> {
|
|||
|
||||
let vref = (value as f64 / ad5680::MAX_VALUE as f64) * ElectricPotential::new::<volt>(DAC_OUT_V_MAX);
|
||||
match channel {
|
||||
0 => self.channel0.vref_meas = vref,
|
||||
1 => self.channel1.vref_meas = vref,
|
||||
0 => self.channel0.vref_calib = vref,
|
||||
1 => self.channel1.vref_calib = vref,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
@ -562,6 +563,8 @@ impl Serialize for CenterPointJson {
|
|||
match self.0 {
|
||||
CenterPoint::Vref =>
|
||||
serializer.serialize_str("vref"),
|
||||
CenterPoint::VrefCalib =>
|
||||
serializer.serialize_str("vref_calib"),
|
||||
CenterPoint::Override(vref) =>
|
||||
serializer.serialize_f32(vref),
|
||||
}
|
||||
|
|
|
@ -207,11 +207,11 @@ impl Handler {
|
|||
}
|
||||
|
||||
fn set_center_point(socket: &mut TcpSocket, channels: &mut Channels, channel: usize, center: CenterPoint) -> Result<Handler, Error> {
|
||||
let i_tec = channels.get_i(channel);
|
||||
let i_set = channels.get_i(channel);
|
||||
let state = channels.channel_state(channel);
|
||||
state.center = center;
|
||||
if !state.pid_engaged {
|
||||
channels.set_i(channel, i_tec);
|
||||
channels.set_i(channel, i_set);
|
||||
}
|
||||
send_line(socket, b"{}");
|
||||
Ok(Handler::Handled)
|
||||
|
|
|
@ -133,6 +133,7 @@ pub enum PwmPin {
|
|||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum CenterPoint {
|
||||
Vref,
|
||||
VrefCalib,
|
||||
Override(f32),
|
||||
}
|
||||
|
||||
|
@ -357,6 +358,7 @@ fn center_point(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
|||
let (input, _) = whitespace(input)?;
|
||||
let (input, center) = alt((
|
||||
value(Ok(CenterPoint::Vref), tag("vref")),
|
||||
value(Ok(CenterPoint::VrefCalib), tag("vref_calib")),
|
||||
|input| {
|
||||
let (input, value) = float(input)?;
|
||||
Ok((input, value.map(|value| CenterPoint::Override(value as f32))))
|
||||
|
|
Loading…
Reference in New Issue