From ea2eb51b27055ae3483024385f32ee104055c5a3 Mon Sep 17 00:00:00 2001 From: Egor Savkin Date: Fri, 23 Dec 2022 13:13:33 +0800 Subject: [PATCH] Add fan warnings Signed-off-by: Egor Savkin --- src/fan_ctrl.rs | 73 +++++++++++++++++++++++++++++++++++++++++++++---- src/main.rs | 10 +++++-- 2 files changed, 75 insertions(+), 8 deletions(-) diff --git a/src/fan_ctrl.rs b/src/fan_ctrl.rs index 7f9a2f1..3c6d9cc 100644 --- a/src/fan_ctrl.rs +++ b/src/fan_ctrl.rs @@ -26,9 +26,10 @@ const MAX_TEC_I: f64 = 3.0; const MAX_FAN_PWM: f64 = 100.0; const MIN_FAN_PWM: f64 = 1.0; const TACHO_MEASURE_MS: i64 = 2500; +const TACHO_LOW_THRESHOLD: u32 = 100; const DEFAULT_K_A: f64 = 1.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)] pub struct HWRev { @@ -36,11 +37,20 @@ pub struct HWRev { pub minor: u8, } +#[derive(Serialize, Clone, Copy, PartialEq)] +pub enum FanStatus { + OK, + NotAvailable, + Stalled, + LowSignal, +} + struct TachoCtrl { tacho: TachoPin, tacho_cnt: u32, tacho_value: Option, prev_epoch: i64, + past_record: u64, } pub struct FanCtrl<'a> { @@ -52,6 +62,7 @@ pub struct FanCtrl<'a> { k_b: f64, k_c: f64, channels: &'a mut Channels, + last_status: FanStatus } impl<'a> FanCtrl<'a> { @@ -74,14 +85,22 @@ impl<'a> FanCtrl<'a> { k_b: DEFAULT_K_B, k_c: DEFAULT_K_C, channels, + last_status: FanStatus::OK, } } - pub fn cycle(&mut self) { + pub fn cycle(&mut self) -> Result<(), FanStatus>{ if self.available { self.tacho.cycle(); } 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 { @@ -91,6 +110,7 @@ impl<'a> FanCtrl<'a> { tacho: self.tacho.get(), abs_max_tec_i: self.channels.current_abs_max_tec_i(), auto_mode: self.fan_auto, + status: self.diagnose(), k_a: self.k_a, k_b: self.k_b, k_c: self.k_c, @@ -139,6 +159,13 @@ impl<'a> FanCtrl<'a> { 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 { let duty = self.fan.get_duty(); let max = self.fan.get_max_duty(); @@ -147,16 +174,17 @@ impl<'a> FanCtrl<'a> { } impl TachoCtrl { - pub fn new(tacho: TachoPin) -> Self { + fn new(tacho: TachoPin) -> Self { TachoCtrl { tacho, tacho_cnt: 0, tacho_value: None, 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, // since the interrupt should be unmasked in the cortex_m::peripheral::NVIC. // Also using interrupt-related workaround is the best @@ -169,7 +197,17 @@ impl TachoCtrl { 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(); if tacho_input { self.tacho.clear_interrupt_pending_bit(); @@ -179,14 +217,25 @@ impl TachoCtrl { let instant = Instant::from_millis(i64::from(timer::now())); if instant.millis - self.prev_epoch >= TACHO_MEASURE_MS { self.tacho_value = Some(self.tacho_cnt); + self.add_record(self.tacho_cnt); self.tacho_cnt = 0; self.prev_epoch = instant.millis; } } - pub fn get(&self) -> u32 { + fn get(&self) -> u32 { 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 { @@ -212,7 +261,19 @@ pub struct FanSummary { tacho: u32, abs_max_tec_i: f64, auto_mode: bool, + status: FanStatus, k_a: f64, k_b: 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(), + } + } } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 798c3b4..1005315 100644 --- a/src/main.rs +++ b/src/main.rs @@ -187,10 +187,10 @@ fn main() -> ! { if let Some(channel) = updated_channel { 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())); - cortex_m::interrupt::free(net::clear_pending); server.poll(instant) .unwrap_or_else(|e| { @@ -241,6 +241,12 @@ fn main() -> ! { } } } + match fan_status { + Ok(_) => {} + Err(status) => { + send_line(&mut socket, status.fmt_u8()); + } + }; } }); } else {