Compare commits
No commits in common. "7f8aec789c5d9702d988a060a8d96ecc57545dec" and "f340278236dd4da0c203940ebaf0644b3552e015" have entirely different histories.
7f8aec789c
...
f340278236
119
rust/src/main.rs
119
rust/src/main.rs
@ -26,9 +26,6 @@ use crate::{config::PlantControllerConfig, webserver::webserver::httpd};
|
|||||||
mod config;
|
mod config;
|
||||||
mod log;
|
mod log;
|
||||||
pub mod plant_hal;
|
pub mod plant_hal;
|
||||||
mod tank;
|
|
||||||
|
|
||||||
use tank::*;
|
|
||||||
|
|
||||||
const TIME_ZONE: Tz = Berlin;
|
const TIME_ZONE: Tz = Berlin;
|
||||||
|
|
||||||
@ -128,6 +125,34 @@ enum SensorError {
|
|||||||
OpenCircuit { hz: f32, min: f32 },
|
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)]
|
#[derive(Serialize)]
|
||||||
struct PlantStateMQTT<'a> {
|
struct PlantStateMQTT<'a> {
|
||||||
a: &'a str,
|
a: &'a str,
|
||||||
@ -606,12 +631,74 @@ 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(
|
fn determine_state_target_moisture_for_plant(
|
||||||
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
||||||
plant: usize,
|
plant: usize,
|
||||||
state: &mut PlantState,
|
state: &mut PlantState,
|
||||||
config: &PlantControllerConfig,
|
config: &PlantControllerConfig,
|
||||||
tank_state: &TankInfo,
|
tank_state: &TankState,
|
||||||
cur: DateTime<Tz>,
|
cur: DateTime<Tz>,
|
||||||
) {
|
) {
|
||||||
let plant_config = &config.plants[plant];
|
let plant_config = &config.plants[plant];
|
||||||
@ -701,7 +788,7 @@ fn determine_state_timer_only_for_plant(
|
|||||||
plant: usize,
|
plant: usize,
|
||||||
state: &mut PlantState,
|
state: &mut PlantState,
|
||||||
config: &PlantControllerConfig,
|
config: &PlantControllerConfig,
|
||||||
tank_state: &TankInfo,
|
tank_state: &TankState,
|
||||||
cur: DateTime<Tz>,
|
cur: DateTime<Tz>,
|
||||||
) {
|
) {
|
||||||
let plant_config = &config.plants[plant];
|
let plant_config = &config.plants[plant];
|
||||||
@ -739,7 +826,7 @@ fn determine_state_timer_and_deadzone_for_plant(
|
|||||||
plant: usize,
|
plant: usize,
|
||||||
state: &mut PlantState,
|
state: &mut PlantState,
|
||||||
config: &PlantControllerConfig,
|
config: &PlantControllerConfig,
|
||||||
tank_state: &TankInfo,
|
tank_state: &TankState,
|
||||||
cur: DateTime<Tz>,
|
cur: DateTime<Tz>,
|
||||||
) {
|
) {
|
||||||
let plant_config = &config.plants[plant];
|
let plant_config = &config.plants[plant];
|
||||||
@ -783,7 +870,7 @@ fn determine_state_timer_and_deadzone_for_plant(
|
|||||||
fn determine_plant_state(
|
fn determine_plant_state(
|
||||||
plantstate: &mut [PlantState; PLANT_COUNT],
|
plantstate: &mut [PlantState; PLANT_COUNT],
|
||||||
cur: DateTime<Tz>,
|
cur: DateTime<Tz>,
|
||||||
tank_state: &TankInfo,
|
tank_state: &TankState,
|
||||||
config: &PlantControllerConfig,
|
config: &PlantControllerConfig,
|
||||||
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
||||||
) {
|
) {
|
||||||
@ -964,6 +1051,24 @@ 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> {
|
fn map_range_moisture(s: f32) -> Result<u8, SensorError> {
|
||||||
if s < FROM.0 {
|
if s < FROM.0 {
|
||||||
return Err(SensorError::OpenCircuit { hz: s, min: FROM.0 });
|
return Err(SensorError::OpenCircuit { hz: s, min: FROM.0 });
|
||||||
|
@ -471,23 +471,44 @@ impl PlantCtrlBoard<'_> {
|
|||||||
Ok(sensor_data.temperature / 10_f32)
|
Ok(sensor_data.temperature / 10_f32)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// return median tank sensor value in milli volt
|
pub fn tank_sensor_percent(&mut self) -> Result<u16> {
|
||||||
pub fn tank_sensor_voltage(&mut self) -> Result<u16> {
|
|
||||||
let delay = Delay::new_default();
|
let delay = Delay::new_default();
|
||||||
self.tank_power.set_high()?;
|
self.tank_power.set_high()?;
|
||||||
//let stabilize
|
//let stabilize
|
||||||
delay.delay_ms(100);
|
delay.delay_ms(100);
|
||||||
|
unsafe {
|
||||||
|
vTaskDelay(100);
|
||||||
|
}
|
||||||
|
|
||||||
let mut store = [0_u16; TANK_MULTI_SAMPLE];
|
let mut store = [0_u16; TANK_MULTI_SAMPLE];
|
||||||
for multisample in 0..TANK_MULTI_SAMPLE {
|
for multisample in 0..TANK_MULTI_SAMPLE {
|
||||||
let value = self.tank_channel.read()?;
|
let value = self.tank_channel.read()?;
|
||||||
store[multisample] = value;
|
store[multisample] = value;
|
||||||
}
|
}
|
||||||
self.tank_power.set_low()?;
|
|
||||||
|
|
||||||
store.sort();
|
store.sort();
|
||||||
let median_mv = store[6] as f32 / 1000_f32;
|
let median = store[6] as f32 / 1000_f32;
|
||||||
Ok(median_mv)
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_low_voltage_in_cycle(&mut self) {
|
pub fn set_low_voltage_in_cycle(&mut self) {
|
||||||
|
146
rust/src/tank.rs
146
rust/src/tank.rs
@ -1,146 +0,0 @@
|
|||||||
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<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<f32, TankError> {
|
|
||||||
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<bool, TankError> {
|
|
||||||
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<bool, TankError> {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user