main pump handler
This commit is contained in:
parent
b9ec3247af
commit
b5b5b25238
@ -14,8 +14,8 @@ pub struct Config {
|
|||||||
pub tank_sensor_enabled: bool,
|
pub tank_sensor_enabled: bool,
|
||||||
pub tank_useable_ml: u32,
|
pub tank_useable_ml: u32,
|
||||||
pub tank_warn_percent: u8,
|
pub tank_warn_percent: u8,
|
||||||
pub tank_empty_mv: f32,
|
pub tank_empty_mv: u16,
|
||||||
pub tank_full_mv: f32,
|
pub tank_full_mv: u16,
|
||||||
|
|
||||||
pub night_lamp_hour_start: u8,
|
pub night_lamp_hour_start: u8,
|
||||||
pub night_lamp_hour_end: u8,
|
pub night_lamp_hour_end: u8,
|
||||||
@ -38,8 +38,8 @@ impl Default for Config {
|
|||||||
plants: [Plant::default(); PLANT_COUNT],
|
plants: [Plant::default(); PLANT_COUNT],
|
||||||
max_consecutive_pump_count: 15,
|
max_consecutive_pump_count: 15,
|
||||||
tank_useable_ml: 5000,
|
tank_useable_ml: 5000,
|
||||||
tank_empty_mv: 0.1,
|
tank_empty_mv: 0100_u16,
|
||||||
tank_full_mv: 3.3,
|
tank_full_mv: 3300_u16,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -48,6 +48,7 @@ pub enum Mode {
|
|||||||
OFF,
|
OFF,
|
||||||
TargetMoisture,
|
TargetMoisture,
|
||||||
TimerOnly,
|
TimerOnly,
|
||||||
|
TimerAndDeadzone,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq)]
|
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq)]
|
||||||
|
345
rust/src/main.rs
345
rust/src/main.rs
@ -3,11 +3,11 @@ use std::{
|
|||||||
sync::{atomic::AtomicBool, Arc, Mutex},
|
sync::{atomic::AtomicBool, Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::{Result, bail};
|
||||||
use chrono::{Datelike, Duration, NaiveDateTime, Timelike};
|
use chrono::{Datelike, Duration, NaiveDateTime, Timelike, DateTime};
|
||||||
use chrono_tz::Europe::Berlin;
|
use chrono_tz::{Europe::Berlin, Tz};
|
||||||
use esp_idf_hal::delay::Delay;
|
use esp_idf_hal::delay::Delay;
|
||||||
use esp_idf_sys::{esp_restart, vTaskDelay};
|
use esp_idf_sys::{esp_restart, vTaskDelay, CONFIG_FREERTOS_HZ, esp_deep_sleep};
|
||||||
use esp_ota::rollback_and_reboot;
|
use esp_ota::rollback_and_reboot;
|
||||||
use log::error;
|
use log::error;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
@ -51,16 +51,21 @@ enum WaitType {
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Default)]
|
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Default)]
|
||||||
struct PlantState {
|
struct PlantState {
|
||||||
a: u8,
|
a: Option<u8>,
|
||||||
b: u8,
|
b: Option<u8>,
|
||||||
p: u8,
|
p: Option<u8>,
|
||||||
after_p: u8,
|
after_p: Option<u8>,
|
||||||
|
do_water: bool,
|
||||||
dry: bool,
|
dry: bool,
|
||||||
active: bool,
|
active: bool,
|
||||||
pump_error: bool,
|
pump_error: bool,
|
||||||
not_effective: bool,
|
not_effective: bool,
|
||||||
cooldown: bool,
|
cooldown: bool,
|
||||||
no_water: bool,
|
no_water: bool,
|
||||||
|
sensor_error_a: bool,
|
||||||
|
sensor_error_b: bool,
|
||||||
|
sensor_error_p: bool,
|
||||||
|
out_of_work_hour: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
|
fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
|
||||||
@ -91,7 +96,9 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
|
|||||||
vTaskDelay(delay);
|
vTaskDelay(delay);
|
||||||
if wait_type == WaitType::StayAlive
|
if wait_type == WaitType::StayAlive
|
||||||
&& !STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed)
|
&& !STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed)
|
||||||
{}
|
{
|
||||||
|
reboot_now.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
}
|
||||||
if reboot_now.load(std::sync::atomic::Ordering::Relaxed) {
|
if reboot_now.load(std::sync::atomic::Ordering::Relaxed) {
|
||||||
println!("Rebooting");
|
println!("Rebooting");
|
||||||
esp_restart();
|
esp_restart();
|
||||||
@ -103,8 +110,131 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
|
|||||||
pub static BOARD_ACCESS: Lazy<Mutex<PlantCtrlBoard>> = Lazy::new(|| PlantHal::create().unwrap());
|
pub static BOARD_ACCESS: Lazy<Mutex<PlantCtrlBoard>> = Lazy::new(|| PlantHal::create().unwrap());
|
||||||
pub static STAY_ALIVE: Lazy<AtomicBool> = Lazy::new(|| AtomicBool::new(false));
|
pub static STAY_ALIVE: Lazy<AtomicBool> = Lazy::new(|| AtomicBool::new(false));
|
||||||
|
|
||||||
fn map_range(from_range: (f32, f32), to_range: (f32, f32), s: f32) -> f32 {
|
fn map_range(from_range: (f32, f32), s: f32) -> Result<f32> {
|
||||||
to_range.0 + (s - from_range.0) * (to_range.1 - to_range.0) / (from_range.1 - from_range.0)
|
if s < from_range.0 {
|
||||||
|
bail!("Value out of range, min {} but current is {}", from_range.0, s);
|
||||||
|
}
|
||||||
|
if s > from_range.1 {
|
||||||
|
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> {
|
||||||
|
if s < FROM.0 {
|
||||||
|
bail!("Value out of range, min {} but current is {}", FROM.0, s);
|
||||||
|
}
|
||||||
|
if s > FROM.1 {
|
||||||
|
bail!("Value out of range, max {} but current is {}", FROM.1, s);
|
||||||
|
}
|
||||||
|
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
|
||||||
|
if(start < end){
|
||||||
|
return curhour > start && curhour < end;
|
||||||
|
} else {
|
||||||
|
//eg 20-05
|
||||||
|
return curhour > start || curhour < end;;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn determine_next_plant(plantstate: &mut [PlantState;PLANT_COUNT],cur: DateTime<Tz>, enough_water: bool, tank_sensor_error: bool, config: &Config, board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>) -> Option<usize> {
|
||||||
|
for plant in 0..PLANT_COUNT {
|
||||||
|
let state = &mut plantstate[plant];
|
||||||
|
let plant_config = config.plants[plant];
|
||||||
|
match plant_config.mode {
|
||||||
|
config::Mode::OFF => {
|
||||||
|
|
||||||
|
},
|
||||||
|
config::Mode::TargetMoisture => {
|
||||||
|
match board.measure_moisture_hz(plant, plant_hal::Sensor::A).and_then (|moist| map_range_moisture(moist as f32)) {
|
||||||
|
Ok(a) => state.a = Some(a),
|
||||||
|
Err(err) => {
|
||||||
|
board.fault(plant, true);
|
||||||
|
println!("Could not determine Moisture A for plant {} due to {}", plant, err);
|
||||||
|
state.a = None;
|
||||||
|
state.sensor_error_a = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match board.measure_moisture_hz(plant, plant_hal::Sensor::B).and_then (|moist| map_range_moisture(moist as f32)) {
|
||||||
|
Ok(b) => state.b = Some(b),
|
||||||
|
Err(err) => {
|
||||||
|
board.fault(plant, true);
|
||||||
|
println!("Could not determine Moisture B for plant {} due to {}", plant, err);
|
||||||
|
state.b = None;
|
||||||
|
state.sensor_error_b = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//FIXME how to average analyze whatever?
|
||||||
|
let a_low = state.a.is_some() && state.a.unwrap() < plant_config.target_moisture;
|
||||||
|
let b_low = state.b.is_some() && state.b.unwrap() < plant_config.target_moisture;
|
||||||
|
|
||||||
|
if a_low || b_low {
|
||||||
|
state.dry = true;
|
||||||
|
if tank_sensor_error && !config.tank_allow_pumping_if_sensor_error || !enough_water {
|
||||||
|
state.no_water = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let duration = Duration::minutes((60 * plant_config.pump_cooldown_min).into());
|
||||||
|
let next_pump = board.last_pump_time(plant) + duration;
|
||||||
|
if next_pump > cur {
|
||||||
|
state.cooldown = true;
|
||||||
|
}
|
||||||
|
if !in_time_range(cur, plant_config.pump_hour_start, plant_config.pump_hour_end) {
|
||||||
|
state.out_of_work_hour = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(state.dry && !state.no_water && !state.cooldown && !state.out_of_work_hour){
|
||||||
|
state.do_water = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
config::Mode::TimerOnly => {
|
||||||
|
let duration = Duration::minutes((60 * plant_config.pump_cooldown_min).into());
|
||||||
|
let next_pump = board.last_pump_time(plant) + duration;
|
||||||
|
if next_pump > cur {
|
||||||
|
state.cooldown = true;
|
||||||
|
} else {
|
||||||
|
state.do_water = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
config::Mode::TimerAndDeadzone => {
|
||||||
|
let duration = Duration::minutes((60 * plant_config.pump_cooldown_min).into());
|
||||||
|
let next_pump = board.last_pump_time(plant) + duration;
|
||||||
|
if next_pump > cur {
|
||||||
|
state.cooldown = true;
|
||||||
|
}
|
||||||
|
if !in_time_range(cur, plant_config.pump_hour_start, plant_config.pump_hour_end) {
|
||||||
|
state.out_of_work_hour = true;
|
||||||
|
}
|
||||||
|
if(!state.cooldown && !state.out_of_work_hour){
|
||||||
|
state.do_water = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
//FIXME publish state here!
|
||||||
|
if state.do_water{
|
||||||
|
if board.consecutive_pump_count(plant) > config.max_consecutive_pump_count.into() {
|
||||||
|
state.not_effective = true;
|
||||||
|
board.fault(plant, true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
board.store_consecutive_pump_count(plant, 0);
|
||||||
|
}
|
||||||
|
println!("Plant {} state is {:?}", plant, state);
|
||||||
|
}
|
||||||
|
for plant in 0..PLANT_COUNT {
|
||||||
|
let state = &plantstate[plant];
|
||||||
|
println!("Checking for water plant {} with state {}", plant, state.do_water);
|
||||||
|
if state.do_water {
|
||||||
|
return Some(plant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("No plant needs water");
|
||||||
|
return None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
@ -129,8 +259,8 @@ fn main() -> Result<()> {
|
|||||||
println!("Version useing git has {}", git_hash);
|
println!("Version useing git has {}", git_hash);
|
||||||
|
|
||||||
let mut partition_state: embedded_svc::ota::SlotState = embedded_svc::ota::SlotState::Unknown;
|
let mut partition_state: embedded_svc::ota::SlotState = embedded_svc::ota::SlotState::Unknown;
|
||||||
// match esp_idf_svc::ota::EspOta::new() {
|
match esp_idf_svc::ota::EspOta::new() {
|
||||||
// Ok(ota) => {
|
Ok(ota) => {
|
||||||
// match ota.get_running_slot(){
|
// match ota.get_running_slot(){
|
||||||
// Ok(slot) => {
|
// Ok(slot) => {
|
||||||
// partition_state = slot.state;
|
// partition_state = slot.state;
|
||||||
@ -143,14 +273,15 @@ fn main() -> Result<()> {
|
|||||||
// println!("Error getting running slot {}", err);
|
// println!("Error getting running slot {}", err);
|
||||||
// },
|
// },
|
||||||
// }
|
// }
|
||||||
// },
|
},
|
||||||
// Err(err) => {
|
Err(err) => {
|
||||||
// println!("Error obtaining ota info {}", err);
|
println!("Error obtaining ota info {}", err);
|
||||||
// },
|
},
|
||||||
// }
|
}
|
||||||
|
|
||||||
println!("Board hal init");
|
println!("Board hal init");
|
||||||
let mut board: std::sync::MutexGuard<'_, PlantCtrlBoard<'_>> = BOARD_ACCESS.lock().unwrap();
|
let mut board: std::sync::MutexGuard<'_, PlantCtrlBoard<'_>> = BOARD_ACCESS.lock().unwrap();
|
||||||
|
board.disable_all()?;
|
||||||
println!("Mounting filesystem");
|
println!("Mounting filesystem");
|
||||||
board.mount_file_system()?;
|
board.mount_file_system()?;
|
||||||
let free_space = board.file_system_size()?;
|
let free_space = board.file_system_size()?;
|
||||||
@ -257,11 +388,12 @@ fn main() -> Result<()> {
|
|||||||
board.general_fault(true);
|
board.general_fault(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!("Running logic at utc {}", cur);
|
|
||||||
let europe_time = cur.with_timezone(&Berlin);
|
|
||||||
println!("Running logic at europe/berlin {}", europe_time);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("Running logic at utc {}", cur);
|
||||||
|
let europe_time = cur.with_timezone(&Berlin);
|
||||||
|
println!("Running logic at europe/berlin {}", europe_time);
|
||||||
|
|
||||||
let config: Config;
|
let config: Config;
|
||||||
match board.get_config() {
|
match board.get_config() {
|
||||||
Ok(valid) => {
|
Ok(valid) => {
|
||||||
@ -298,92 +430,66 @@ fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut enough_water = true;
|
let mut enough_water = true;
|
||||||
|
let mut tank_sensor_error = false;
|
||||||
if config.tank_sensor_enabled {
|
if config.tank_sensor_enabled {
|
||||||
let tank_value = board.tank_sensor_mv();
|
let mut tank_value_r = 0;
|
||||||
match tank_value {
|
|
||||||
Ok(tank_raw) => {
|
let success = board.tank_sensor_mv().and_then(|raw| {
|
||||||
//FIXME clear
|
tank_value_r = raw;
|
||||||
let percent = map_range(
|
return map_range(
|
||||||
(config.tank_empty_mv, config.tank_full_mv),
|
(config.tank_empty_mv as f32, config.tank_full_mv as f32),
|
||||||
(0_f32, 100_f32),
|
raw as f32,
|
||||||
tank_raw.into(),
|
);
|
||||||
);
|
}).and_then(|percent| {
|
||||||
let left_ml = ((percent / 100_f32) * config.tank_useable_ml as f32) as u32;
|
let left_ml = ((percent / 100_f32) * config.tank_useable_ml as f32) as u32;
|
||||||
println!(
|
println!(
|
||||||
"Tank sensor returned mv {} as {}% leaving {} ml useable",
|
"Tank sensor returned mv {} as {}% leaving {} ml useable",
|
||||||
tank_raw, percent as u8, left_ml
|
tank_value_r, percent as u8, left_ml
|
||||||
);
|
);
|
||||||
if config.tank_warn_percent > percent as u8 {
|
if config.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_warn_percent
|
|
||||||
);
|
|
||||||
//FIXME warn here
|
|
||||||
}
|
|
||||||
if config.tank_warn_percent <= 0 {
|
|
||||||
enough_water = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
board.general_fault(true);
|
board.general_fault(true);
|
||||||
if !config.tank_allow_pumping_if_sensor_error {
|
println!(
|
||||||
enough_water = false;
|
"Low water, current percent is {}, minimum warn level is {}",
|
||||||
}
|
percent as u8, config.tank_warn_percent
|
||||||
//set tank sensor state to fault
|
);
|
||||||
}
|
}
|
||||||
|
if config.tank_warn_percent <= 0 {
|
||||||
|
enough_water = false;
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
});
|
||||||
|
match success {
|
||||||
|
Err(err) => {
|
||||||
|
println!("Could not determine tank value due to {}", err);
|
||||||
|
board.general_fault(true);
|
||||||
|
tank_sensor_error = true;
|
||||||
|
}
|
||||||
|
Ok(_) => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let plantstate = [PlantState {
|
let mut water_frozen = false;
|
||||||
|
for _attempt in 0..5 {
|
||||||
|
let water_temperature = board.water_temperature_c();
|
||||||
|
match water_temperature {
|
||||||
|
Ok(temp) => {
|
||||||
|
//FIXME mqtt here
|
||||||
|
println!("Water temp is {}", temp);
|
||||||
|
if(temp < 4_f32){
|
||||||
|
water_frozen = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
println!("Could not get water temp {}", err)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut plantstate = [PlantState {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}; PLANT_COUNT];
|
}; PLANT_COUNT];
|
||||||
for plant in 0..PLANT_COUNT {
|
let plant_to_pump = determine_next_plant(&mut plantstate, europe_time, enough_water, tank_sensor_error, &config, &mut board);
|
||||||
let mut state = plantstate[plant];
|
|
||||||
//return mapf(mMoisture_raw.getMedian(), MOIST_SENSOR_MIN_FRQ, MOIST_SENSOR_MAX_FRQ, 0, 100);
|
|
||||||
state.a = map_range(
|
|
||||||
FROM,
|
|
||||||
TO,
|
|
||||||
board.measure_moisture_hz(plant, plant_hal::Sensor::A)? as f32,
|
|
||||||
) as u8;
|
|
||||||
state.b = map_range(
|
|
||||||
FROM,
|
|
||||||
TO,
|
|
||||||
board.measure_moisture_hz(plant, plant_hal::Sensor::B)? as f32,
|
|
||||||
) as u8;
|
|
||||||
state.p = map_range(
|
|
||||||
FROM,
|
|
||||||
TO,
|
|
||||||
board.measure_moisture_hz(plant, plant_hal::Sensor::PUMP)? as f32,
|
|
||||||
) as u8;
|
|
||||||
let plant_config = config.plants[plant];
|
|
||||||
|
|
||||||
//FIXME how to average analyze whatever?
|
|
||||||
if state.a < plant_config.target_moisture || state.b < plant_config.target_moisture {
|
|
||||||
state.dry = true;
|
|
||||||
if !enough_water {
|
|
||||||
state.no_water = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let duration = Duration::minutes((60 * plant_config.pump_cooldown_min).into());
|
|
||||||
if (board.last_pump_time(plant)? + duration) > cur {
|
|
||||||
state.cooldown = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if state.dry {
|
|
||||||
let consecutive_pump_count = board.consecutive_pump_count(plant) + 1;
|
|
||||||
board.store_consecutive_pump_count(plant, consecutive_pump_count);
|
|
||||||
if consecutive_pump_count > config.max_consecutive_pump_count.into() {
|
|
||||||
state.not_effective = true;
|
|
||||||
board.fault(plant, true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
board.store_consecutive_pump_count(plant, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO update mqtt state here!
|
|
||||||
}
|
|
||||||
|
|
||||||
if STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed) {
|
if STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed) {
|
||||||
drop(board);
|
drop(board);
|
||||||
@ -391,30 +497,40 @@ fn main() -> Result<()> {
|
|||||||
let _webserver = httpd(reboot_now.clone());
|
let _webserver = httpd(reboot_now.clone());
|
||||||
wait_infinity(WaitType::StayAlive, reboot_now.clone());
|
wait_infinity(WaitType::StayAlive, reboot_now.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
'eachplant: for plant in 0..PLANT_COUNT {
|
match plant_to_pump {
|
||||||
let mut state = plantstate[plant];
|
Some(plant) => {
|
||||||
if state.dry && !state.cooldown {
|
let mut state = plantstate[plant];
|
||||||
println!("Trying to pump with pump {} now", plant);
|
let consecutive_pump_count = board.consecutive_pump_count(plant) + 1;
|
||||||
|
board.store_consecutive_pump_count(plant, consecutive_pump_count);
|
||||||
let plant_config = config.plants[plant];
|
let plant_config = config.plants[plant];
|
||||||
|
println!("Trying to pump for {}s with pump {} now", plant_config.pump_time_s,plant);
|
||||||
|
|
||||||
board.any_pump(true)?;
|
board.any_pump(true)?;
|
||||||
board.store_last_pump_time(plant, cur);
|
board.store_last_pump_time(plant, cur);
|
||||||
board.pump(plant, true)?;
|
board.pump(plant, true)?;
|
||||||
board.last_pump_time(plant)?;
|
board.last_pump_time(plant);
|
||||||
state.active = true;
|
state.active = true;
|
||||||
unsafe { vTaskDelay(plant_config.pump_time_s.into()) };
|
//FIXME do periodic pump test here and state update
|
||||||
state.after_p = map_range(
|
unsafe { vTaskDelay(plant_config.pump_time_s as u32*CONFIG_FREERTOS_HZ) };
|
||||||
FROM,
|
match map_range_moisture(board.measure_moisture_hz(plant, plant_hal::Sensor::PUMP)? as f32) {
|
||||||
TO,
|
Ok(p) => state.after_p = Some(p),
|
||||||
board.measure_moisture_hz(plant, plant_hal::Sensor::PUMP)? as f32,
|
Err(err) => {
|
||||||
) as u8;
|
board.fault(plant, true);
|
||||||
if state.after_p < state.p + 5 {
|
println!("Could not determine Moisture P after for plant {} due to {}", plant, err);
|
||||||
|
state.after_p = None;
|
||||||
|
state.sensor_error_p = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if state.after_p.is_none() || state.p.is_none() || state.after_p.unwrap() < state.p.unwrap() + 5 {
|
||||||
state.pump_error = true;
|
state.pump_error = true;
|
||||||
board.fault(plant, true);
|
board.fault(plant, true);
|
||||||
}
|
}
|
||||||
break 'eachplant;
|
},
|
||||||
|
None => {
|
||||||
|
println!("Nothing to do");
|
||||||
}
|
}
|
||||||
|
,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -437,6 +553,7 @@ fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
//deepsleep here?
|
//deepsleep here?
|
||||||
|
unsafe { esp_deep_sleep(1000*1000*10) };
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ pub trait PlantCtrlBoardInteraction {
|
|||||||
|
|
||||||
fn measure_moisture_hz(&self, plant: usize, sensor: Sensor) -> Result<i32>;
|
fn measure_moisture_hz(&self, plant: usize, sensor: Sensor) -> Result<i32>;
|
||||||
fn pump(&self, plant: usize, enable: bool) -> Result<()>;
|
fn pump(&self, plant: usize, enable: bool) -> Result<()>;
|
||||||
fn last_pump_time(&self, plant: usize) -> Result<chrono::DateTime<Utc>>;
|
fn last_pump_time(&self, plant: usize) -> chrono::DateTime<Utc>;
|
||||||
fn store_last_pump_time(&mut self, plant: usize, time: chrono::DateTime<Utc>);
|
fn store_last_pump_time(&mut self, plant: usize, time: chrono::DateTime<Utc>);
|
||||||
fn store_consecutive_pump_count(&mut self, plant: usize, count: u32);
|
fn store_consecutive_pump_count(&mut self, plant: usize, count: u32);
|
||||||
fn consecutive_pump_count(&mut self, plant: usize) -> u32;
|
fn consecutive_pump_count(&mut self, plant: usize) -> u32;
|
||||||
@ -163,6 +163,7 @@ pub trait PlantCtrlBoardInteraction {
|
|||||||
fn test(&mut self) -> Result<()>;
|
fn test(&mut self) -> Result<()>;
|
||||||
fn is_wifi_config_file_existant(&mut self) -> bool;
|
fn is_wifi_config_file_existant(&mut self) -> bool;
|
||||||
fn mqtt(&mut self, config: &Config) -> Result<()>;
|
fn mqtt(&mut self, config: &Config) -> Result<()>;
|
||||||
|
fn disable_all(&mut self) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CreatePlantHal<'a> {
|
pub trait CreatePlantHal<'a> {
|
||||||
@ -230,7 +231,7 @@ impl PlantCtrlBoardInteraction for PlantCtrlBoard<'_> {
|
|||||||
if sensor_data.temperature == 85_f32 {
|
if sensor_data.temperature == 85_f32 {
|
||||||
bail!("Ds18b20 dummy temperature returned");
|
bail!("Ds18b20 dummy temperature returned");
|
||||||
}
|
}
|
||||||
Ok(sensor_data.temperature)
|
Ok(sensor_data.temperature/10_f32)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tank_sensor_mv(&mut self) -> Result<u16> {
|
fn tank_sensor_mv(&mut self) -> Result<u16> {
|
||||||
@ -265,11 +266,10 @@ impl PlantCtrlBoardInteraction for PlantCtrlBoard<'_> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn last_pump_time(&self, plant: usize) -> Result<chrono::DateTime<Utc>> {
|
fn last_pump_time(&self, plant: usize) -> chrono::DateTime<Utc> {
|
||||||
let ts = unsafe { LAST_WATERING_TIMESTAMP }[plant];
|
let ts = unsafe { LAST_WATERING_TIMESTAMP }[plant];
|
||||||
let timestamp = NaiveDateTime::from_timestamp_millis(ts)
|
let timestamp = NaiveDateTime::from_timestamp_millis(ts).unwrap();
|
||||||
.ok_or(anyhow!("could not convert timestamp"))?;
|
DateTime::<Utc>::from_naive_utc_and_offset(timestamp, Utc)
|
||||||
Ok(DateTime::<Utc>::from_naive_utc_and_offset(timestamp, Utc))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn store_last_pump_time(&mut self, plant: usize, time: chrono::DateTime<Utc>) {
|
fn store_last_pump_time(&mut self, plant: usize, time: chrono::DateTime<Utc>) {
|
||||||
@ -660,6 +660,15 @@ impl PlantCtrlBoardInteraction for PlantCtrlBoard<'_> {
|
|||||||
}
|
}
|
||||||
bail!("Mqtt did not complete roundtrip in time");
|
bail!("Mqtt did not complete roundtrip in time");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn disable_all(&mut self) -> Result<()> {
|
||||||
|
for mut pin in self.shift_register.decompose() {
|
||||||
|
pin.set_low().unwrap();
|
||||||
|
}
|
||||||
|
self.general_fault(false);
|
||||||
|
self.any_pump(false)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreatePlantHal<'_> for PlantHal {
|
impl CreatePlantHal<'_> for PlantHal {
|
||||||
|
Loading…
Reference in New Issue
Block a user