Part 1 of V3 extraction in preperation of v4 and v3 software merge #16
| @@ -5,8 +5,8 @@ target = "riscv32imac-esp-espidf" | ||||
| [target.riscv32imac-esp-espidf] | ||||
| linker = "ldproxy" | ||||
| #runner = "espflash flash --monitor --baud 921600 --partition-table partitions.csv -b no-reset" # Select this runner in case of usb ttl | ||||
| #runner = "espflash flash --monitor --baud 921600 --flash-size 16mb --partition-table  partitions.csv" | ||||
| runner = "cargo runner" | ||||
| runner = "espflash flash --monitor --baud 921600 --flash-size 16mb --partition-table  partitions.csv" | ||||
| #runner = "cargo runner" | ||||
|  | ||||
|  | ||||
| #runner = "espflash flash --monitor --partition-table partitions.csv -b no-reset" # create upgrade image file for webupload | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| use std::str::FromStr; | ||||
|  | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use std::str::FromStr; | ||||
|  | ||||
| use crate::plant_state::PlantWateringMode; | ||||
| use crate::PLANT_COUNT; | ||||
| @@ -13,6 +12,7 @@ pub struct NetworkConfig { | ||||
|     pub password: Option<heapless::String<64>>, | ||||
|     pub mqtt_url: Option<heapless::String<128>>, | ||||
|     pub base_topic: Option<heapless::String<64>>, | ||||
|     pub max_wait: u32 | ||||
| } | ||||
| impl Default for NetworkConfig { | ||||
|     fn default() -> Self { | ||||
| @@ -22,6 +22,7 @@ impl Default for NetworkConfig { | ||||
|             password: None, | ||||
|             mqtt_url: None, | ||||
|             base_topic: None, | ||||
|             max_wait: 10000, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -72,9 +73,26 @@ impl Default for TankConfig { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] | ||||
| pub enum BatteryBoardVersion{ | ||||
|     #[default] | ||||
|     Disabled, | ||||
|     BQ34Z100G1, | ||||
|     WchI2cSlave | ||||
| } | ||||
| #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] | ||||
| pub enum BoardVersion{ | ||||
|     #[default] | ||||
|     INITIAL, | ||||
|     V3, | ||||
|     V4 | ||||
| } | ||||
|  | ||||
| #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] | ||||
| #[serde(default)] | ||||
| pub struct PlantControllerConfig { | ||||
|     pub board_hardware: BoardVersion, | ||||
|     pub battery_hardware: BatteryBoardVersion, | ||||
|     pub network: NetworkConfig, | ||||
|     pub tank: TankConfig, | ||||
|     pub night_lamp: NightLampConfig, | ||||
|   | ||||
							
								
								
									
										140
									
								
								rust/src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										140
									
								
								rust/src/main.rs
									
									
									
									
									
								
							| @@ -1,4 +1,4 @@ | ||||
| use crate::{config::PlantControllerConfig, webserver::webserver::httpd}; | ||||
| use crate::webserver::webserver::httpd; | ||||
| use anyhow::bail; | ||||
| use chrono::{DateTime, Datelike, Timelike, Utc}; | ||||
| use chrono_tz::Tz; | ||||
| @@ -27,11 +27,11 @@ pub mod plant_hal; | ||||
| mod plant_state; | ||||
| mod tank; | ||||
|  | ||||
| use crate::plant_hal::{BatteryInteraction, BoardInteraction, HAL}; | ||||
| use crate::plant_hal::{BatteryInteraction, BoardHal, BoardInteraction, HAL}; | ||||
| use plant_state::PlantState; | ||||
| use tank::*; | ||||
|  | ||||
| pub static BOARD_ACCESS: Lazy<Mutex<HAL>> = Lazy::new(|| PlantHal::create_v3().unwrap()); | ||||
| pub static BOARD_ACCESS: Lazy<Mutex<HAL>> = Lazy::new(|| PlantHal::create().unwrap()); | ||||
| pub static STAY_ALIVE: Lazy<AtomicBool> = Lazy::new(|| AtomicBool::new(false)); | ||||
|  | ||||
| mod webserver { | ||||
| @@ -113,7 +113,6 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|             esp_idf_sys::CONFIG_MAIN_TASK_STACK_SIZE | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     println!("Startup Rust"); | ||||
|  | ||||
|     let mut to_config = false; | ||||
| @@ -151,20 +150,9 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|     }; | ||||
|     log(LogMessage::PartitionState, 0, 0, "", ota_state_string); | ||||
|  | ||||
|     let mut board = BOARD_ACCESS.lock().unwrap(); | ||||
|     let mut board = BOARD_ACCESS.lock().expect("Could not lock board no other lock should be able to exist during startup!"); | ||||
|     board.general_fault(false); | ||||
|  | ||||
|     log(LogMessage::MountingFilesystem, 0, 0, "", ""); | ||||
|     board.mount_file_system()?; | ||||
|     let free_space = board.file_system_size()?; | ||||
|     log( | ||||
|         LogMessage::FilesystemMount, | ||||
|         free_space.free_size as u32, | ||||
|         free_space.total_size as u32, | ||||
|         &free_space.used_size.to_string(), | ||||
|         "", | ||||
|     ); | ||||
|  | ||||
|     let cur = board | ||||
|         .get_rtc_time() | ||||
|         .or_else(|err| { | ||||
| @@ -220,28 +208,22 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let config: PlantControllerConfig = match board.get_config() { | ||||
|         Ok(valid) => valid, | ||||
|         Err(err) => { | ||||
|             log( | ||||
|                 LogMessage::ConfigModeMissingConfig, | ||||
|                 0, | ||||
|                 0, | ||||
|                 "", | ||||
|                 &err.to_string(), | ||||
|             ); | ||||
|             //config upload will trigger reboot! | ||||
|             let _ = board.wifi_ap(None); | ||||
|     match board.board_hal { | ||||
|         BoardHal::Initial { .. } => { | ||||
|             //config upload will trigger reboot and then switch to selected board_hal | ||||
|             let _ = board.wifi_ap(); | ||||
|             drop(board); | ||||
|             let reboot_now = Arc::new(AtomicBool::new(false)); | ||||
|             let _webserver = httpd(reboot_now.clone()); | ||||
|             wait_infinity(WaitType::MissingConfig, reboot_now.clone()); | ||||
|         } | ||||
|     }; | ||||
|         _ => {} | ||||
|     } | ||||
|  | ||||
|  | ||||
|     println!("attempting to connect wifi"); | ||||
|     let network_mode = if config.network.ssid.is_some() { | ||||
|         try_connect_wifi_sntp_mqtt(&mut board, &config) | ||||
|     let network_mode = if board.config.network.ssid.is_some() { | ||||
|         try_connect_wifi_sntp_mqtt(&mut board) | ||||
|     } else { | ||||
|         println!("No wifi configured"); | ||||
|         NetworkMode::OFFLINE | ||||
| @@ -249,7 +231,7 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|  | ||||
|     if matches!(network_mode, NetworkMode::OFFLINE) && to_config { | ||||
|         println!("Could not connect to station and config mode forced, switching to ap mode!"); | ||||
|         match board.wifi_ap(Some(config.network.ap_ssid.clone())) { | ||||
|         match board.wifi_ap() { | ||||
|             Ok(_) => { | ||||
|                 println!("Started ap, continuing") | ||||
|             } | ||||
| @@ -257,7 +239,7 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let timezone = match &config.timezone { | ||||
|     let timezone = match & board.config.timezone { | ||||
|         Some(tz_str) => tz_str.parse::<Tz>().unwrap_or_else(|_| { | ||||
|             println!("Invalid timezone '{}', falling back to UTC", tz_str); | ||||
|             UTC | ||||
| @@ -274,8 +256,8 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|     ); | ||||
|  | ||||
|     if let NetworkMode::WIFI { ref ip_address, .. } = network_mode { | ||||
|         publish_firmware_info(version, address, ota_state_string, &mut board, &config, &ip_address, timezone_time); | ||||
|         publish_battery_state(&mut board, &config); | ||||
|         publish_firmware_info(version, address, ota_state_string, &mut board, &ip_address, timezone_time); | ||||
|         publish_battery_state(&mut board); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -301,10 +283,10 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|  | ||||
|     let dry_run = false; | ||||
|  | ||||
|     let tank_state = determine_tank_state(&mut board, &config); | ||||
|     let tank_state = determine_tank_state(&mut board); | ||||
|  | ||||
|     if tank_state.is_enabled() { | ||||
|         if let Some(err) = tank_state.got_error(&config.tank) { | ||||
|         if let Some(err) = tank_state.got_error(&board.config.tank) { | ||||
|             match err { | ||||
|                 TankError::SensorDisabled => { /* unreachable */ } | ||||
|                 TankError::SensorMissing(raw_value_mv) => log( | ||||
| @@ -327,7 +309,7 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|             } | ||||
|             // disabled cannot trigger this because of wrapping if is_enabled | ||||
|             board.general_fault(true); | ||||
|         } else if tank_state.warn_level(&config.tank).is_ok_and(|warn| warn) { | ||||
|         } else if tank_state.warn_level(&board.config.tank).is_ok_and(|warn| warn) { | ||||
|             log(LogMessage::TankWaterLevelLow, 0, 0, "", ""); | ||||
|             board.general_fault(true); | ||||
|         } | ||||
| @@ -342,15 +324,15 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     publish_tank_state(&mut board, &config, &tank_state, &water_temp); | ||||
|     publish_tank_state(&mut board, &tank_state, &water_temp); | ||||
|  | ||||
|     let plantstate: [PlantState; PLANT_COUNT] = | ||||
|         core::array::from_fn(|i| PlantState::read_hardware_state(i, &mut board, &config.plants[i])); | ||||
|     publish_plant_states(&mut board, &config, &timezone_time, &plantstate); | ||||
|         core::array::from_fn(|i| PlantState::read_hardware_state(i, &mut board)); | ||||
|     publish_plant_states(&mut board, &timezone_time, &plantstate); | ||||
|  | ||||
|     let pump_required = plantstate | ||||
|         .iter() | ||||
|         .zip(&config.plants) | ||||
|         .zip(&board.config.plants) | ||||
|         .any(|(it, conf)| it.needs_to_be_watered(conf, &timezone_time)) | ||||
|         && !water_frozen; | ||||
|     if pump_required { | ||||
| @@ -358,7 +340,7 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|         if !dry_run { | ||||
|             board.any_pump(true)?; // enables main power output, eg for a central pump with valve setup or a main water valve for the risk affine | ||||
|         } | ||||
|         for (plant_id, (state, plant_config)) in plantstate.iter().zip(&config.plants).enumerate() { | ||||
|         for (plant_id, (state, plant_config)) in plantstate.iter().zip(&board.config.plants.clone()).enumerate() { | ||||
|             if state.needs_to_be_watered(plant_config, &timezone_time) { | ||||
|                 let pump_count = board.consecutive_pump_count(plant_id) + 1; | ||||
|                 board.store_consecutive_pump_count(plant_id, pump_count); | ||||
| @@ -385,14 +367,14 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|                 board.last_pump_time(plant_id); | ||||
|                 //state.active = true; | ||||
|  | ||||
|                 pump_info(&mut board, &config, plant_id, true, pump_ineffective); | ||||
|                 pump_info(&mut board,  plant_id, true, pump_ineffective); | ||||
|  | ||||
|                 if !dry_run { | ||||
|                     board.pump(plant_id, true)?; | ||||
|                     Delay::new_default().delay_ms(1000 * plant_config.pump_time_s as u32); | ||||
|                     board.pump(plant_id, false)?; | ||||
|                 } | ||||
|                 pump_info(&mut board, &config, plant_id, false, pump_ineffective); | ||||
|                 pump_info(&mut board,  plant_id, false, pump_ineffective); | ||||
|  | ||||
|             } else if !state.pump_in_timeout(plant_config, &timezone_time) { | ||||
|                 // plant does not need to be watered and is not in timeout | ||||
| @@ -409,26 +391,26 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|     let state_of_charge = board.battery_monitor.state_charge_percent().unwrap_or(0); | ||||
|  | ||||
|     let mut light_state = LightState { | ||||
|         enabled: config.night_lamp.enabled, | ||||
|         enabled: board.config.night_lamp.enabled, | ||||
|         ..Default::default() | ||||
|     }; | ||||
|     if light_state.enabled { | ||||
|         light_state.is_day = is_day; | ||||
|         light_state.out_of_work_hour = !in_time_range( | ||||
|             &timezone_time, | ||||
|             config.night_lamp.night_lamp_hour_start, | ||||
|             config.night_lamp.night_lamp_hour_end, | ||||
|             board.config.night_lamp.night_lamp_hour_start, | ||||
|             board.config.night_lamp.night_lamp_hour_end, | ||||
|         ); | ||||
|  | ||||
|         if state_of_charge < config.night_lamp.low_soc_cutoff { | ||||
|         if state_of_charge < board.config.night_lamp.low_soc_cutoff { | ||||
|             board.set_low_voltage_in_cycle(); | ||||
|         } else if state_of_charge > config.night_lamp.low_soc_restore { | ||||
|         } else if state_of_charge > board.config.night_lamp.low_soc_restore { | ||||
|             board.clear_low_voltage_in_cycle(); | ||||
|         } | ||||
|         light_state.battery_low = board.low_voltage_in_cycle(); | ||||
|  | ||||
|         if !light_state.out_of_work_hour { | ||||
|             if config.night_lamp.night_lamp_only_when_dark { | ||||
|             if board.config.night_lamp.night_lamp_only_when_dark { | ||||
|                 if !light_state.is_day { | ||||
|                     if light_state.battery_low { | ||||
|                         board.light(false)?; | ||||
| @@ -453,7 +435,7 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|  | ||||
|     match serde_json::to_string(&light_state) { | ||||
|         Ok(state) => { | ||||
|             let _ = board.mqtt_publish(&config, "/light", state.as_bytes()); | ||||
|             let _ = board.mqtt_publish( "/light", state.as_bytes()); | ||||
|         } | ||||
|         Err(err) => { | ||||
|             println!("Error publishing lightstate {}", err); | ||||
| @@ -461,16 +443,16 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|     }; | ||||
|  | ||||
|     let deep_sleep_duration_minutes: u32 = if state_of_charge < 10 { | ||||
|         let _ = board.mqtt_publish(&config, "/deepsleep", "low Volt 12h".as_bytes()); | ||||
|         let _ = board.mqtt_publish( "/deepsleep", "low Volt 12h".as_bytes()); | ||||
|         12 * 60 | ||||
|     } else if is_day { | ||||
|         let _ = board.mqtt_publish(&config, "/deepsleep", "normal 20m".as_bytes()); | ||||
|         let _ = board.mqtt_publish( "/deepsleep", "normal 20m".as_bytes()); | ||||
|         20 | ||||
|     } else { | ||||
|         let _ = board.mqtt_publish(&config, "/deepsleep", "night 1h".as_bytes()); | ||||
|         let _ = board.mqtt_publish( "/deepsleep", "night 1h".as_bytes()); | ||||
|         60 | ||||
|     }; | ||||
|     let _ = board.mqtt_publish(&config, "/state", "sleep".as_bytes()); | ||||
|     let _ = board.mqtt_publish( "/state", "sleep".as_bytes()); | ||||
|  | ||||
|     mark_app_valid(); | ||||
|  | ||||
| @@ -512,10 +494,10 @@ fn obtain_tank_temperature(board: &mut MutexGuard<HAL>) -> anyhow::Result<f32> { | ||||
|     water_temp | ||||
| } | ||||
|  | ||||
| fn publish_tank_state(board: &mut MutexGuard<HAL>, config: &PlantControllerConfig, tank_state: &TankState, water_temp: &anyhow::Result<f32>) { | ||||
|     match serde_json::to_string(&tank_state.as_mqtt_info(&config.tank, water_temp)) { | ||||
| fn publish_tank_state(board: &mut MutexGuard<HAL>, tank_state: &TankState, water_temp: &anyhow::Result<f32>) { | ||||
|     match serde_json::to_string(&tank_state.as_mqtt_info(&board.config.tank, water_temp)) { | ||||
|         Ok(state) => { | ||||
|             let _ = board.mqtt_publish(&config, "/water", state.as_bytes()); | ||||
|             let _ = board.mqtt_publish("/water", state.as_bytes()); | ||||
|         } | ||||
|         Err(err) => { | ||||
|             println!("Error publishing tankstate {}", err); | ||||
| @@ -523,12 +505,12 @@ fn publish_tank_state(board: &mut MutexGuard<HAL>, config: &PlantControllerConfi | ||||
|     }; | ||||
| } | ||||
|  | ||||
| fn publish_plant_states(board: &mut MutexGuard<HAL>, config: &PlantControllerConfig, timezone_time: &DateTime<Tz>, plantstate: &[PlantState; 8]) { | ||||
|     for (plant_id, (plant_state, plant_conf)) in plantstate.iter().zip(&config.plants).enumerate() { | ||||
| fn publish_plant_states(board: &mut MutexGuard<HAL>, timezone_time: &DateTime<Tz>, plantstate: &[PlantState; 8]) { | ||||
|     for (plant_id, (plant_state, plant_conf)) in plantstate.iter().zip(& board.config.plants.clone()).enumerate() { | ||||
|         match serde_json::to_string(&plant_state.to_mqtt_info(plant_conf, &timezone_time)) { | ||||
|             Ok(state) => { | ||||
|                 let plant_topic = format!("/plant{}", plant_id + 1); | ||||
|                 let _ = board.mqtt_publish(&config, &plant_topic, state.as_bytes()); | ||||
|                 let _ = board.mqtt_publish(&plant_topic, state.as_bytes()); | ||||
|                 //reduce speed as else messages will be dropped | ||||
|                 Delay::new_default().delay_ms(200); | ||||
|             } | ||||
| @@ -539,34 +521,27 @@ fn publish_plant_states(board: &mut MutexGuard<HAL>, config: &PlantControllerCon | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn publish_firmware_info(version: VersionInfo, address: u32, ota_state_string: &str, board: &mut MutexGuard<HAL>, config: &PlantControllerConfig, ip_address: &String, timezone_time: DateTime<Tz>) { | ||||
|     let _ = board.mqtt_publish(&config, "/firmware/address", ip_address.as_bytes()); | ||||
|     let _ = board.mqtt_publish(&config, "/firmware/githash", version.git_hash.as_bytes()); | ||||
| fn publish_firmware_info(version: VersionInfo, address: u32, ota_state_string: &str, board: &mut MutexGuard<HAL>, ip_address: &String, timezone_time: DateTime<Tz>) { | ||||
|     let _ = board.mqtt_publish("/firmware/address", ip_address.as_bytes()); | ||||
|     let _ = board.mqtt_publish( "/firmware/githash", version.git_hash.as_bytes()); | ||||
|     let _ = board.mqtt_publish( | ||||
|         &config, | ||||
|         "/firmware/buildtime", | ||||
|         version.build_time.as_bytes(), | ||||
|     ); | ||||
|     let _ = board.mqtt_publish( | ||||
|         &config, | ||||
|         "/firmware/last_online", | ||||
|         timezone_time.to_rfc3339().as_bytes(), | ||||
|     ); | ||||
|     let _ = board.mqtt_publish(&config, "/firmware/ota_state", ota_state_string.as_bytes()); | ||||
|     let _ = board.mqtt_publish( "/firmware/ota_state", ota_state_string.as_bytes()); | ||||
|     let _ = board.mqtt_publish( | ||||
|         &config, | ||||
|         "/firmware/partition_address", | ||||
|         format!("{:#06x}", address).as_bytes(), | ||||
|     ); | ||||
|     let _ = board.mqtt_publish(&config, "/state", "online".as_bytes()); | ||||
|     let _ = board.mqtt_publish( "/state", "online".as_bytes()); | ||||
| } | ||||
|  | ||||
| fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard<HAL>, config: &PlantControllerConfig) -> NetworkMode{ | ||||
|     match board.wifi( | ||||
|         config.network.ssid.clone().unwrap(), | ||||
|         config.network.password.clone(), | ||||
|         10000, | ||||
|     ) { | ||||
| fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard<HAL>) -> NetworkMode{ | ||||
|     match board.wifi() { | ||||
|         Ok(ip_info) => { | ||||
|             let sntp_mode: SntpMode = match board.sntp(1000 * 10) { | ||||
|                 Ok(new_time) => { | ||||
| @@ -580,8 +555,8 @@ fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard<HAL>, config: &PlantControl | ||||
|                     SntpMode::OFFLINE | ||||
|                 } | ||||
|             }; | ||||
|             let mqtt_connected = if let Some(_) = config.network.mqtt_url { | ||||
|                 match board.mqtt(&config) { | ||||
|             let mqtt_connected = if let Some(_) = board.config.network.mqtt_url { | ||||
|                 match board.mqtt() { | ||||
|                     Ok(_) => { | ||||
|                         println!("Mqtt connection ready"); | ||||
|                         true | ||||
| @@ -609,7 +584,7 @@ fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard<HAL>, config: &PlantControl | ||||
| } | ||||
|  | ||||
| //TODO clean this up? better state | ||||
| fn pump_info(board: &mut MutexGuard<HAL>, config: &PlantControllerConfig, plant_id: usize, pump_active: bool, pump_ineffective: bool)  { | ||||
| fn pump_info(board: &mut MutexGuard<HAL>, plant_id: usize, pump_active: bool, pump_ineffective: bool)  { | ||||
|     let pump_info = PumpInfo { | ||||
|         enabled: pump_active, | ||||
|         pump_ineffective | ||||
| @@ -617,7 +592,7 @@ fn pump_info(board: &mut MutexGuard<HAL>, config: &PlantControllerConfig, plant_ | ||||
|     let pump_topic = format!("/pump{}", plant_id + 1); | ||||
|     match serde_json::to_string(&pump_info) { | ||||
|         Ok(state) => { | ||||
|             let _ = board.mqtt_publish(config, &pump_topic, state.as_bytes()); | ||||
|             let _ = board.mqtt_publish(&pump_topic, state.as_bytes()); | ||||
|             //reduce speed as else messages will be dropped | ||||
|             Delay::new_default().delay_ms(200); | ||||
|         } | ||||
| @@ -628,11 +603,10 @@ fn pump_info(board: &mut MutexGuard<HAL>, config: &PlantControllerConfig, plant_ | ||||
| } | ||||
|  | ||||
| fn publish_battery_state( | ||||
|     board: &mut MutexGuard<'_, HAL<'_>>, | ||||
|     config: &PlantControllerConfig, | ||||
|     board: &mut MutexGuard<'_, HAL<'_>> | ||||
| ) { | ||||
|     let state = board.get_battery_state(); | ||||
|     let _ = board.mqtt_publish(config, "/battery", state.as_bytes()); | ||||
|     let _ = board.mqtt_publish( "/battery", state.as_bytes()); | ||||
| } | ||||
|  | ||||
| fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! { | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -114,15 +114,14 @@ fn map_range_moisture( | ||||
| impl PlantState { | ||||
|     pub fn read_hardware_state( | ||||
|         plant_id: usize, | ||||
|         board: &mut plant_hal::HAL, | ||||
|         config: &PlantConfig, | ||||
|         board: &mut plant_hal::HAL | ||||
|     ) -> Self { | ||||
|         let sensor_a = if config.sensor_a { | ||||
|         let sensor_a = if board.config.plants[plant_id].sensor_a { | ||||
|             match board.measure_moisture_hz(plant_id, plant_hal::Sensor::A) { | ||||
|                 Ok(raw) => match map_range_moisture( | ||||
|                     raw, | ||||
|                     config.moisture_sensor_min_frequency, | ||||
|                     config.moisture_sensor_max_frequency, | ||||
|                     board.config.plants[plant_id].moisture_sensor_min_frequency, | ||||
|                     board.config.plants[plant_id].moisture_sensor_max_frequency, | ||||
|                 ) { | ||||
|                     Ok(moisture_percent) => MoistureSensorState::MoistureValue { | ||||
|                         raw_hz: raw, | ||||
| @@ -138,12 +137,12 @@ impl PlantState { | ||||
|             MoistureSensorState::Disabled | ||||
|         }; | ||||
|  | ||||
|         let sensor_b = if config.sensor_b { | ||||
|         let sensor_b = if board.config.plants[plant_id].sensor_b { | ||||
|             match board.measure_moisture_hz(plant_id, plant_hal::Sensor::B) { | ||||
|                 Ok(raw) => match map_range_moisture( | ||||
|                     raw, | ||||
|                     config.moisture_sensor_min_frequency, | ||||
|                     config.moisture_sensor_max_frequency, | ||||
|                     board.config.plants[plant_id].moisture_sensor_min_frequency, | ||||
|                     board.config.plants[plant_id].moisture_sensor_max_frequency, | ||||
|                 ) { | ||||
|                     Ok(moisture_percent) => MoistureSensorState::MoistureValue { | ||||
|                         raw_hz: raw, | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| use serde::Serialize; | ||||
|  | ||||
| use crate::config::TankConfig; | ||||
| use crate::plant_hal::{BoardInteraction, HAL}; | ||||
| use crate::config::{PlantControllerConfig, TankConfig}; | ||||
|  | ||||
| const OPEN_TANK_VOLTAGE: f32 = 3.0; | ||||
| pub const WATER_FROZEN_THRESH: f32 = 4.0; | ||||
| @@ -156,10 +156,9 @@ impl TankState { | ||||
| } | ||||
|  | ||||
| pub fn determine_tank_state( | ||||
|     board: &mut std::sync::MutexGuard<'_, HAL<'_>>, | ||||
|     config: &PlantControllerConfig, | ||||
|     board: &mut std::sync::MutexGuard<'_, HAL<'_>> | ||||
| ) -> TankState { | ||||
|     if config.tank.tank_sensor_enabled { | ||||
|     if board.config.tank.tank_sensor_enabled { | ||||
|         match board.tank_sensor_voltage() { | ||||
|             Ok(raw_sensor_value_mv) => TankState::Present(raw_sensor_value_mv), | ||||
|             Err(err) => TankState::Error(TankError::BoardError(err.to_string())), | ||||
|   | ||||
| @@ -114,11 +114,9 @@ fn get_timezones( | ||||
| fn get_live_moisture( | ||||
|     _request: &mut Request<&mut EspHttpConnection>, | ||||
| ) -> Result<Option<std::string::String>, anyhow::Error> { | ||||
|     let mut board = BOARD_ACCESS.lock().unwrap(); | ||||
|     let config = board.get_config().unwrap(); | ||||
|  | ||||
|     let mut board = BOARD_ACCESS.lock().expect("Should never fail"); | ||||
|     let plant_state = Vec::from_iter( | ||||
|         (0..PLANT_COUNT).map(|i| PlantState::read_hardware_state(i, &mut board, &config.plants[i])), | ||||
|         (0..PLANT_COUNT).map(|i| PlantState::read_hardware_state(i, &mut board)), | ||||
|     ); | ||||
|     let a = Vec::from_iter( | ||||
|         plant_state | ||||
| @@ -159,11 +157,8 @@ fn get_live_moisture( | ||||
| fn get_config( | ||||
|     _request: &mut Request<&mut EspHttpConnection>, | ||||
| ) -> Result<Option<std::string::String>, anyhow::Error> { | ||||
|     let mut board = BOARD_ACCESS.lock().unwrap(); | ||||
|     let json = match board.get_config() { | ||||
|         Ok(config) => serde_json::to_string(&config)?, | ||||
|         Err(_) => serde_json::to_string(&PlantControllerConfig::default())?, | ||||
|     }; | ||||
|     let board = BOARD_ACCESS.lock().expect("Should never fail"); | ||||
|     let json = serde_json::to_string(&board.config)?; | ||||
|     anyhow::Ok(Some(json)) | ||||
| } | ||||
|  | ||||
| @@ -193,7 +188,7 @@ fn get_backup_config( | ||||
| fn backup_info( | ||||
|     _request: &mut Request<&mut EspHttpConnection>, | ||||
| ) -> Result<Option<std::string::String>, anyhow::Error> { | ||||
|     let mut board = BOARD_ACCESS.lock().unwrap(); | ||||
|     let mut board = BOARD_ACCESS.lock().expect("Should never fail"); | ||||
|     let header = board.get_backup_info(); | ||||
|     let json = match header { | ||||
|         Ok(h) => { | ||||
| @@ -222,7 +217,9 @@ fn set_config( | ||||
|     let all = read_up_to_bytes_from_request(request, Some(3072))?; | ||||
|     let config: PlantControllerConfig = serde_json::from_slice(&all)?; | ||||
|     let mut board = BOARD_ACCESS.lock().unwrap(); | ||||
|     board.set_config(&config)?; | ||||
|     board.esp.set_config(&config)?; | ||||
|  | ||||
|     board.config = config; | ||||
|     anyhow::Ok(Some("saved".to_owned())) | ||||
| } | ||||
|  | ||||
| @@ -268,12 +265,11 @@ fn tank_info( | ||||
|     _request: &mut Request<&mut EspHttpConnection>, | ||||
| ) -> Result<Option<std::string::String>, anyhow::Error> { | ||||
|     let mut board = BOARD_ACCESS.lock().unwrap(); | ||||
|     let config = board.get_config()?; | ||||
|     let tank_info = determine_tank_state(&mut board, &config); | ||||
|     let tank_info = determine_tank_state(&mut board); | ||||
|     //should be multsampled | ||||
|     let water_temp = board.water_temperature_c(); | ||||
|     Ok(Some(serde_json::to_string( | ||||
|         &tank_info.as_mqtt_info(&config.tank, &water_temp), | ||||
|         &tank_info.as_mqtt_info(&board.config.tank, &water_temp), | ||||
|     )?)) | ||||
| } | ||||
|  | ||||
| @@ -302,8 +298,8 @@ fn wifi_scan( | ||||
| fn list_files( | ||||
|     _request: &mut Request<&mut EspHttpConnection>, | ||||
| ) -> Result<Option<std::string::String>, anyhow::Error> { | ||||
|     let board = BOARD_ACCESS.lock().unwrap(); | ||||
|     let result = board.list_files(); | ||||
|     let board = BOARD_ACCESS.lock().expect("It should be possible to lock the board for exclusive fs access"); | ||||
|     let result = board.esp.list_files(); | ||||
|     let file_list_json = serde_json::to_string(&result)?; | ||||
|     anyhow::Ok(Some(file_list_json)) | ||||
| } | ||||
| @@ -493,6 +489,7 @@ pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> { | ||||
|             let file_handle = BOARD_ACCESS | ||||
|                 .lock() | ||||
|                 .unwrap() | ||||
|                 .esp | ||||
|                 .get_file_handle(&filename, false); | ||||
|             match file_handle { | ||||
|                 Ok(mut file_handle) => { | ||||
| @@ -529,7 +526,7 @@ pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> { | ||||
|         .fn_handler("/file", Method::Post, move |mut request| { | ||||
|             let filename = query_param(request.uri(), "filename").unwrap(); | ||||
|             let lock = BOARD_ACCESS.lock().unwrap(); | ||||
|             let file_handle = lock.get_file_handle(&filename, true); | ||||
|             let file_handle = lock.esp.get_file_handle(&filename, true); | ||||
|             match file_handle { | ||||
|                 //TODO get free filesystem size, check against during write if not to large | ||||
|                 Ok(mut file_handle) => { | ||||
| @@ -573,7 +570,7 @@ pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> { | ||||
|             let filename = query_param(request.uri(), "filename").unwrap(); | ||||
|             let copy = filename.clone(); | ||||
|             let board = BOARD_ACCESS.lock().unwrap(); | ||||
|             match board.delete_file(&filename) { | ||||
|             match board.esp.delete_file(&filename) { | ||||
|                 Ok(_) => { | ||||
|                     let info = format!("Deleted file {copy}"); | ||||
|                     cors_response(request, 200, &info)?; | ||||
|   | ||||
 Submodule website/themes/blowfish updated: 26d1205439...1d21656d5e
									
								
							
		Reference in New Issue
	
	Block a user