forked from M-Labs/thermostat
Add fan warnings
Signed-off-by: Egor Savkin <es@m-labs.hk>
This commit is contained in:
parent
58650d37f1
commit
ea2eb51b27
|
@ -26,9 +26,10 @@ const MAX_TEC_I: f64 = 3.0;
|
||||||
const MAX_FAN_PWM: f64 = 100.0;
|
const MAX_FAN_PWM: f64 = 100.0;
|
||||||
const MIN_FAN_PWM: f64 = 1.0;
|
const MIN_FAN_PWM: f64 = 1.0;
|
||||||
const TACHO_MEASURE_MS: i64 = 2500;
|
const TACHO_MEASURE_MS: i64 = 2500;
|
||||||
|
const TACHO_LOW_THRESHOLD: u32 = 100;
|
||||||
const DEFAULT_K_A: f64 = 1.0;
|
const DEFAULT_K_A: f64 = 1.0;
|
||||||
const DEFAULT_K_B: f64 = 0.0;
|
const DEFAULT_K_B: f64 = 0.0;
|
||||||
const DEFAULT_K_C: f64 = 0.0;
|
const DEFAULT_K_C: f64 = 0.04;
|
||||||
|
|
||||||
#[derive(Serialize, Copy, Clone)]
|
#[derive(Serialize, Copy, Clone)]
|
||||||
pub struct HWRev {
|
pub struct HWRev {
|
||||||
|
@ -36,11 +37,20 @@ pub struct HWRev {
|
||||||
pub minor: u8,
|
pub minor: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Clone, Copy, PartialEq)]
|
||||||
|
pub enum FanStatus {
|
||||||
|
OK,
|
||||||
|
NotAvailable,
|
||||||
|
Stalled,
|
||||||
|
LowSignal,
|
||||||
|
}
|
||||||
|
|
||||||
struct TachoCtrl {
|
struct TachoCtrl {
|
||||||
tacho: TachoPin,
|
tacho: TachoPin,
|
||||||
tacho_cnt: u32,
|
tacho_cnt: u32,
|
||||||
tacho_value: Option<u32>,
|
tacho_value: Option<u32>,
|
||||||
prev_epoch: i64,
|
prev_epoch: i64,
|
||||||
|
past_record: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FanCtrl<'a> {
|
pub struct FanCtrl<'a> {
|
||||||
|
@ -52,6 +62,7 @@ pub struct FanCtrl<'a> {
|
||||||
k_b: f64,
|
k_b: f64,
|
||||||
k_c: f64,
|
k_c: f64,
|
||||||
channels: &'a mut Channels,
|
channels: &'a mut Channels,
|
||||||
|
last_status: FanStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FanCtrl<'a> {
|
impl<'a> FanCtrl<'a> {
|
||||||
|
@ -74,14 +85,22 @@ impl<'a> FanCtrl<'a> {
|
||||||
k_b: DEFAULT_K_B,
|
k_b: DEFAULT_K_B,
|
||||||
k_c: DEFAULT_K_C,
|
k_c: DEFAULT_K_C,
|
||||||
channels,
|
channels,
|
||||||
|
last_status: FanStatus::OK,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cycle(&mut self) {
|
pub fn cycle(&mut self) -> Result<(), FanStatus>{
|
||||||
if self.available {
|
if self.available {
|
||||||
self.tacho.cycle();
|
self.tacho.cycle();
|
||||||
}
|
}
|
||||||
self.adjust_speed();
|
self.adjust_speed();
|
||||||
|
let diagnose = self.diagnose();
|
||||||
|
if diagnose != self.last_status {
|
||||||
|
self.last_status = diagnose;
|
||||||
|
Err(diagnose)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn summary(&mut self) -> Result<JsonBuffer, serde_json_core::ser::Error> {
|
pub fn summary(&mut self) -> Result<JsonBuffer, serde_json_core::ser::Error> {
|
||||||
|
@ -91,6 +110,7 @@ impl<'a> FanCtrl<'a> {
|
||||||
tacho: self.tacho.get(),
|
tacho: self.tacho.get(),
|
||||||
abs_max_tec_i: self.channels.current_abs_max_tec_i(),
|
abs_max_tec_i: self.channels.current_abs_max_tec_i(),
|
||||||
auto_mode: self.fan_auto,
|
auto_mode: self.fan_auto,
|
||||||
|
status: self.diagnose(),
|
||||||
k_a: self.k_a,
|
k_a: self.k_a,
|
||||||
k_b: self.k_b,
|
k_b: self.k_b,
|
||||||
k_c: self.k_c,
|
k_c: self.k_c,
|
||||||
|
@ -139,6 +159,13 @@ impl<'a> FanCtrl<'a> {
|
||||||
value as f64 / (max as f64)
|
value as f64 / (max as f64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn diagnose(&mut self) -> FanStatus {
|
||||||
|
if !self.available {
|
||||||
|
return FanStatus::NotAvailable;
|
||||||
|
}
|
||||||
|
self.tacho.diagnose()
|
||||||
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
@ -147,16 +174,17 @@ impl<'a> FanCtrl<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TachoCtrl {
|
impl TachoCtrl {
|
||||||
pub fn new(tacho: TachoPin) -> Self {
|
fn new(tacho: TachoPin) -> Self {
|
||||||
TachoCtrl {
|
TachoCtrl {
|
||||||
tacho,
|
tacho,
|
||||||
tacho_cnt: 0,
|
tacho_cnt: 0,
|
||||||
tacho_value: None,
|
tacho_value: None,
|
||||||
prev_epoch: 0,
|
prev_epoch: 0,
|
||||||
|
past_record: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(&mut self, exti: &mut EXTI, syscfg: &mut SysCfg) {
|
fn init(&mut self, exti: &mut EXTI, syscfg: &mut SysCfg) {
|
||||||
// These lines do not cause NVIC to run the ISR,
|
// These lines do not cause NVIC to run the ISR,
|
||||||
// since the interrupt should be unmasked in the cortex_m::peripheral::NVIC.
|
// since the interrupt should be unmasked in the cortex_m::peripheral::NVIC.
|
||||||
// Also using interrupt-related workaround is the best
|
// Also using interrupt-related workaround is the best
|
||||||
|
@ -169,7 +197,17 @@ impl TachoCtrl {
|
||||||
self.tacho.enable_interrupt(exti);
|
self.tacho.enable_interrupt(exti);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cycle(&mut self) {
|
#[inline]
|
||||||
|
fn add_record(&mut self, value: u32) {
|
||||||
|
self.past_record = self.past_record << 2;
|
||||||
|
if value >= TACHO_LOW_THRESHOLD {
|
||||||
|
self.past_record += 0b11;
|
||||||
|
} else if value > 0 && self.tacho_cnt < TACHO_LOW_THRESHOLD {
|
||||||
|
self.past_record += 0b10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cycle(&mut self) {
|
||||||
let tacho_input = self.tacho.check_interrupt();
|
let tacho_input = self.tacho.check_interrupt();
|
||||||
if tacho_input {
|
if tacho_input {
|
||||||
self.tacho.clear_interrupt_pending_bit();
|
self.tacho.clear_interrupt_pending_bit();
|
||||||
|
@ -179,14 +217,25 @@ impl TachoCtrl {
|
||||||
let instant = Instant::from_millis(i64::from(timer::now()));
|
let instant = Instant::from_millis(i64::from(timer::now()));
|
||||||
if instant.millis - self.prev_epoch >= TACHO_MEASURE_MS {
|
if instant.millis - self.prev_epoch >= TACHO_MEASURE_MS {
|
||||||
self.tacho_value = Some(self.tacho_cnt);
|
self.tacho_value = Some(self.tacho_cnt);
|
||||||
|
self.add_record(self.tacho_cnt);
|
||||||
self.tacho_cnt = 0;
|
self.tacho_cnt = 0;
|
||||||
self.prev_epoch = instant.millis;
|
self.prev_epoch = instant.millis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self) -> u32 {
|
fn get(&self) -> u32 {
|
||||||
self.tacho_value.unwrap_or(u32::MAX)
|
self.tacho_value.unwrap_or(u32::MAX)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn diagnose(&mut self) -> FanStatus {
|
||||||
|
if self.past_record & 0b11 == 0b11 {
|
||||||
|
FanStatus::OK
|
||||||
|
} else if self.past_record & 0xAAAAAAAAAAAAAAAA > 0 {
|
||||||
|
FanStatus::LowSignal
|
||||||
|
} else {
|
||||||
|
FanStatus::Stalled
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HWRev {
|
impl HWRev {
|
||||||
|
@ -212,7 +261,19 @@ pub struct FanSummary {
|
||||||
tacho: u32,
|
tacho: u32,
|
||||||
abs_max_tec_i: f64,
|
abs_max_tec_i: f64,
|
||||||
auto_mode: bool,
|
auto_mode: bool,
|
||||||
|
status: FanStatus,
|
||||||
k_a: f64,
|
k_a: f64,
|
||||||
k_b: f64,
|
k_b: f64,
|
||||||
k_c: f64,
|
k_c: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FanStatus {
|
||||||
|
pub fn fmt_u8(&self) -> &'static [u8] {
|
||||||
|
match *self {
|
||||||
|
FanStatus::OK => "Fan is OK".as_bytes(),
|
||||||
|
FanStatus::NotAvailable => "Fan is not available".as_bytes(),
|
||||||
|
FanStatus::Stalled => "Fan is stalled".as_bytes(),
|
||||||
|
FanStatus::LowSignal => "Fan is low signal".as_bytes(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
src/main.rs
10
src/main.rs
|
@ -187,10 +187,10 @@ fn main() -> ! {
|
||||||
if let Some(channel) = updated_channel {
|
if let Some(channel) = updated_channel {
|
||||||
server.for_each(|_, session| session.set_report_pending(channel.into()));
|
server.for_each(|_, session| session.set_report_pending(channel.into()));
|
||||||
}
|
}
|
||||||
fan_ctrl.cycle();
|
|
||||||
|
let fan_status = fan_ctrl.cycle();
|
||||||
|
|
||||||
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);
|
||||||
server.poll(instant)
|
server.poll(instant)
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
|
@ -241,6 +241,12 @@ fn main() -> ! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
match fan_status {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(status) => {
|
||||||
|
send_line(&mut socket, status.fmt_u8());
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue