WIP refactor tank state code

This commit is contained in:
ju6ge 2025-03-13 22:41:00 +01:00
parent 420f4f4ae5
commit fb180630e4
Signed by: judge
GPG Key ID: 6512C30DD8E017B5
3 changed files with 165 additions and 139 deletions

View File

@ -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<u32, error_type>
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<Tz>,
) {
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<Tz>,
) {
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<Tz>,
) {
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<Tz>,
tank_state: &TankState,
tank_state: &TankInfo,
config: &PlantControllerConfig,
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
) {
@ -1051,24 +964,6 @@ fn to_string<T: Display>(value: Result<T>) -> String {
};
}
fn map_range(from_range: (f32, f32), s: f32) -> anyhow::Result<f32> {
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<u8, SensorError> {
if s < FROM.0 {
return Err(SensorError::OpenCircuit { hz: s, min: FROM.0 });

View File

@ -471,44 +471,23 @@ impl PlantCtrlBoard<'_> {
Ok(sensor_data.temperature / 10_f32)
}
pub fn tank_sensor_percent(&mut self) -> Result<u16> {
/// return median tank sensor value in milli volt
pub fn tank_sensor_voltage(&mut self) -> Result<u16> {
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) {

152
rust/src/tank.rs Normal file
View File

@ -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<u32, error_type>
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<f32, TankError> {
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<f32, TankError> {
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<u32, TankError> {
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,
};
}