diff --git a/README.md b/README.md index b0b5d1a..721daf6 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ formatted as line-delimited JSON. | `fcurve ` | Set fan controller curve coefficients (see *Fan control* section) | | `fcurve default` | Set fan controller curve coefficients to defaults (see *Fan control* section) | | `hwrev` | Show hardware revision, and settings related to it | +| `swap [0/1]` | Swap TEC polarities for channel all/0/1 (For use with Zotino) | ## USB @@ -180,9 +181,11 @@ postfilter rate can be tuned with the `postfilter` command. ## Thermo-Electric Cooling (TEC) -- Connect TEC module device 0 to TEC0- and TEC0+. -- Connect TEC module device 1 to TEC1- and TEC1+. -- The GND pin is for shielding not for sinking TEC module currents. +- Connect TEC module device 0 to TEC0-/T0- and TEC0+/T0+. +- Connect TEC module device 1 to TEC1-/T1- and TEC1+/T1+. +- The GND pin is for shielding, not for sinking TEC module currents. + +For Zotino temperature regulation, connect an IDC cable to the internal Zotino header labeled TEC1/TEC2. For pre 3.0 Thermostats, use the `swap` command for those channels. When using a TEC module with the Thermostat, the Thermostat expects the thermal load (where the thermistor is located) to cool down with a positive software current set point, and heat up with a negative current set point. diff --git a/src/channel_state.rs b/src/channel_state.rs index 4820131..63beaf9 100644 --- a/src/channel_state.rs +++ b/src/channel_state.rs @@ -35,6 +35,7 @@ pub struct ChannelState { pub pid_engaged: bool, pub pid: pid::Controller, pub sh: sh::Parameters, + pub swap_tec_polarity: bool, } impl ChannelState { @@ -51,6 +52,7 @@ impl ChannelState { pid_engaged: false, pid: pid::Controller::new(pid::Parameters::default()), sh: sh::Parameters::default(), + swap_tec_polarity: false, } } diff --git a/src/channels.rs b/src/channels.rs index 488a8d6..7aed719 100644 --- a/src/channels.rs +++ b/src/channels.rs @@ -118,7 +118,11 @@ impl<'a> Channels<'a> { pub fn get_i(&mut self, channel: usize) -> ElectricCurrent { let i_set = self.channel_state(channel).i_set; - i_set + if self.channel_state(channel).swap_tec_polarity { + -i_set + } else { + i_set + } } /// i_set DAC @@ -137,13 +141,16 @@ impl<'a> Channels<'a> { // Silently clamp i_set let i_ceiling = ElectricCurrent::new::(MAX_TEC_I); let i_floor = ElectricCurrent::new::(-MAX_TEC_I); - let i_set = i_set.min(i_ceiling).max(i_floor); + let mut i_set = i_set.min(i_ceiling).max(i_floor); let vref_meas = match channel.into() { 0 => self.channel0.vref_meas, 1 => self.channel1.vref_meas, _ => unreachable!(), }; + if self.channel_state(channel).swap_tec_polarity { + i_set = -i_set; + } let center_point = vref_meas; let r_sense = ElectricalResistance::new::(R_SENSE); let voltage = i_set * 10.0 * r_sense + center_point; @@ -390,7 +397,12 @@ impl<'a> Channels<'a> { // Get current passing through TEC pub fn get_tec_i(&mut self, channel: usize) -> ElectricCurrent { - (self.read_itec(channel) - self.read_vref(channel)) / ElectricalResistance::new::(0.4) + let tec_i = (self.read_itec(channel) - self.read_vref(channel)) / ElectricalResistance::new::(0.4); + if self.channel_state(channel).swap_tec_polarity { + -tec_i + } else { + tec_i + } } // Get voltage across TEC diff --git a/src/command_handler.rs b/src/command_handler.rs index efea1b8..f982ac0 100644 --- a/src/command_handler.rs +++ b/src/command_handler.rs @@ -412,6 +412,16 @@ impl Handler { } } + fn swap_tec_polarity (socket: &mut TcpSocket, channels: &mut Channels, channel: Option) -> Result { + for c in 0..CHANNELS { + if channel.is_none() || channel == Some(c) { + channels.channel_state(c).swap_tec_polarity = !channels.channel_state(c).swap_tec_polarity; + } + } + send_line(socket, b"{}"); + Ok(Handler::Handled) + } + pub fn handle_command(command: Command, socket: &mut TcpSocket, channels: &mut Channels, session: &Session, store: &mut FlashStore, ipv4_config: &mut Ipv4Config, fan_ctrl: &mut FanCtrl, hwrev: HWRev) -> Result { match command { Command::Quit => Ok(Handler::CloseSocket), @@ -441,6 +451,7 @@ impl Handler { Command::FanCurve { k_a, k_b, k_c } => Handler::fan_curve(socket, fan_ctrl, k_a, k_b, k_c), Command::FanCurveDefaults => Handler::fan_defaults(socket, fan_ctrl), Command::ShowHWRev => Handler::show_hwrev(socket, hwrev), + Command::SwapTECPolarity { channel } => Handler::swap_tec_polarity(socket, channels, channel), } } } \ No newline at end of file diff --git a/src/command_parser.rs b/src/command_parser.rs index b9e70bb..1f9f49c 100644 --- a/src/command_parser.rs +++ b/src/command_parser.rs @@ -191,6 +191,9 @@ pub enum Command { }, FanCurveDefaults, ShowHWRev, + SwapTECPolarity { + channel: Option, + }, } fn end(input: &[u8]) -> IResult<&[u8], ()> { @@ -584,6 +587,22 @@ fn fan_curve(input: &[u8]) -> IResult<&[u8], Result> { ))(input) } +fn swap(input: &[u8]) -> IResult<&[u8], Result> { + let (input, _) = tag("swap")(input)?; + let (input, channel) = alt(( + |input| { + let (input, _) = whitespace(input)?; + let (input, channel) = channel(input)?; + let (input, _) = end(input)?; + Ok((input, Some(channel))) + }, + value(None, end) + ))(input)?; + + let result = Ok(Command::SwapTECPolarity { channel }); + Ok((input, result)) +} + fn command(input: &[u8]) -> IResult<&[u8], Result> { alt((value(Ok(Command::Quit), tag("quit")), load, @@ -600,6 +619,7 @@ fn command(input: &[u8]) -> IResult<&[u8], Result> { fan, fan_curve, value(Ok(Command::ShowHWRev), tag("hwrev")), + swap, ))(input) } diff --git a/src/config.rs b/src/config.rs index e7286fa..5c9d08e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -22,6 +22,7 @@ pub struct ChannelConfig { pwm: PwmLimits, /// uses variant `PostFilter::Invalid` instead of `None` to save space adc_postfilter: PostFilter, + swap_tec_polarity: bool, } impl ChannelConfig { @@ -41,6 +42,7 @@ impl ChannelConfig { sh: state.sh.clone(), pwm, adc_postfilter, + swap_tec_polarity: state.swap_tec_polarity, } } @@ -51,6 +53,7 @@ impl ChannelConfig { state.pid.target = self.pid_target.into(); state.pid_engaged = self.pid_engaged; state.sh = self.sh.clone(); + state.swap_tec_polarity = self.swap_tec_polarity; self.pwm.apply(channels, channel);