proceed with bq34z100 extraction

This commit is contained in:
2024-02-02 21:35:04 +01:00
parent 541f7e4471
commit 060a1cc32d
11 changed files with 1371 additions and 1439 deletions

View File

@@ -3,11 +3,13 @@ use std::{
sync::{atomic::AtomicBool, Arc, Mutex},
};
use anyhow::{Result, bail};
use chrono::{Datelike, Duration, NaiveDateTime, Timelike, DateTime};
use anyhow::{bail, Result};
use chrono::{DateTime, Datelike, Duration, NaiveDateTime, Timelike};
use chrono_tz::{Europe::Berlin, Tz};
use esp_idf_hal::delay::Delay;
use esp_idf_sys::{esp_restart, vTaskDelay, CONFIG_FREERTOS_HZ, esp_deep_sleep};
use esp_idf_sys::{
esp_deep_sleep, esp_restart, gpio_deep_sleep_hold_dis, gpio_deep_sleep_hold_en, vTaskDelay, CONFIG_FREERTOS_HZ
};
use esp_ota::rollback_and_reboot;
use log::error;
use once_cell::sync::Lazy;
@@ -51,11 +53,11 @@ enum WaitType {
}
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Default)]
struct LightState{
struct LightState {
active: bool,
out_of_work_hour: bool,
battery_low: bool,
is_day: bool
is_day: bool,
}
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Default)]
@@ -75,7 +77,7 @@ struct PlantState {
sensor_error_a: bool,
sensor_error_b: bool,
sensor_error_p: bool,
out_of_work_hour: bool
out_of_work_hour: bool,
}
fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
@@ -122,10 +124,18 @@ pub static STAY_ALIVE: Lazy<AtomicBool> = Lazy::new(|| AtomicBool::new(false));
fn map_range(from_range: (f32, f32), s: f32) -> Result<f32> {
if s < from_range.0 {
bail!("Value out of range, min {} but current is {}", from_range.0, s);
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);
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));
}
@@ -141,7 +151,7 @@ fn map_range_moisture(s: f32) -> Result<u8> {
return Ok(tmp as u8);
}
fn in_time_range(cur: DateTime<Tz>, start:u8, end:u8) -> bool{
fn in_time_range(cur: DateTime<Tz>, start: u8, end: u8) -> bool {
let curhour = cur.hour() as u8;
//eg 10-14
if start < end {
@@ -152,40 +162,60 @@ fn in_time_range(cur: DateTime<Tz>, start:u8, end:u8) -> bool{
}
}
fn determine_next_plant(plantstate: &mut [PlantState;PLANT_COUNT],cur: DateTime<Tz>, enough_water: bool, water_frozen: bool, tank_sensor_error: bool, config: &Config, board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>) -> Option<usize> {
fn determine_next_plant(
plantstate: &mut [PlantState; PLANT_COUNT],
cur: DateTime<Tz>,
enough_water: bool,
water_frozen: 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::OFF => {}
config::Mode::TargetMoisture => {
match board.measure_moisture_hz(plant, plant_hal::Sensor::A).and_then (|moist| map_range_moisture(moist as f32)) {
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;
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)) {
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;
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?
//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 {
if tank_sensor_error && !config.tank_allow_pumping_if_sensor_error
|| !enough_water
{
state.no_water = true;
}
}
@@ -194,20 +224,24 @@ fn determine_next_plant(plantstate: &mut [PlantState;PLANT_COUNT],cur: DateTime<
if next_pump > cur {
state.cooldown = true;
}
if !in_time_range(cur, plant_config.pump_hour_start, plant_config.pump_hour_end) {
if !in_time_range(
cur,
plant_config.pump_hour_start,
plant_config.pump_hour_end,
) {
state.out_of_work_hour = true;
}
if water_frozen {
state.frozen = true;
}
if state.dry && !state.no_water && !state.cooldown && !state.out_of_work_hour{
if state.dry && !state.no_water && !state.cooldown && !state.out_of_work_hour {
if water_frozen {
state.frozen = true;
} else {
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;
@@ -220,14 +254,18 @@ fn determine_next_plant(plantstate: &mut [PlantState;PLANT_COUNT],cur: DateTime<
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) {
}
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 {
@@ -237,10 +275,10 @@ fn determine_next_plant(plantstate: &mut [PlantState;PLANT_COUNT],cur: DateTime<
state.do_water = true;
}
}
},
}
}
//FIXME publish state here!
if state.do_water{
if state.do_water {
if board.consecutive_pump_count(plant) > config.max_consecutive_pump_count.into() {
state.not_effective = true;
board.fault(plant, true);
@@ -252,13 +290,16 @@ fn determine_next_plant(plantstate: &mut [PlantState;PLANT_COUNT],cur: DateTime<
}
for plant in 0..PLANT_COUNT {
let state = &plantstate[plant];
println!("Checking for water plant {} with state {}", plant, state.do_water);
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
return None;
}
fn safe_main() -> Result<()> {
@@ -284,23 +325,23 @@ fn safe_main() -> Result<()> {
let partition_state: embedded_svc::ota::SlotState = embedded_svc::ota::SlotState::Unknown;
match esp_idf_svc::ota::EspOta::new() {
Ok(ota) => {
//match ota.get_running_slot(){
// Ok(slot) => {
// partition_state = slot.state;
// println!(
// "Booting from {} with state {:?}",
// slot.label, partition_state
// );
//},
// Err(err) => {
// println!("Error getting running slot {}", err);
// },
//}
},
Err(err) => {
println!("Error obtaining ota info {}", err);
},
Ok(ota) => {
//match ota.get_running_slot(){
// Ok(slot) => {
// partition_state = slot.state;
// println!(
// "Booting from {} with state {:?}",
// slot.label, partition_state
// );
//},
// Err(err) => {
// println!("Error getting running slot {}", err);
// },
//}
}
Err(err) => {
println!("Error obtaining ota info {}", err);
}
}
println!("Board hal init");
@@ -402,7 +443,7 @@ fn safe_main() -> Result<()> {
}
if online_mode == OnlineMode::Wifi {
match board.sntp(1000 * 120) {
match board.sntp(1000 * 5) {
Ok(new_time) => {
cur = new_time;
online_mode = OnlineMode::SnTp;
@@ -458,37 +499,43 @@ fn safe_main() -> Result<()> {
if config.tank_sensor_enabled {
let mut tank_value_r = 0;
let success = board.tank_sensor_mv().and_then(|raw| {
tank_value_r = raw;
return map_range(
(config.tank_empty_mv as f32, config.tank_full_mv as f32),
raw as f32,
);
}).and_then(|percent| {
let left_ml = ((percent / 100_f32) * config.tank_useable_ml as f32) as u32;
println!(
"Tank sensor returned mv {} as {}% leaving {} ml useable",
tank_value_r, percent as u8, left_ml
);
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
let success = board
.tank_sensor_percent()
.and_then(|raw| {
tank_value_r = raw;
return map_range(
(
config.tank_empty_percent as f32,
config.tank_full_percent as f32,
),
raw as f32,
);
}
if config.tank_warn_percent <= 0 {
enough_water = false;
}
return Ok(());
});
})
.and_then(|percent| {
let left_ml = (percent * config.tank_useable_ml as f32) as u32;
println!(
"Tank sensor returned mv {} as {}% leaving {} ml useable",
tank_value_r, percent as u8, left_ml
);
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
);
}
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(_) => {},
Ok(_) => {}
}
}
@@ -503,17 +550,25 @@ fn safe_main() -> Result<()> {
water_frozen = true;
}
break;
},
}
Err(err) => {
println!("Could not get water temp {}", err)
},
}
}
}
let mut plantstate = [PlantState {
..Default::default()
}; PLANT_COUNT];
let plant_to_pump = determine_next_plant(&mut plantstate, europe_time, enough_water, water_frozen, tank_sensor_error, &config, &mut board);
let plant_to_pump = determine_next_plant(
&mut plantstate,
europe_time,
enough_water,
water_frozen,
tank_sensor_error,
&config,
&mut board,
);
if STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed) {
drop(board);
@@ -521,72 +576,94 @@ fn safe_main() -> Result<()> {
let _webserver = httpd(reboot_now.clone());
wait_infinity(WaitType::StayAlive, reboot_now.clone());
}
match plant_to_pump {
Some(plant) => {
let mut state = plantstate[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];
println!("Trying to pump for {}s with pump {} now", plant_config.pump_time_s,plant);
println!(
"Trying to pump for {}s with pump {} now",
plant_config.pump_time_s, plant
);
board.any_pump(true)?;
board.store_last_pump_time(plant, cur);
board.pump(plant, true)?;
board.last_pump_time(plant);
state.active = true;
//FIXME do periodic pump test here and state update
unsafe { vTaskDelay(plant_config.pump_time_s as u32*CONFIG_FREERTOS_HZ) };
match map_range_moisture(board.measure_moisture_hz(plant, plant_hal::Sensor::PUMP)? as f32) {
unsafe { vTaskDelay(plant_config.pump_time_s as u32 * CONFIG_FREERTOS_HZ) };
board.pump(plant, false)?;
match map_range_moisture(
board.measure_moisture_hz(plant, plant_hal::Sensor::PUMP)? as f32
) {
Ok(p) => state.after_p = Some(p),
Err(err) => {
board.fault(plant, true);
println!("Could not determine Moisture P after for plant {} due to {}", plant, err);
state.after_p = None;
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 {
if state.after_p.is_none()
|| state.p.is_none()
|| state.after_p.unwrap() < state.p.unwrap() + 5
{
state.pump_error = true;
board.fault(plant, true);
}
},
}
None => {
println!("Nothing to do");
}
,
}
let mut light_state = LightState{ ..Default::default() };
let mut light_state = LightState {
..Default::default()
};
light_state.is_day = board.is_day();
light_state.out_of_work_hour = !in_time_range(europe_time, config.night_lamp_hour_start, config.night_lamp_hour_end);
light_state.out_of_work_hour = !in_time_range(
europe_time,
config.night_lamp_hour_start,
config.night_lamp_hour_end,
);
if !light_state.out_of_work_hour {
if config.night_lamp_only_when_dark {
if !light_state.is_day {
light_state.active = true;
board.light(true).unwrap();
}
}else {
} else {
light_state.active = true;
board.light(true).unwrap();
}
} else {
light_state.active = false;
board.light(false).unwrap();
}
println!("Lightstate is {:?}", light_state);
//check if during light time
//lightstate += out of worktime
//check battery level
//lightstate += battery empty
//check solar level if config requires
//lightstate += stillday
//if no preventing lightstate, enable light
//lightstate = active
//deepsleep here?
unsafe { esp_deep_sleep(1000*1000*10) };
//check if during light time
//lightstate += out of worktime
//check battery level
//lightstate += battery empty
//check solar level if config requires
//lightstate += stillday
//if no preventing lightstate, enable light
//lightstate = active
//relatch
unsafe{gpio_deep_sleep_hold_dis()};
unsafe { gpio_deep_sleep_hold_en() };
unsafe { esp_deep_sleep(1000 * 1000 * 20) };
}
fn main(){
fn main() {
let result = safe_main();
result.unwrap();
}