From fb180630e45e5475dd820cc1e1ecd4956a15054d Mon Sep 17 00:00:00 2001 From: ju6ge Date: Thu, 13 Mar 2025 22:41:00 +0100 Subject: [PATCH] WIP refactor tank state code --- rust/src/main.rs | 119 ++------------------------------- rust/src/plant_hal.rs | 33 ++------- rust/src/tank.rs | 152 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 139 deletions(-) create mode 100644 rust/src/tank.rs diff --git a/rust/src/main.rs b/rust/src/main.rs index 8387cb7..7293148 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -26,6 +26,9 @@ use crate::{config::PlantControllerConfig, webserver::webserver::httpd}; mod config; mod log; pub mod plant_hal; +mod tank; + +use tank::*; const TIME_ZONE: Tz = Berlin; @@ -125,34 +128,6 @@ enum SensorError { OpenCircuit { hz: f32, min: f32 }, } -#[derive(Debug, PartialEq, Default)] -/// State data for water tank -/// -/// TODO unify with TankStateMQTT -struct TankState { - /// 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, -} - -#[derive(Serialize)] -struct TankStateMQTT { - enough_water: bool, - warn_level: bool, - left_ml: u32, - sensor_error: bool, - raw: u16, - water_frozen: String, -} - #[derive(Serialize)] struct PlantStateMQTT<'a> { a: &'a str, @@ -631,74 +606,12 @@ fn publish_battery_state( }; } -fn determine_tank_state( - board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>, - config: &PlantControllerConfig, -) -> TankState { - if config.tank.tank_sensor_enabled { - let mut rv: TankState = TankState { - ..Default::default() - }; - let success = board - .tank_sensor_percent() - .and_then(|raw| { - rv.raw = raw; - return map_range( - ( - config.tank.tank_empty_percent as f32, - config.tank.tank_full_percent as f32, - ), - raw as f32, - ); - }) - .and_then(|percent| { - rv.left_ml = ((percent * config.tank.tank_useable_ml as f32) / 100_f32) as u32; - println!( - "Tank sensor returned mv {} as {}% leaving {} ml useable", - rv.raw, percent as u8, rv.left_ml - ); - if config.tank.tank_warn_percent > percent as u8 { - board.general_fault(true); - println!( - "Low water, current percent is {}, minimum warn level is {}", - percent as u8, config.tank.tank_warn_percent - ); - rv.warn_level = true; - } - if config.tank.tank_empty_percent < percent as u8 { - println!( - "Enough water, current percent is {}, minimum empty level is {}", - percent as u8, config.tank.tank_empty_percent - ); - rv.enough_water = true; - } - return Ok(()); - }); - match success { - Err(err) => { - println!("Could not determine tank value due to {}", err); - board.general_fault(true); - rv.sensor_error = true; - } - Ok(_) => {} - } - return rv; - } - return TankState { - warn_level: false, - enough_water: true, - left_ml: 1337, - sensor_error: false, - raw: 0, - }; -} - fn determine_state_target_moisture_for_plant( board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>, plant: usize, state: &mut PlantState, config: &PlantControllerConfig, - tank_state: &TankState, + tank_state: &TankInfo, cur: DateTime, ) { let plant_config = &config.plants[plant]; @@ -788,7 +701,7 @@ fn determine_state_timer_only_for_plant( plant: usize, state: &mut PlantState, config: &PlantControllerConfig, - tank_state: &TankState, + tank_state: &TankInfo, cur: DateTime, ) { let plant_config = &config.plants[plant]; @@ -826,7 +739,7 @@ fn determine_state_timer_and_deadzone_for_plant( plant: usize, state: &mut PlantState, config: &PlantControllerConfig, - tank_state: &TankState, + tank_state: &TankInfo, cur: DateTime, ) { let plant_config = &config.plants[plant]; @@ -870,7 +783,7 @@ fn determine_state_timer_and_deadzone_for_plant( fn determine_plant_state( plantstate: &mut [PlantState; PLANT_COUNT], cur: DateTime, - tank_state: &TankState, + tank_state: &TankInfo, config: &PlantControllerConfig, board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>, ) { @@ -1051,24 +964,6 @@ fn to_string(value: Result) -> String { }; } -fn map_range(from_range: (f32, f32), s: f32) -> anyhow::Result { - if s < from_range.0 { - anyhow::bail!( - "Value out of range, min {} but current is {}", - from_range.0, - s - ); - } - if s > from_range.1 { - anyhow::bail!( - "Value out of range, max {} but current is {}", - from_range.1, - s - ); - } - return Ok(TO.0 + (s - from_range.0) * (TO.1 - TO.0) / (from_range.1 - from_range.0)); -} - fn map_range_moisture(s: f32) -> Result { if s < FROM.0 { return Err(SensorError::OpenCircuit { hz: s, min: FROM.0 }); diff --git a/rust/src/plant_hal.rs b/rust/src/plant_hal.rs index c6dc2b5..a65c91d 100644 --- a/rust/src/plant_hal.rs +++ b/rust/src/plant_hal.rs @@ -471,44 +471,23 @@ impl PlantCtrlBoard<'_> { Ok(sensor_data.temperature / 10_f32) } - pub fn tank_sensor_percent(&mut self) -> Result { + /// return median tank sensor value in milli volt + pub fn tank_sensor_voltage(&mut self) -> Result { let delay = Delay::new_default(); self.tank_power.set_high()?; //let stabilize delay.delay_ms(100); - unsafe { - vTaskDelay(100); - } let mut store = [0_u16; TANK_MULTI_SAMPLE]; for multisample in 0..TANK_MULTI_SAMPLE { let value = self.tank_channel.read()?; store[multisample] = value; } + self.tank_power.set_low()?; + store.sort(); - let median = store[6] as f32 / 1000_f32; - let config_open_voltage_mv = 3.0; - if config_open_voltage_mv < median { - self.tank_power.set_low()?; - bail!( - "Tank sensor missing, open loop voltage {} on tank sensor input {}", - config_open_voltage_mv, - median - ); - } - - let r2 = median * 50.0 / (3.3 - median); - let mut percent = r2 / 190_f32 * 100_f32; - percent = percent.clamp(0.0, 100.0); - log( - LogMessage::SensorTankRaw, - median as u32, - percent as u32, - "", - "", - ); - - return Ok(percent as u16); + let median_mv = store[6] as f32 / 1000_f32; + Ok(median_mv) } pub fn set_low_voltage_in_cycle(&mut self) { diff --git a/rust/src/tank.rs b/rust/src/tank.rs new file mode 100644 index 0000000..05f0374 --- /dev/null +++ b/rust/src/tank.rs @@ -0,0 +1,152 @@ +use crate::config::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::SensorMissing), + TankState::TankSensorPresent(raw_value_mv) => { + let tank_fill_percent = raw_voltage_to_tank_fill_percent(raw_value_mv, config); + todo!() + } + } + } +} + +#[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, +) -> TankInfo { + if config.tank.tank_sensor_enabled { + let mut rv: TankInfo = TankInfo { + ..Default::default() + }; + let success = board + .tank_sensor_percent() + .and_then(|raw| { + rv.raw = raw; + return map_range( + ( + config.tank.tank_empty_percent as f32, + config.tank.tank_full_percent as f32, + ), + raw as f32, + ); + }) + .and_then(|percent| { + rv.left_ml = ((percent * config.tank.tank_useable_ml as f32) / 100_f32) as u32; + println!( + "Tank sensor returned mv {} as {}% leaving {} ml useable", + rv.raw, percent as u8, rv.left_ml + ); + if config.tank.tank_warn_percent > percent as u8 { + board.general_fault(true); + println!( + "Low water, current percent is {}, minimum warn level is {}", + percent as u8, config.tank.tank_warn_percent + ); + rv.warn_level = true; + } + if config.tank.tank_empty_percent < percent as u8 { + println!( + "Enough water, current percent is {}, minimum empty level is {}", + percent as u8, config.tank.tank_empty_percent + ); + rv.enough_water = true; + } + return Ok(()); + }); + match success { + Err(err) => { + println!("Could not determine tank value due to {}", err); + board.general_fault(true); + rv.sensor_error = true; + } + Ok(_) => {} + } + return rv; + } + return TankInfo { + warn_level: false, + enough_water: true, + left_ml: 1337, + sensor_error: false, + raw: 0, + }; +}