diff --git a/fan-controller/src/fan/mod.rs b/fan-controller/src/fan/mod.rs index 2706224..2fe4087 100644 --- a/fan-controller/src/fan/mod.rs +++ b/fan-controller/src/fan/mod.rs @@ -1,6 +1,8 @@ //! ebm-pabst [RadiCal centrifugal fans in scroll housings for residential ventilation](https://www.ebmpapst.com/us/en/campaigns/product-campaigns/centrifugal-fans/radical-with-scroll-housing.html) //! specific configuration and constants +pub(crate) mod set_point; + use core::str::FromStr; use crate::modbus::ReadInputRegister; @@ -28,67 +30,28 @@ pub(crate) fn get_configuration() -> uart::Config { configuration } -pub(crate) const MAX_SET_POINT: u16 = 64_000; - -/// Describes the desired speed of the fan from 0 to [`MAX_SET_POINT`] -#[derive(Debug, Format, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub(crate) struct SetPoint(pub(crate) u16); - -#[derive(Debug, Format)] -pub(crate) struct SetPointOutOfBoundsError; - -impl SetPoint { - pub(crate) const ZERO: Self = match Self::new(0) { - Ok(setting) => setting, - Err(error) => panic!("Invalid value. This should not be reachable."), - }; - - pub(crate) const fn new(set_point: u16) -> Result { - if set_point > MAX_SET_POINT { - return Err(SetPointOutOfBoundsError); - } - - Ok(Self(set_point)) - } - - const fn get(&self) -> u16 { - self.0 - } -} - -pub(crate) enum ParseSetPointError { - ParseInt(core::num::ParseIntError), - SettingOutOfBounds(SetPointOutOfBoundsError), -} - -impl FromStr for SetPoint { - type Err = ParseSetPointError; - - fn from_str(s: &str) -> Result { - let set_point = s.parse().map_err(ParseSetPointError::ParseInt)?; - Self::new(set_point).map_err(ParseSetPointError::SettingOutOfBounds) - } -} - /// Settings specific to our use case for these fans. They are custom tuned to the house. /// For example, we don't run the fans at full speed to reduce wear on them pub(crate) mod user_setting { - use crate::fan; + use crate::fan::{ + self, + set_point::{self, SetPoint}, + }; /// Max speed 64000 / 3.3 - pub(crate) const LOW: fan::SetPoint = match fan::SetPoint::new(19_393) { + pub(crate) const LOW: SetPoint = match SetPoint::new(19_393) { Ok(setting) => setting, Err(error) => panic!("Invalid value"), }; /// Max speed 64000 / 2.4 - pub(crate) const MEDIUM: fan::SetPoint = match fan::SetPoint::new(26_666) { + pub(crate) const MEDIUM: SetPoint = match SetPoint::new(26_666) { Ok(setting) => setting, Err(error) => panic!("Invalid value"), }; /// Max speed 50% /// Not set to full speed to not wear out the fans - pub(crate) const HIGH: fan::SetPoint = match fan::SetPoint::new(fan::MAX_SET_POINT / 2) { + pub(crate) const HIGH: SetPoint = match SetPoint::new(set_point::MAX / 2) { Ok(setting) => setting, Err(error) => panic!("Invalid value"), }; diff --git a/fan-controller/src/fan/set_point.rs b/fan-controller/src/fan/set_point.rs new file mode 100644 index 0000000..c112641 --- /dev/null +++ b/fan-controller/src/fan/set_point.rs @@ -0,0 +1,45 @@ +use core::str::FromStr; + +use defmt::Format; + +pub(crate) const MAX: u16 = 64_000; + +/// Describes the desired speed of the fan from 0 to [`MAX_SET_POINT`] +#[derive(Debug, Format, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub(crate) struct SetPoint(pub(crate) u16); + +#[derive(Debug, Format)] +pub(crate) struct SetPointOutOfBoundsError; + +impl SetPoint { + pub(crate) const ZERO: Self = match Self::new(0) { + Ok(setting) => setting, + Err(error) => panic!("Invalid value. This should not be reachable."), + }; + + pub(crate) const fn new(set_point: u16) -> Result { + if set_point > MAX { + return Err(SetPointOutOfBoundsError); + } + + Ok(Self(set_point)) + } + + const fn get(&self) -> u16 { + self.0 + } +} + +pub(crate) enum ParseSetPointError { + ParseInt(core::num::ParseIntError), + SettingOutOfBounds(SetPointOutOfBoundsError), +} + +impl FromStr for SetPoint { + type Err = ParseSetPointError; + + fn from_str(s: &str) -> Result { + let set_point = s.parse().map_err(ParseSetPointError::ParseInt)?; + Self::new(set_point).map_err(ParseSetPointError::SettingOutOfBounds) + } +} diff --git a/fan-controller/src/main.rs b/fan-controller/src/main.rs index 2bf52b6..197d558 100644 --- a/fan-controller/src/main.rs +++ b/fan-controller/src/main.rs @@ -36,7 +36,8 @@ use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; use self::mqtt::packet; -use crate::fan::{Fan, ParseSetPointError, SetPoint}; +use crate::fan::set_point::{ParseSetPointError, SetPoint}; +use crate::fan::Fan; use crate::mqtt::packet::ping_request::PingRequest; use crate::mqtt::packet::{connect, publish, subscribe}; use crate::task::{set_up_network_stack, MqttBrokerConfiguration, Publish}; @@ -171,7 +172,7 @@ async fn input(pin_18: PIN_18) { .0 .try_get() .map(|state| state.setting) - .unwrap_or(fan::SetPoint::ZERO); + .unwrap_or(SetPoint::ZERO); FanState { setting, is_on: false, @@ -208,7 +209,7 @@ async fn update_fans() { // Only comparing on state causes button triggers to be ignored let mut previous = FAN_CONTROLLER.fan_states.0.try_get().unwrap_or(FanState { is_on: false, - setting: fan::SetPoint::ZERO, + setting: SetPoint::ZERO, }); loop { @@ -230,7 +231,7 @@ async fn update_fans() { state.setting } else { // Turn off fans - fan::SetPoint::ZERO + SetPoint::ZERO }; match fans.set_set_point(&setting).await { @@ -259,7 +260,7 @@ static FANS: Fans = Mutex::new(None); #[derive(PartialEq, Clone)] struct FanState { is_on: bool, - setting: fan::SetPoint, + setting: SetPoint, } struct FanController { @@ -316,7 +317,7 @@ async fn led_routine(pin_21: PIN_21, pin_20: PIN_20) { // Set initial state let mut current_state = FAN_CONTROLLER.fan_states.0.try_get().unwrap_or(FanState { is_on: false, - setting: fan::SetPoint::ZERO, + setting: SetPoint::ZERO, }); loop { diff --git a/fan-controller/src/modbus/client.rs b/fan-controller/src/modbus/client.rs index 3ee310f..0c5951f 100644 --- a/fan-controller/src/modbus/client.rs +++ b/fan-controller/src/modbus/client.rs @@ -11,7 +11,7 @@ use embedded_io_async::{Read, Write}; use crate::{ configuration, - fan::{self, address, holding_registers, Fan, FanResponse, SetPoint, BAUD_RATE}, + fan::{self, address, holding_registers, set_point::SetPoint, Fan, FanResponse, BAUD_RATE}, modbus::{self, ReadInputRegister}, }; diff --git a/fan-controller/src/task.rs b/fan-controller/src/task.rs index 5f63d9d..6b25d71 100644 --- a/fan-controller/src/task.rs +++ b/fan-controller/src/task.rs @@ -1,3 +1,4 @@ +use crate::fan::set_point::SetPoint; use crate::mqtt::packet::connect::Connect; use crate::mqtt::packet::disconnect::Disconnect; use crate::mqtt::packet::ping_response::PingResponse; @@ -122,7 +123,7 @@ async fn handle_publish<'f>( }; info!("SETTING FAN {}", set_point); - let Ok(setting) = fan::SetPoint::new(set_point) else { + let Ok(setting) = SetPoint::new(set_point) else { warn!( "Setting fan speed out of bounds. Not accepting new setting: {}", set_point @@ -150,7 +151,7 @@ async fn handle_publish<'f>( setting: sender .try_get() .map(|state| state.setting) - .unwrap_or(fan::SetPoint::ZERO), + .unwrap_or(SetPoint::ZERO), is_on, }); // Home assistant and fan update will be done by receiver @@ -264,7 +265,7 @@ async fn listen< ) { Ok(response) => response, // Matching to get compiler error if this changes - Err(Infallible) => { + Err(_) => { defmt::unreachable!("Ping response is always empty so decode should always succeed if the protocol did not change") } }; @@ -286,7 +287,7 @@ async fn listen< enum PredefinedPublish { FanPercentageState { - setting: fan::SetPoint, + setting: SetPoint, }, FanOnState { is_on: bool, @@ -535,7 +536,7 @@ async fn update_homeassistant( // which is good as we don't know the state on homeassistant let mut previous_state = receiver.try_get().unwrap_or(FanState { is_on: false, - setting: fan::SetPoint::ZERO, + setting: SetPoint::ZERO, }); loop {