Compare commits
2 Commits
develop
...
refactor/p
Author | SHA1 | Date | |
---|---|---|---|
79113530b8 | |||
76835b23b1 |
@ -89,6 +89,7 @@ pub struct PlantConfig {
|
||||
pub pump_cooldown_min: u16,
|
||||
pub pump_hour_start: u8,
|
||||
pub pump_hour_end: u8,
|
||||
pub sensor_a: bool,
|
||||
pub sensor_b: bool,
|
||||
pub max_consecutive_pump_count: u8,
|
||||
}
|
||||
@ -101,6 +102,7 @@ impl Default for PlantConfig {
|
||||
pump_cooldown_min: 60,
|
||||
pump_hour_start: 9,
|
||||
pump_hour_end: 20,
|
||||
sensor_a: true,
|
||||
sensor_b: false,
|
||||
max_consecutive_pump_count: 10,
|
||||
}
|
||||
|
@ -26,21 +26,14 @@ use crate::{config::PlantControllerConfig, webserver::webserver::httpd};
|
||||
mod config;
|
||||
mod log;
|
||||
pub mod plant_hal;
|
||||
mod plant_state;
|
||||
mod tank;
|
||||
|
||||
use plant_state::{PlantInfo, PlantStateMQTT};
|
||||
use tank::*;
|
||||
|
||||
const TIME_ZONE: Tz = Berlin;
|
||||
|
||||
const MOIST_SENSOR_MAX_FREQUENCY: u32 = 5500; // 60kHz (500Hz margin)
|
||||
const MOIST_SENSOR_MIN_FREQUENCY: u32 = 150; // this is really really dry, think like cactus levels
|
||||
|
||||
const FROM: (f32, f32) = (
|
||||
MOIST_SENSOR_MIN_FREQUENCY as f32,
|
||||
MOIST_SENSOR_MAX_FREQUENCY as f32,
|
||||
);
|
||||
const TO: (f32, f32) = (0_f32, 100_f32);
|
||||
|
||||
pub static BOARD_ACCESS: Lazy<Mutex<PlantCtrlBoard>> = Lazy::new(|| PlantHal::create().unwrap());
|
||||
pub static STAY_ALIVE: Lazy<AtomicBool> = Lazy::new(|| AtomicBool::new(false));
|
||||
|
||||
@ -80,46 +73,6 @@ struct LightState {
|
||||
is_day: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Default)]
|
||||
/// State of a single plant to be tracked
|
||||
///
|
||||
/// TODO can some state be replaced with functions
|
||||
/// TODO unify with PlantStateMQTT
|
||||
struct PlantState {
|
||||
/// state of humidity sensor on bank a
|
||||
a: Option<u8>,
|
||||
/// raw measured frequency value for sensor on bank a in hertz
|
||||
a_raw: Option<u32>,
|
||||
/// state of humidity sensor on bank b
|
||||
b: Option<u8>,
|
||||
/// raw measured frequency value for sensor on bank b in hertz
|
||||
b_raw: Option<u32>,
|
||||
/// how often has the logic determined that plant should have been irrigated but wasn't
|
||||
consecutive_pump_count: u32,
|
||||
/// plant needs to be watered
|
||||
do_water: bool,
|
||||
/// is plant considerd to be dry according to settings
|
||||
dry: bool,
|
||||
/// is pump currently running
|
||||
active: bool,
|
||||
/// TODO: convert this to an Option<PumpErorr> enum for every case that can happen
|
||||
pump_error: bool,
|
||||
/// if pump count has increased higher than configured limit
|
||||
not_effective: bool,
|
||||
/// plant irrigation cooldown is active
|
||||
cooldown: bool,
|
||||
/// we want to irrigate but tank is empty
|
||||
no_water: bool,
|
||||
///TODO: combine with field a using Result
|
||||
sensor_error_a: Option<SensorError>,
|
||||
///TODO: combine with field b using Result
|
||||
sensor_error_b: Option<SensorError>,
|
||||
/// pump should not be watered at this time of day
|
||||
out_of_work_hour: bool,
|
||||
/// next time when pump should activate
|
||||
next_pump: Option<DateTime<Tz>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
/// humidity sensor error
|
||||
enum SensorError {
|
||||
@ -128,24 +81,6 @@ enum SensorError {
|
||||
OpenCircuit { hz: f32, min: f32 },
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct PlantStateMQTT<'a> {
|
||||
a: &'a str,
|
||||
a_raw: &'a str,
|
||||
b: &'a str,
|
||||
b_raw: &'a str,
|
||||
mode: &'a str,
|
||||
consecutive_pump_count: u32,
|
||||
dry: bool,
|
||||
active: bool,
|
||||
pump_error: bool,
|
||||
not_effective: bool,
|
||||
cooldown: bool,
|
||||
out_of_work_hour: bool,
|
||||
last_pump: &'a str,
|
||||
next_pump: &'a str,
|
||||
}
|
||||
|
||||
fn safe_main() -> anyhow::Result<()> {
|
||||
// It is necessary to call this function once. Otherwise some patches to the runtime
|
||||
// implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
|
||||
@ -471,7 +406,7 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
}
|
||||
};
|
||||
|
||||
let mut plantstate: [PlantState; PLANT_COUNT] = core::array::from_fn(|_| PlantState {
|
||||
let mut plantstate: [PlantInfo; PLANT_COUNT] = core::array::from_fn(|_| PlantInfo {
|
||||
..Default::default()
|
||||
});
|
||||
determine_plant_state(
|
||||
@ -636,7 +571,7 @@ fn publish_battery_state(
|
||||
fn determine_state_target_moisture_for_plant(
|
||||
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
||||
plant: usize,
|
||||
state: &mut PlantState,
|
||||
state: &mut PlantInfo,
|
||||
config: &PlantControllerConfig,
|
||||
tank_state: &TankState,
|
||||
cur: DateTime<Tz>,
|
||||
@ -731,7 +666,7 @@ fn determine_state_target_moisture_for_plant(
|
||||
fn determine_state_timer_only_for_plant(
|
||||
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
||||
plant: usize,
|
||||
state: &mut PlantState,
|
||||
state: &mut PlantInfo,
|
||||
config: &PlantControllerConfig,
|
||||
tank_state: &TankState,
|
||||
cur: DateTime<Tz>,
|
||||
@ -774,7 +709,7 @@ fn determine_state_timer_only_for_plant(
|
||||
fn determine_state_timer_and_deadzone_for_plant(
|
||||
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
||||
plant: usize,
|
||||
state: &mut PlantState,
|
||||
state: &mut PlantInfo,
|
||||
config: &PlantControllerConfig,
|
||||
tank_state: &TankState,
|
||||
cur: DateTime<Tz>,
|
||||
@ -823,7 +758,7 @@ fn determine_state_timer_and_deadzone_for_plant(
|
||||
}
|
||||
|
||||
fn determine_plant_state(
|
||||
plantstate: &mut [PlantState; PLANT_COUNT],
|
||||
plantstate: &mut [PlantInfo; PLANT_COUNT],
|
||||
cur: DateTime<Tz>,
|
||||
tank_state: &TankState,
|
||||
config: &PlantControllerConfig,
|
||||
@ -861,7 +796,7 @@ fn determine_plant_state(
|
||||
}
|
||||
|
||||
fn update_plant_state(
|
||||
plantstate: &mut [PlantState; PLANT_COUNT],
|
||||
plantstate: &mut [PlantInfo; PLANT_COUNT],
|
||||
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
||||
config: &PlantControllerConfig,
|
||||
) {
|
||||
@ -1006,18 +941,6 @@ fn to_string<T: Display>(value: Result<T>) -> String {
|
||||
};
|
||||
}
|
||||
|
||||
fn map_range_moisture(s: f32) -> Result<u8, SensorError> {
|
||||
if s < FROM.0 {
|
||||
return Err(SensorError::OpenCircuit { hz: s, min: FROM.0 });
|
||||
}
|
||||
if s > FROM.1 {
|
||||
return Err(SensorError::ShortCircuit { hz: s, max: FROM.1 });
|
||||
}
|
||||
let tmp = TO.0 + (s - FROM.0) * (TO.1 - TO.0) / (FROM.1 - FROM.0);
|
||||
|
||||
return Ok(tmp as u8);
|
||||
}
|
||||
|
||||
fn in_time_range(cur: &DateTime<Tz>, start: u8, end: u8) -> bool {
|
||||
let curhour = cur.hour() as u8;
|
||||
//eg 10-14
|
||||
|
130
rust/src/plant_state.rs
Normal file
130
rust/src/plant_state.rs
Normal file
@ -0,0 +1,130 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use chrono_tz::Tz;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{config, plant_hal};
|
||||
|
||||
const MOIST_SENSOR_MAX_FREQUENCY: u32 = 5500; // 60kHz (500Hz margin)
|
||||
const MOIST_SENSOR_MIN_FREQUENCY: u32 = 150; // this is really really dry, think like cactus levels
|
||||
|
||||
pub enum HumiditySensorError{
|
||||
ShortCircuit{hz: f32, max: f32},
|
||||
OpenLoop{hz: f32, min: f32}
|
||||
}
|
||||
|
||||
pub enum HumiditySensorState {
|
||||
Disabled,
|
||||
HumidityValue{raw_hz: u32, moisture_percent: f32},
|
||||
SensorError(HumiditySensorError),
|
||||
BoardError(String)
|
||||
}
|
||||
|
||||
impl HumiditySensorState {
|
||||
}
|
||||
|
||||
pub enum PumpError {}
|
||||
|
||||
pub struct PumpState {
|
||||
consecutive_pump_count: u32,
|
||||
previous_pump: Option<DateTime<Utc>>
|
||||
}
|
||||
|
||||
pub enum PlantError{}
|
||||
|
||||
pub struct PlantState {
|
||||
sensor_a: HumiditySensorState,
|
||||
sensor_b: HumiditySensorState,
|
||||
pump: PumpState,
|
||||
}
|
||||
|
||||
fn map_range_moisture(s: f32) -> Result<f32, HumiditySensorError> {
|
||||
if s < MOIST_SENSOR_MIN_FREQUENCY {
|
||||
return Err(HumiditySensorError::OpenCircuit { hz: s, min: FROM.0 });
|
||||
}
|
||||
if s > MOIST_SENSOR_MAX_FREQUENCY {
|
||||
return Err(HumiditySensorError::ShortCircuit { hz: s, max: FROM.1 });
|
||||
}
|
||||
let moisture_percent = (s - MOIST_SENSOR_MIN_FREQUENCY) * 100 / (MOIST_SENSOR_MAX_FREQUENCY - MOIST_SENSOR_MIN_FREQUENCY);
|
||||
|
||||
return Ok(moisture_percent);
|
||||
}
|
||||
|
||||
|
||||
impl PlantState {
|
||||
pub fn read_hardware_state(
|
||||
plant_id: usize,
|
||||
board: &mut plant_hal::PlantCtrlBoard,
|
||||
config: &config::PlantConfig
|
||||
) -> Self {
|
||||
let sensor_a = if config.sensor_a {
|
||||
match board.measure_moisture_hz(plant_id, plant_hal::Sensor::A) {
|
||||
Ok(raw) => {
|
||||
match map_range_moisture(raw) {
|
||||
Ok(moisture_percent) => HumiditySensorState::HumidityValue { raw_hz: raw, moisture_percent },
|
||||
Err(err) => HumiditySensorState::SensorError(err),
|
||||
}
|
||||
},
|
||||
Err(err) => HumiditySensorState::BoardError(err.to_string()),
|
||||
}
|
||||
} else {
|
||||
HumiditySensorState::Disabled
|
||||
};
|
||||
let sensor_b = if config.sensor_b {
|
||||
match board.measure_moisture_hz(plant_id, plant_hal::Sensor::B) {
|
||||
Ok(raw) => {
|
||||
match map_range_moisture(raw) {
|
||||
Ok(moisture_percent) => HumiditySensorState::HumidityValue { raw_hz: raw, moisture_percent },
|
||||
Err(err) => HumiditySensorState::SensorError(err),
|
||||
}
|
||||
},
|
||||
Err(err) => HumiditySensorState::BoardError(err.to_string()),
|
||||
}
|
||||
} else {
|
||||
HumiditySensorState::Disabled
|
||||
};
|
||||
let previous_pump = board.last_pump_time(plant_id);
|
||||
let consecutive_pump_count = board.consecutive_pump_count(plant_id);
|
||||
Self {
|
||||
sensor_a,
|
||||
sensor_b,
|
||||
pump: PumpState { consecutive_pump_count , previous_pump}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Default, Serialize)]
|
||||
/// State of a single plant to be tracked
|
||||
pub struct PlantInfo {
|
||||
/// state of humidity sensor on bank a
|
||||
a: HumiditySensorState,
|
||||
/// raw measured frequency value for sensor on bank a in hertz
|
||||
a_raw: Option<u32>,
|
||||
/// state of humidity sensor on bank b
|
||||
b: HumiditySensorState,
|
||||
/// raw measured frequency value for sensor on bank b in hertz
|
||||
b_raw: Option<u32>,
|
||||
/// configured plant watering mode
|
||||
mode: config::Mode,
|
||||
/// how often has the logic determined that plant should have been irrigated but wasn't
|
||||
consecutive_pump_count: u32,
|
||||
/// plant needs to be watered
|
||||
do_water: bool,
|
||||
/// is plant considerd to be dry according to settings
|
||||
dry: bool,
|
||||
/// is pump currently running
|
||||
active: bool,
|
||||
/// TODO: convert this to an Option<PumpErorr> enum for every case that can happen
|
||||
pump_error: bool,
|
||||
/// if pump count has increased higher than configured limit
|
||||
not_effective: bool,
|
||||
/// plant irrigation cooldown is active
|
||||
cooldown: bool,
|
||||
/// we want to irrigate but tank is empty
|
||||
no_water: bool,
|
||||
/// pump should not be watered at this time of day
|
||||
out_of_work_hour: bool,
|
||||
/// last time when pump was active
|
||||
last_pump: Option<DateTime<Tz>>,
|
||||
/// next time when pump should activate
|
||||
next_pump: Option<DateTime<Tz>>,
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user