use crate::config::{self, PlantControllerConfig, TankConfig}; const OPEN_TANK_VOLTAGE: f32 = 3.0; #[derive(Debug, PartialEq, Default)] /// State data for water tank /// /// TODO unify with TankStateMQTT pub struct TankInfo { /// is there enough water in the tank enough_water: bool, /// warning that water needs to be refilled soon warn_level: bool, /// estimation how many ml are still in tank left_ml: u32, /// if there is was an issue with the water level sensor /// TODO merge with left_ml as Result sensor_error: bool, /// raw water sensor value raw: u16, } pub enum TankError { SensorDisabled, SensorMissing(f32), SensorValueError { value: f32, min: f32, max: f32 }, } pub enum TankState { TankSensorPresent(u16), TankSensorDisabled, } fn raw_volatge_to_divider_percent(raw_value_mv: u16) -> Result { if raw_value_mv > OPEN_TANK_VOLTAGE { return Err(TankError::SensorMissing(raw_value_mv)); } let r2 = raw_value_mv * 50.0 / (3.3 - raw_value_mv); let mut percent = r2 / 190_f32 * 100_f32; percent = percent.clamp(0.0, 100.0); // TODO(judge) move this to a sensible place //log( //LogMessage::SensorTankRaw, //raw_value_mv as u32, //percent as u32, //"", //"", //); Ok(percent) } fn raw_voltage_to_tank_fill_percent( raw_value_mv: u16, config: &TankConfig, ) -> Result { let divider_percent = raw_volatge_to_divider_percent(raw_value_mv)?; if s < config.tank_empty_percent || s > config.tank_full_percent { return Err(TankError::SensorValueError { value: divider_percent, min: config.tank_empty_percent, max: config.tank_full_percent, }); } Ok((divider_percent - config.tank_empty_percent) * 100 / (config.tank_full_percent - config.tank_empty_percent)) } impl TankState { pub fn left_ml(&self, config: &TankConfig) -> Result { match self { TankState::TankSensorDisabled => Err(TankError::SensorDisabled), TankState::TankSensorPresent(raw_value_mv) => { let tank_fill_percent = raw_voltage_to_tank_fill_percent(raw_value_mv, config)?; //TODO(judge) move logging to more sensible place //println!( //"Tank sensor returned mv {} as {}% leaving {} ml useable", //rv.raw, percent as u8, rv.left_ml //); Ok(config.tank_useable_ml as f32 * tank_fill_percent / 100.) } } } pub fn enough_water(&self, config: &TankConfig) -> Result { match self { TankState::TankSensorDisabled => Err(TankError::SensorDisabled), TankState::TankSensorPresent(raw_value_mv) => { let tank_fill_percent = raw_voltage_to_tank_fill_percent(raw_value_mv, config)?; if tank_fill_percent > config.tank_empty_percent { //TODO(judge) move logging to more sensible place //println!( //"Enough water, current percent is {}, minimum empty level is {}", //percent as u8, config.tank.tank_empty_percent //); Ok(true) } else { Ok(false) } }, } } pub fn warn_level(&self, config: &TankConfig) -> Result { match self { TankState::TankSensorDisabled => Err(TankError::SensorDisabled), TankState::TankSensorPresent(raw_value_mv) => { let tank_fill_percent = raw_voltage_to_tank_fill_percent(raw_value_mv, config)?; if tank_fill_percent < config.tank_warn_percent { //TODO(judge) move logging to more sensible place //println!( //"Low water, current percent is {}, minimum warn level is {}", //percent as u8, config.tank.tank_warn_percent //); // TODO(judge) move board fault setting // board.general_fault(true); Ok(true) } else { Ok(false) } }, } } } #[derive(Serialize)] pub struct TankStateMQTT { enough_water: bool, warn_level: bool, left_ml: u32, sensor_error: bool, raw: u16, water_frozen: String, } pub fn determine_tank_state( board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>, config: &PlantControllerConfig, ) -> TankState { if config.tank.tank_sensor_enabled { let raw_sensor_value_mv = board.tank_sensor_voltage(); TankState::TankSensorPresent(raw_sensor_value_mv) } else { TankState::TankSensorDisabled } }