forked from M-Labs/thermostat
Move HWRev to separate file, address PR comments, emit warning on the using PWM on unsupported fans
Signed-off-by: Egor Savkin <es@m-labs.hk>
This commit is contained in:
parent
83d5c28a67
commit
2c9436a0b3
|
@ -129,6 +129,7 @@ formatted as line-delimited JSON.
|
||||||
| `fan auto` | Enable automatic fan speed control |
|
| `fan auto` | Enable automatic fan speed control |
|
||||||
| `fcurve <a> <b> <c>` | Set fan controller curve coefficients (see *Fan control* section) |
|
| `fcurve <a> <b> <c>` | Set fan controller curve coefficients (see *Fan control* section) |
|
||||||
| `fcurve default` | Set fan controller curve coefficients to defaults (see *Fan control* section) |
|
| `fcurve default` | Set fan controller curve coefficients to defaults (see *Fan control* section) |
|
||||||
|
| `hwrev` | Show hardware revision |
|
||||||
|
|
||||||
|
|
||||||
## USB
|
## USB
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use core::cmp::max_by;
|
use core::cmp::max_by;
|
||||||
use heapless::{consts::{U2, U1024}, Vec};
|
use heapless::{consts::U2, Vec};
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
use smoltcp::time::Instant;
|
use smoltcp::time::Instant;
|
||||||
use stm32f4xx_hal::hal;
|
use stm32f4xx_hal::hal;
|
||||||
|
@ -17,9 +17,9 @@ use crate::{
|
||||||
channel::{Channel, Channel0, Channel1},
|
channel::{Channel, Channel0, Channel1},
|
||||||
channel_state::ChannelState,
|
channel_state::ChannelState,
|
||||||
command_parser::{CenterPoint, PwmPin},
|
command_parser::{CenterPoint, PwmPin},
|
||||||
|
command_handler::JsonBuffer,
|
||||||
pins,
|
pins,
|
||||||
steinhart_hart,
|
steinhart_hart,
|
||||||
fan_ctrl::HWRev,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const CHANNELS: usize = 2;
|
pub const CHANNELS: usize = 2;
|
||||||
|
@ -35,7 +35,6 @@ pub struct Channels {
|
||||||
/// stm32f4 integrated adc
|
/// stm32f4 integrated adc
|
||||||
pins_adc: pins::PinsAdc,
|
pins_adc: pins::PinsAdc,
|
||||||
pub pwm: pins::PwmPins,
|
pub pwm: pins::PwmPins,
|
||||||
pub hwrev: HWRev,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Channels {
|
impl Channels {
|
||||||
|
@ -57,8 +56,7 @@ impl Channels {
|
||||||
let channel1 = Channel::new(pins.channel1, adc_calibration1);
|
let channel1 = Channel::new(pins.channel1, adc_calibration1);
|
||||||
let pins_adc = pins.pins_adc;
|
let pins_adc = pins.pins_adc;
|
||||||
let pwm = pins.pwm;
|
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: HWRev::detect_hw_rev(&pins.hwrev)};
|
|
||||||
for channel in 0..CHANNELS {
|
for channel in 0..CHANNELS {
|
||||||
channels.channel_state(channel).vref = channels.read_vref(channel);
|
channels.channel_state(channel).vref = channels.read_vref(channel);
|
||||||
channels.calibrate_dac_value(channel);
|
channels.calibrate_dac_value(channel);
|
||||||
|
@ -458,7 +456,6 @@ impl Channels {
|
||||||
tec_i,
|
tec_i,
|
||||||
tec_u_meas: self.get_tec_v(channel),
|
tec_u_meas: self.get_tec_v(channel),
|
||||||
pid_output,
|
pid_output,
|
||||||
hwrev: self.hwrev
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,8 +528,6 @@ impl Channels {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type JsonBuffer = Vec<u8, U1024>;
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct Report {
|
pub struct Report {
|
||||||
channel: usize,
|
channel: usize,
|
||||||
|
@ -550,7 +545,6 @@ pub struct Report {
|
||||||
tec_i: ElectricCurrent,
|
tec_i: ElectricCurrent,
|
||||||
tec_u_meas: ElectricPotential,
|
tec_u_meas: ElectricPotential,
|
||||||
pid_output: ElectricCurrent,
|
pid_output: ElectricCurrent,
|
||||||
hwrev: HWRev,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CenterPointJson(CenterPoint);
|
pub struct CenterPointJson(CenterPoint);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use smoltcp::socket::TcpSocket;
|
use smoltcp::socket::TcpSocket;
|
||||||
use log::{error, warn};
|
use log::{error, warn};
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
|
use heapless::{consts::U1024, Vec};
|
||||||
use super::{
|
use super::{
|
||||||
net,
|
net,
|
||||||
command_parser::{
|
command_parser::{
|
||||||
|
@ -24,6 +25,7 @@ use super::{
|
||||||
flash_store::FlashStore,
|
flash_store::FlashStore,
|
||||||
session::Session,
|
session::Session,
|
||||||
FanCtrl,
|
FanCtrl,
|
||||||
|
hw_rev::HWRev,
|
||||||
};
|
};
|
||||||
|
|
||||||
use uom::{
|
use uom::{
|
||||||
|
@ -56,6 +58,8 @@ pub enum Error {
|
||||||
FlashError
|
FlashError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type JsonBuffer = Vec<u8, U1024>;
|
||||||
|
|
||||||
fn send_line(socket: &mut TcpSocket, data: &[u8]) -> bool {
|
fn send_line(socket: &mut TcpSocket, data: &[u8]) -> bool {
|
||||||
let send_free = socket.send_capacity() - socket.send_queue();
|
let send_free = socket.send_capacity() - socket.send_queue();
|
||||||
if data.len() > send_free + 1 {
|
if data.len() > send_free + 1 {
|
||||||
|
@ -345,7 +349,11 @@ impl Handler {
|
||||||
fn set_fan(socket: &mut TcpSocket, fan_pwm: u32, fan_ctrl: &mut FanCtrl) -> Result<Handler, Error> {
|
fn set_fan(socket: &mut TcpSocket, fan_pwm: u32, fan_ctrl: &mut FanCtrl) -> Result<Handler, Error> {
|
||||||
fan_ctrl.set_auto_mode(false);
|
fan_ctrl.set_auto_mode(false);
|
||||||
fan_ctrl.set_pwm(fan_pwm);
|
fan_ctrl.set_pwm(fan_pwm);
|
||||||
send_line(socket, b"{}");
|
if fan_ctrl.is_default_auto() {
|
||||||
|
send_line(socket, b"{}");
|
||||||
|
} else {
|
||||||
|
send_line(socket, b"{ \"warning\": \"this fan doesn't have full PWM support. Use it on your own risk!\" }");
|
||||||
|
}
|
||||||
Ok(Handler::Handled)
|
Ok(Handler::Handled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,11 +373,15 @@ impl Handler {
|
||||||
|
|
||||||
fn fan_auto(socket: &mut TcpSocket, fan_ctrl: &mut FanCtrl) -> Result<Handler, Error> {
|
fn fan_auto(socket: &mut TcpSocket, fan_ctrl: &mut FanCtrl) -> Result<Handler, Error> {
|
||||||
fan_ctrl.set_auto_mode(true);
|
fan_ctrl.set_auto_mode(true);
|
||||||
send_line(socket, b"{}");
|
if fan_ctrl.is_default_auto() {
|
||||||
|
send_line(socket, b"{}");
|
||||||
|
} else {
|
||||||
|
send_line(socket, b"{ \"warning\": \"this fan doesn't have full PWM support. Use it on your own risk!\" }");
|
||||||
|
}
|
||||||
Ok(Handler::Handled)
|
Ok(Handler::Handled)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fan_curve(socket: &mut TcpSocket, fan_ctrl: &mut FanCtrl, k_a: f64, k_b: f64, k_c: f64) -> Result<Handler, Error> {
|
fn fan_curve(socket: &mut TcpSocket, fan_ctrl: &mut FanCtrl, k_a: f32, k_b: f32, k_c: f32) -> Result<Handler, Error> {
|
||||||
fan_ctrl.set_curve(k_a, k_b, k_c);
|
fan_ctrl.set_curve(k_a, k_b, k_c);
|
||||||
send_line(socket, b"{}");
|
send_line(socket, b"{}");
|
||||||
Ok(Handler::Handled)
|
Ok(Handler::Handled)
|
||||||
|
@ -381,7 +393,21 @@ impl Handler {
|
||||||
Ok(Handler::Handled)
|
Ok(Handler::Handled)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_command(command: Command, socket: &mut TcpSocket, channels: &mut Channels, session: &Session, leds: &mut Leds, store: &mut FlashStore, ipv4_config: &mut Ipv4Config, fan_ctrl: &mut FanCtrl) -> Result<Self, Error> {
|
fn show_hwrev(socket: &mut TcpSocket, hwrev: HWRev) -> Result<Handler, Error> {
|
||||||
|
match hwrev.summary() {
|
||||||
|
Ok(buf) => {
|
||||||
|
send_line(socket, &buf);
|
||||||
|
Ok(Handler::Handled)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("unable to serialize HWRev summary: {:?}", e);
|
||||||
|
let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e);
|
||||||
|
Err(Error::ReportError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_command(command: Command, socket: &mut TcpSocket, channels: &mut Channels, session: &Session, leds: &mut Leds, store: &mut FlashStore, ipv4_config: &mut Ipv4Config, fan_ctrl: &mut FanCtrl, hwrev: HWRev) -> Result<Self, Error> {
|
||||||
match command {
|
match command {
|
||||||
Command::Quit => Ok(Handler::CloseSocket),
|
Command::Quit => Ok(Handler::CloseSocket),
|
||||||
Command::Reporting(_reporting) => Handler::reporting(socket),
|
Command::Reporting(_reporting) => Handler::reporting(socket),
|
||||||
|
@ -409,6 +435,7 @@ impl Handler {
|
||||||
Command::FanAuto => Handler::fan_auto(socket, fan_ctrl),
|
Command::FanAuto => Handler::fan_auto(socket, fan_ctrl),
|
||||||
Command::FanCurve { k_a, k_b, k_c } => Handler::fan_curve(socket, fan_ctrl, k_a, k_b, k_c),
|
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::FanCurveDefaults => Handler::fan_defaults(socket, fan_ctrl),
|
||||||
|
Command::ShowHWRev => Handler::show_hwrev(socket, hwrev),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -185,11 +185,12 @@ pub enum Command {
|
||||||
FanAuto,
|
FanAuto,
|
||||||
ShowFan,
|
ShowFan,
|
||||||
FanCurve {
|
FanCurve {
|
||||||
k_a: f64,
|
k_a: f32,
|
||||||
k_b: f64,
|
k_b: f32,
|
||||||
k_c: f64,
|
k_c: f32,
|
||||||
},
|
},
|
||||||
FanCurveDefaults,
|
FanCurveDefaults,
|
||||||
|
ShowHWRev,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end(input: &[u8]) -> IResult<&[u8], ()> {
|
fn end(input: &[u8]) -> IResult<&[u8], ()> {
|
||||||
|
@ -573,7 +574,7 @@ fn fan_curve(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
||||||
let (input, k_c) = float(input)?;
|
let (input, k_c) = float(input)?;
|
||||||
let (input, _) = end(input)?;
|
let (input, _) = end(input)?;
|
||||||
if k_a.is_ok() && k_b.is_ok() && k_c.is_ok() {
|
if k_a.is_ok() && k_b.is_ok() && k_c.is_ok() {
|
||||||
Ok((input, Ok(Command::FanCurve { k_a: k_a.unwrap(), k_b: k_b.unwrap(), k_c: k_c.unwrap() })))
|
Ok((input, Ok(Command::FanCurve { k_a: k_a.unwrap() as f32, k_b: k_b.unwrap() as f32, k_c: k_c.unwrap() as f32 })))
|
||||||
} else {
|
} else {
|
||||||
Err(nom::Err::Incomplete(Needed::Size(3)))
|
Err(nom::Err::Incomplete(Needed::Size(3)))
|
||||||
}
|
}
|
||||||
|
@ -601,6 +602,7 @@ fn command(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
||||||
value(Ok(Command::Dfu), tag("dfu")),
|
value(Ok(Command::Dfu), tag("dfu")),
|
||||||
fan,
|
fan,
|
||||||
fan_curve,
|
fan_curve,
|
||||||
|
value(Ok(Command::ShowHWRev), tag("hwrev")),
|
||||||
))(input)
|
))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -856,4 +858,10 @@ mod test {
|
||||||
let command = Command::parse(b"fcurve default");
|
let command = Command::parse(b"fcurve default");
|
||||||
assert_eq!(command, Ok(Command::FanCurveDefaults));
|
assert_eq!(command, Ok(Command::FanCurveDefaults));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_hwrev() {
|
||||||
|
let command = Command::parse(b"hwrev");
|
||||||
|
assert_eq!(command, Ok(Command::ShowHWRev));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
115
src/fan_ctrl.rs
115
src/fan_ctrl.rs
|
@ -6,65 +6,62 @@ use stm32f4xx_hal::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
pins::HWRevPins,
|
hw_rev::HWRev,
|
||||||
channels::JsonBuffer,
|
command_handler::JsonBuffer,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type FanPin = PwmChannels<TIM8, pwm::C4>;
|
pub type FanPin = PwmChannels<TIM8, pwm::C4>;
|
||||||
|
|
||||||
// as stated in the schematics
|
// as stated in the schematics
|
||||||
const MAX_TEC_I: f64 = 3.0;
|
const MAX_TEC_I: f32 = 3.0;
|
||||||
|
|
||||||
const MAX_USER_FAN_PWM: f64 = 100.0;
|
const MAX_USER_FAN_PWM: f32 = 100.0;
|
||||||
const MIN_USER_FAN_PWM: f64 = 1.0;
|
const MIN_USER_FAN_PWM: f32 = 1.0;
|
||||||
const MAX_FAN_PWM: f64 = 1.0;
|
const MAX_FAN_PWM: f32 = 1.0;
|
||||||
// below this value motor's autostart feature may fail
|
// below this value motor's autostart feature may fail
|
||||||
const MIN_FAN_PWM: f64 = 0.04;
|
const MIN_FAN_PWM: f32 = 0.04;
|
||||||
|
|
||||||
const DEFAULT_K_A: f64 = 1.0;
|
const DEFAULT_K_A: f32 = 1.0;
|
||||||
const DEFAULT_K_B: f64 = 0.0;
|
const DEFAULT_K_B: f32 = 0.0;
|
||||||
const DEFAULT_K_C: f64 = 0.0;
|
const DEFAULT_K_C: f32 = 0.0;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Serialize, Copy, Clone)]
|
|
||||||
pub struct HWRev {
|
|
||||||
pub major: u8,
|
|
||||||
pub minor: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct FanCtrl {
|
pub struct FanCtrl {
|
||||||
fan: FanPin,
|
fan: FanPin,
|
||||||
fan_auto: bool,
|
fan_auto: bool,
|
||||||
available: bool,
|
available: bool,
|
||||||
k_a: f64,
|
default_auto: bool,
|
||||||
k_b: f64,
|
pwm_enabled: bool,
|
||||||
k_c: f64,
|
k_a: f32,
|
||||||
abs_max_tec_i: f64,
|
k_b: f32,
|
||||||
|
k_c: f32,
|
||||||
|
abs_max_tec_i: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FanCtrl {
|
impl FanCtrl {
|
||||||
pub fn new(mut fan: FanPin, hwrev: &HWRev) -> Self {
|
pub fn new(fan: FanPin, hwrev: HWRev) -> Self {
|
||||||
let available = hwrev.fan_available();
|
let available = hwrev.fan_available();
|
||||||
|
let default_auto = hwrev.fan_default_auto();
|
||||||
|
|
||||||
if available {
|
let mut fan_ctrl = FanCtrl {
|
||||||
fan.set_duty(0);
|
|
||||||
fan.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
FanCtrl {
|
|
||||||
fan,
|
fan,
|
||||||
available,
|
available,
|
||||||
// do not enable auto mode by default,
|
// do not enable auto mode by default,
|
||||||
// but allow to turn it on on customer's own risk
|
// but allow to turn it on on user's own risk
|
||||||
fan_auto: hwrev.fan_auto_mode_available(),
|
default_auto,
|
||||||
|
fan_auto: default_auto,
|
||||||
|
pwm_enabled: false,
|
||||||
k_a: DEFAULT_K_A,
|
k_a: DEFAULT_K_A,
|
||||||
k_b: DEFAULT_K_B,
|
k_b: DEFAULT_K_B,
|
||||||
k_c: DEFAULT_K_C,
|
k_c: DEFAULT_K_C,
|
||||||
abs_max_tec_i: 0f64,
|
abs_max_tec_i: 0f32,
|
||||||
|
};
|
||||||
|
if fan_ctrl.fan_auto {
|
||||||
|
fan_ctrl.enable_pwm();
|
||||||
}
|
}
|
||||||
|
fan_ctrl
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cycle(&mut self, abs_max_tec_i: f64) {
|
pub fn cycle(&mut self, abs_max_tec_i: f32) {
|
||||||
self.abs_max_tec_i = abs_max_tec_i;
|
self.abs_max_tec_i = abs_max_tec_i;
|
||||||
self.adjust_speed();
|
self.adjust_speed();
|
||||||
}
|
}
|
||||||
|
@ -99,7 +96,7 @@ impl FanCtrl {
|
||||||
self.fan_auto = fan_auto;
|
self.fan_auto = fan_auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_curve(&mut self, k_a: f64, k_b: f64, k_c: f64) {
|
pub fn set_curve(&mut self, k_a: f32, k_b: f32, k_c: f32) {
|
||||||
self.k_a = k_a;
|
self.k_a = k_a;
|
||||||
self.k_b = k_b;
|
self.k_b = k_b;
|
||||||
self.k_c = k_c;
|
self.k_c = k_c;
|
||||||
|
@ -109,55 +106,47 @@ impl FanCtrl {
|
||||||
self.set_curve(DEFAULT_K_A, DEFAULT_K_B, DEFAULT_K_C);
|
self.set_curve(DEFAULT_K_A, DEFAULT_K_B, DEFAULT_K_C);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_pwm(&mut self, fan_pwm: u32) -> f64 {
|
pub fn set_pwm(&mut self, fan_pwm: u32) -> f32 {
|
||||||
|
if !self.pwm_enabled {
|
||||||
|
self.enable_pwm()
|
||||||
|
}
|
||||||
let fan_pwm = fan_pwm.min(MAX_USER_FAN_PWM as u32).max(MIN_USER_FAN_PWM as u32);
|
let fan_pwm = fan_pwm.min(MAX_USER_FAN_PWM as u32).max(MIN_USER_FAN_PWM as u32);
|
||||||
let duty = Self::scale_number(fan_pwm as f64, MIN_FAN_PWM, MAX_FAN_PWM, MIN_USER_FAN_PWM, MAX_USER_FAN_PWM);
|
let duty = Self::scale_number(fan_pwm as f32, MIN_FAN_PWM, MAX_FAN_PWM, MIN_USER_FAN_PWM, MAX_USER_FAN_PWM);
|
||||||
let max = self.fan.get_max_duty();
|
let max = self.fan.get_max_duty();
|
||||||
let value = ((duty * (max as f64)) as u16).min(max);
|
let value = ((duty * (max as f32)) as u16).min(max);
|
||||||
self.fan.set_duty(value);
|
self.fan.set_duty(value);
|
||||||
value as f64 / (max as f64)
|
value as f32 / (max as f32)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scale_number(unscaled: f64, to_min: f64, to_max: f64, from_min: f64, from_max: f64) -> f64 {
|
pub fn is_default_auto(&self) -> bool {
|
||||||
|
self.default_auto
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scale_number(unscaled: f32, to_min: f32, to_max: f32, from_min: f32, from_max: f32) -> f32 {
|
||||||
(to_max - to_min) * (unscaled - from_min) / (from_max - from_min) + to_min
|
(to_max - to_min) * (unscaled - from_min) / (from_max - from_min) + to_min
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pwm(&self) -> u32 {
|
fn get_pwm(&self) -> u32 {
|
||||||
let duty = self.fan.get_duty();
|
let duty = self.fan.get_duty();
|
||||||
let max = self.fan.get_max_duty();
|
let max = self.fan.get_max_duty();
|
||||||
Self::scale_number(duty as f64 / (max as f64), MIN_USER_FAN_PWM, MAX_USER_FAN_PWM, MIN_FAN_PWM, MAX_FAN_PWM).round() as u32
|
Self::scale_number(duty as f32 / (max as f32), MIN_USER_FAN_PWM, MAX_USER_FAN_PWM, MIN_FAN_PWM, MAX_FAN_PWM).round() as u32
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl HWRev {
|
fn enable_pwm(&mut self) {
|
||||||
pub fn detect_hw_rev(hwrev_pins: &HWRevPins) -> Self {
|
if self.available {
|
||||||
let (h0, h1, h2, h3) = (hwrev_pins.hwrev0.is_high(), hwrev_pins.hwrev1.is_high(),
|
self.fan.set_duty(0);
|
||||||
hwrev_pins.hwrev2.is_high(), hwrev_pins.hwrev3.is_high());
|
self.fan.enable();
|
||||||
match (h0, h1, h2, h3) {
|
self.pwm_enabled = true;
|
||||||
(true, true, true, false) => HWRev { major: 1, minor: 0 },
|
|
||||||
(true, false, false, false) => HWRev { major: 2, minor: 0 },
|
|
||||||
(false, true, false, false) => HWRev { major: 2, minor: 2 },
|
|
||||||
(_, _, _, _) => HWRev { major: 0, minor: 0 }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fan_available(&self) -> bool {
|
|
||||||
self.major == 2 && self.minor == 2
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fan_auto_mode_available(&self) -> bool {
|
|
||||||
// see https://github.com/sinara-hw/Thermostat/issues/115 and
|
|
||||||
// https://git.m-labs.hk/M-Labs/thermostat/issues/69#issuecomment-6464 for explanation
|
|
||||||
self.fan_available() && self.minor != 2
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct FanSummary {
|
pub struct FanSummary {
|
||||||
fan_pwm: u32,
|
fan_pwm: u32,
|
||||||
abs_max_tec_i: f64,
|
abs_max_tec_i: f32,
|
||||||
auto_mode: bool,
|
auto_mode: bool,
|
||||||
k_a: f64,
|
k_a: f32,
|
||||||
k_b: f64,
|
k_b: f32,
|
||||||
k_c: f64,
|
k_c: f32,
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
pins::HWRevPins,
|
||||||
|
command_handler::JsonBuffer
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Serialize, Copy, Clone)]
|
||||||
|
pub struct HWRev {
|
||||||
|
pub major: u8,
|
||||||
|
pub minor: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl HWRev {
|
||||||
|
pub fn detect_hw_rev(hwrev_pins: &HWRevPins) -> Self {
|
||||||
|
let (h0, h1, h2, h3) = (hwrev_pins.hwrev0.is_high(), hwrev_pins.hwrev1.is_high(),
|
||||||
|
hwrev_pins.hwrev2.is_high(), hwrev_pins.hwrev3.is_high());
|
||||||
|
match (h0, h1, h2, h3) {
|
||||||
|
(true, true, true, false) => HWRev { major: 1, minor: 0 },
|
||||||
|
(true, false, false, false) => HWRev { major: 2, minor: 0 },
|
||||||
|
(false, true, false, false) => HWRev { major: 2, minor: 2 },
|
||||||
|
(_, _, _, _) => HWRev { major: 0, minor: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fan_available(&self) -> bool {
|
||||||
|
self.major == 2 && self.minor == 2
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fan_default_auto(&self) -> bool {
|
||||||
|
// see https://github.com/sinara-hw/Thermostat/issues/115 and
|
||||||
|
// https://git.m-labs.hk/M-Labs/thermostat/issues/69#issuecomment-6464 for explanation
|
||||||
|
self.fan_available() && self.minor != 2
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn summary(&self) -> Result<JsonBuffer, serde_json_core::ser::Error> {
|
||||||
|
serde_json_core::to_vec(&self)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
10
src/main.rs
10
src/main.rs
|
@ -55,6 +55,8 @@ mod command_handler;
|
||||||
use command_handler::Handler;
|
use command_handler::Handler;
|
||||||
mod fan_ctrl;
|
mod fan_ctrl;
|
||||||
use fan_ctrl::FanCtrl;
|
use fan_ctrl::FanCtrl;
|
||||||
|
mod hw_rev;
|
||||||
|
use hw_rev::HWRev;
|
||||||
|
|
||||||
const HSE: MegaHertz = MegaHertz(8);
|
const HSE: MegaHertz = MegaHertz(8);
|
||||||
#[cfg(not(feature = "semihosting"))]
|
#[cfg(not(feature = "semihosting"))]
|
||||||
|
@ -138,6 +140,8 @@ fn main() -> ! {
|
||||||
|
|
||||||
let mut store = flash_store::store(dp.FLASH);
|
let mut store = flash_store::store(dp.FLASH);
|
||||||
|
|
||||||
|
let hwrev = HWRev::detect_hw_rev(&pins.hwrev);
|
||||||
|
|
||||||
let mut channels = Channels::new(pins);
|
let mut channels = Channels::new(pins);
|
||||||
for c in 0..CHANNELS {
|
for c in 0..CHANNELS {
|
||||||
match store.read_value::<ChannelConfig>(CHANNEL_CONFIG_KEY[c]) {
|
match store.read_value::<ChannelConfig>(CHANNEL_CONFIG_KEY[c]) {
|
||||||
|
@ -150,7 +154,7 @@ fn main() -> ! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut fan_ctrl = FanCtrl::new(fan, &channels.hwrev);
|
let mut fan_ctrl = FanCtrl::new(fan, hwrev);
|
||||||
|
|
||||||
// default net config:
|
// default net config:
|
||||||
let mut ipv4_config = Ipv4Config {
|
let mut ipv4_config = Ipv4Config {
|
||||||
|
@ -185,7 +189,7 @@ fn main() -> ! {
|
||||||
server.for_each(|_, session| session.set_report_pending(channel.into()));
|
server.for_each(|_, session| session.set_report_pending(channel.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fan_ctrl.cycle(channels.current_abs_max_tec_i());
|
fan_ctrl.cycle(channels.current_abs_max_tec_i() as f32);
|
||||||
|
|
||||||
let instant = Instant::from_millis(i64::from(timer::now()));
|
let instant = Instant::from_millis(i64::from(timer::now()));
|
||||||
cortex_m::interrupt::free(net::clear_pending);
|
cortex_m::interrupt::free(net::clear_pending);
|
||||||
|
@ -210,7 +214,7 @@ fn main() -> ! {
|
||||||
// Do nothing and feed more data to the line reader in the next loop cycle.
|
// Do nothing and feed more data to the line reader in the next loop cycle.
|
||||||
Ok(SessionInput::Nothing) => {}
|
Ok(SessionInput::Nothing) => {}
|
||||||
Ok(SessionInput::Command(command)) => {
|
Ok(SessionInput::Command(command)) => {
|
||||||
match Handler::handle_command(command, &mut socket, &mut channels, session, &mut leds, &mut store, &mut ipv4_config, &mut fan_ctrl) {
|
match Handler::handle_command(command, &mut socket, &mut channels, session, &mut leds, &mut store, &mut ipv4_config, &mut fan_ctrl, hwrev) {
|
||||||
Ok(Handler::NewIPV4(ip)) => new_ipv4_config = Some(ip),
|
Ok(Handler::NewIPV4(ip)) => new_ipv4_config = Some(ip),
|
||||||
Ok(Handler::Handled) => {},
|
Ok(Handler::Handled) => {},
|
||||||
Ok(Handler::CloseSocket) => socket.close(),
|
Ok(Handler::CloseSocket) => socket.close(),
|
||||||
|
|
|
@ -227,7 +227,8 @@ impl Pins {
|
||||||
};
|
};
|
||||||
|
|
||||||
// According to `SUNON DC Brushless Fan & Blower(255-E)` catalogue p.36-37
|
// According to `SUNON DC Brushless Fan & Blower(255-E)` catalogue p.36-37
|
||||||
// Model name: MF35101V1-1000U-G99
|
// model MF35101V1-1000U-G99 doesn't have a PWM wire, so it is advised to have
|
||||||
|
// higher frequency to have less audible noise.
|
||||||
let fan = Timer::new(tim8, &clocks).pwm(gpioc.pc9.into_alternate(), 25u32.khz());
|
let fan = Timer::new(tim8, &clocks).pwm(gpioc.pc9.into_alternate(), 25u32.khz());
|
||||||
|
|
||||||
(pins, leds, eeprom, eth_pins, usb, fan)
|
(pins, leds, eeprom, eth_pins, usb, fan)
|
||||||
|
|
Loading…
Reference in New Issue