Part 1 of V3 extraction in preperation of v4 and v3 software merge #16
| @@ -1,8 +1,4 @@ | ||||
| use std::{ | ||||
|     fmt::Display, | ||||
|     sync::{atomic::AtomicBool, Arc, Mutex}, | ||||
| }; | ||||
| use std::sync::MutexGuard; | ||||
| use crate::{config::PlantControllerConfig, webserver::webserver::httpd}; | ||||
| use anyhow::bail; | ||||
| use chrono::{DateTime, Datelike, Timelike, Utc}; | ||||
| use chrono_tz::Tz; | ||||
| @@ -18,19 +14,24 @@ use esp_idf_sys::{ | ||||
| use esp_ota::{mark_app_valid, rollback_and_reboot}; | ||||
| use log::{log, LogMessage}; | ||||
| use once_cell::sync::Lazy; | ||||
| use plant_hal::{PlantCtrlBoard, PlantHal, PLANT_COUNT}; | ||||
| use plant_hal::{PlantHal, PLANT_COUNT}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use crate::{config::PlantControllerConfig, webserver::webserver::httpd}; | ||||
| use std::sync::MutexGuard; | ||||
| use std::{ | ||||
|     fmt::Display, | ||||
|     sync::{atomic::AtomicBool, Arc, Mutex}, | ||||
| }; | ||||
| mod config; | ||||
| mod log; | ||||
| pub mod plant_hal; | ||||
| mod plant_state; | ||||
| mod tank; | ||||
|  | ||||
| use crate::plant_hal::{BatteryInteraction, BoardInteraction, HAL}; | ||||
| use plant_state::PlantState; | ||||
| use tank::*; | ||||
|  | ||||
| pub static BOARD_ACCESS: Lazy<Mutex<PlantCtrlBoard>> = Lazy::new(|| PlantHal::create().unwrap()); | ||||
| pub static BOARD_ACCESS: Lazy<Mutex<HAL>> = Lazy::new(|| PlantHal::create_v3().unwrap()); | ||||
| pub static STAY_ALIVE: Lazy<AtomicBool> = Lazy::new(|| AtomicBool::new(false)); | ||||
|  | ||||
| mod webserver { | ||||
| @@ -150,7 +151,7 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|     }; | ||||
|     log(LogMessage::PartitionState, 0, 0, "", ota_state_string); | ||||
|  | ||||
|     let mut board: std::sync::MutexGuard<'_, PlantCtrlBoard<'_>> = BOARD_ACCESS.lock().unwrap(); | ||||
|     let mut board = BOARD_ACCESS.lock().unwrap(); | ||||
|     board.general_fault(false); | ||||
|  | ||||
|     log(LogMessage::MountingFilesystem, 0, 0, "", ""); | ||||
| @@ -400,7 +401,7 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|     } | ||||
|  | ||||
|     let is_day = board.is_day(); | ||||
|     let state_of_charge = board.state_charge_percent().unwrap_or(0); | ||||
|     let state_of_charge = board.battery_monitor.state_charge_percent().unwrap_or(0); | ||||
|  | ||||
|     let mut light_state = LightState { | ||||
|         enabled: config.night_lamp.enabled, | ||||
| @@ -484,7 +485,7 @@ fn safe_main() -> anyhow::Result<()> { | ||||
|     board.deep_sleep(1000 * 1000 * 60 * deep_sleep_duration_minutes as u64); | ||||
| } | ||||
|  | ||||
| fn obtain_tank_temperature(board: &mut MutexGuard<PlantCtrlBoard>) -> anyhow::Result<f32> { | ||||
| fn obtain_tank_temperature(board: &mut MutexGuard<HAL>) -> anyhow::Result<f32> { | ||||
|     //multisample should be moved to water_temperature_c | ||||
|     let mut attempt = 1; | ||||
|     let water_temp: Result<f32, anyhow::Error> = loop { | ||||
| @@ -506,7 +507,7 @@ fn obtain_tank_temperature(board: &mut MutexGuard<PlantCtrlBoard>) -> anyhow::Re | ||||
|     water_temp | ||||
| } | ||||
|  | ||||
| fn publish_tank_state(board: &mut MutexGuard<PlantCtrlBoard>, config: &PlantControllerConfig, tank_state: &TankState, water_temp: &anyhow::Result<f32>) { | ||||
| 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)) { | ||||
|         Ok(state) => { | ||||
|             let _ = board.mqtt_publish(&config, "/water", state.as_bytes()); | ||||
| @@ -517,7 +518,7 @@ fn publish_tank_state(board: &mut MutexGuard<PlantCtrlBoard>, config: &PlantCont | ||||
|     }; | ||||
| } | ||||
|  | ||||
| fn publish_plant_states(board: &mut MutexGuard<PlantCtrlBoard>, config: &PlantControllerConfig, timezone_time: &DateTime<Tz>, plantstate: &[PlantState; 8]) { | ||||
| 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() { | ||||
|         match serde_json::to_string(&plant_state.to_mqtt_info(plant_conf, &timezone_time)) { | ||||
|             Ok(state) => { | ||||
| @@ -533,7 +534,7 @@ fn publish_plant_states(board: &mut MutexGuard<PlantCtrlBoard>, config: &PlantCo | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn publish_firmware_info(version: VersionInfo, address: u32, ota_state_string: &str, board: &mut MutexGuard<PlantCtrlBoard>, config: &PlantControllerConfig, ip_address: &String, timezone_time: DateTime<Tz>) { | ||||
| 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()); | ||||
|     let _ = board.mqtt_publish( | ||||
| @@ -555,7 +556,7 @@ fn publish_firmware_info(version: VersionInfo, address: u32, ota_state_string: & | ||||
|     let _ = board.mqtt_publish(&config, "/state", "online".as_bytes()); | ||||
| } | ||||
|  | ||||
| fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard<PlantCtrlBoard>, config: &PlantControllerConfig) -> NetworkMode{ | ||||
| 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(), | ||||
| @@ -603,7 +604,7 @@ fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard<PlantCtrlBoard>, config: &P | ||||
| } | ||||
|  | ||||
| //TODO clean this up? better state | ||||
| fn pump_info(board: &mut MutexGuard<PlantCtrlBoard>, config: &PlantControllerConfig, plant_id: usize, pump_active: bool, pump_ineffective: bool)  { | ||||
| fn pump_info(board: &mut MutexGuard<HAL>, config: &PlantControllerConfig, plant_id: usize, pump_active: bool, pump_ineffective: bool)  { | ||||
|     let pump_info = PumpInfo { | ||||
|         enabled: pump_active, | ||||
|         pump_ineffective | ||||
| @@ -622,18 +623,11 @@ fn pump_info(board: &mut MutexGuard<PlantCtrlBoard>, config: &PlantControllerCon | ||||
| } | ||||
|  | ||||
| fn publish_battery_state( | ||||
|     board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>, | ||||
|     board: &mut MutexGuard<'_, HAL<'_>>, | ||||
|     config: &PlantControllerConfig, | ||||
| ) { | ||||
|     let bat = board.get_battery_state(); | ||||
|     match serde_json::to_string(&bat) { | ||||
|         Ok(state) => { | ||||
|             let _ = board.mqtt_publish(config, "/battery", state.as_bytes()); | ||||
|         } | ||||
|         Err(err) => { | ||||
|             println!("Error publishing battery_state {}", err); | ||||
|         } | ||||
|     }; | ||||
|     let state = board.get_battery_state(); | ||||
|     let _ = board.mqtt_publish(config, "/battery", state.as_bytes()); | ||||
| } | ||||
|  | ||||
| fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! { | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -3,6 +3,7 @@ use chrono_tz::Tz; | ||||
| use serde::{Deserialize, Serialize}; | ||||
|  | ||||
| use crate::{config::PlantConfig, in_time_range, plant_hal}; | ||||
| use crate::plant_hal::BoardInteraction; | ||||
|  | ||||
| const MOIST_SENSOR_MAX_FREQUENCY: f32 = 7500.; // 60kHz (500Hz margin) | ||||
| const MOIST_SENSOR_MIN_FREQUENCY: f32 = 150.; // this is really, really dry, think like cactus levels | ||||
| @@ -113,7 +114,7 @@ fn map_range_moisture( | ||||
| impl PlantState { | ||||
|     pub fn read_hardware_state( | ||||
|         plant_id: usize, | ||||
|         board: &mut plant_hal::PlantCtrlBoard, | ||||
|         board: &mut plant_hal::HAL, | ||||
|         config: &PlantConfig, | ||||
|     ) -> Self { | ||||
|         let sensor_a = if config.sensor_a { | ||||
|   | ||||
| @@ -1,9 +1,7 @@ | ||||
| use serde::Serialize; | ||||
|  | ||||
| use crate::{ | ||||
|     config::{PlantControllerConfig, TankConfig}, | ||||
|     plant_hal::PlantCtrlBoard, | ||||
| }; | ||||
| 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; | ||||
| @@ -158,7 +156,7 @@ impl TankState { | ||||
| } | ||||
|  | ||||
| pub fn determine_tank_state( | ||||
|     board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>, | ||||
|     board: &mut std::sync::MutexGuard<'_, HAL<'_>>, | ||||
|     config: &PlantControllerConfig, | ||||
| ) -> TankState { | ||||
|     if config.tank.tank_sensor_enabled { | ||||
|   | ||||
| @@ -8,7 +8,6 @@ use anyhow::bail; | ||||
| use chrono::DateTime; | ||||
| use core::result::Result::Ok; | ||||
| use embedded_svc::http::Method; | ||||
| use esp_idf_hal::delay::Delay; | ||||
| use esp_idf_svc::http::server::{Configuration, EspHttpConnection, EspHttpServer, Request}; | ||||
| use esp_idf_sys::{settimeofday, timeval, vTaskDelay}; | ||||
| use esp_ota::OtaUpdate; | ||||
| @@ -21,6 +20,7 @@ use std::{ | ||||
| use url::Url; | ||||
|  | ||||
| use crate::config::PlantControllerConfig; | ||||
| use crate::plant_hal::BoardInteraction; | ||||
| use crate::plant_state::MoistureSensorState; | ||||
|  | ||||
| #[derive(Serialize, Debug)] | ||||
| @@ -351,45 +351,6 @@ fn ota( | ||||
|     anyhow::Ok(None) | ||||
| } | ||||
|  | ||||
| fn flash_bq(filename: &str, dryrun: bool) -> anyhow::Result<()> { | ||||
|     let mut board = BOARD_ACCESS.lock().unwrap(); | ||||
|  | ||||
|     let mut toggle = true; | ||||
|     let delay = Delay::new(1); | ||||
|  | ||||
|     let file_handle = board.get_file_handle(filename, false)?; | ||||
|  | ||||
|     let mut reader = std::io::BufRead::lines(std::io::BufReader::with_capacity(512, file_handle)); | ||||
|     let mut line = 0; | ||||
|     loop { | ||||
|         board.general_fault(toggle); | ||||
|         toggle = !toggle; | ||||
|  | ||||
|         delay.delay_us(2); | ||||
|         line += 1; | ||||
|         match reader.next() { | ||||
|             Some(next) => { | ||||
|                 let input = next?; | ||||
|                 println!("flashing bq34z100 dryrun:{dryrun} line {line} payload: {input}"); | ||||
|                 match board.flash_bq34_z100(&input, dryrun) { | ||||
|                     Ok(_) => { | ||||
|                         println!("ok") | ||||
|                     } | ||||
|                     Err(err) => { | ||||
|                         bail!( | ||||
|                             "Error flashing bq34z100 in dryrun: {dryrun} line: {line} error: {err}" | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             None => break, | ||||
|         } | ||||
|     } | ||||
|     println!("Finished flashing file {line} lines processed"); | ||||
|     board.general_fault(false); | ||||
|     anyhow::Ok(()) | ||||
| } | ||||
|  | ||||
| fn query_param(uri: &str, param_name: &str) -> Option<std::string::String> { | ||||
|     println!("{uri} get {param_name}"); | ||||
|     let parsed = Url::parse(&format!("http://127.0.0.1/{uri}")).unwrap(); | ||||
| @@ -630,35 +591,6 @@ pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> { | ||||
|             cors_response(request, 200, "") | ||||
|         }) | ||||
|         .unwrap(); | ||||
|  | ||||
|     server | ||||
|         .fn_handler("/flashbattery", Method::Post, move |request| { | ||||
|             let filename = query_param(request.uri(),"filename").unwrap(); | ||||
|             let dryrun = true; | ||||
|             match flash_bq(&filename, false) { | ||||
|                 Ok(_) => { | ||||
|                     if !dryrun { | ||||
|                         match  flash_bq(&filename, true) { | ||||
|                             Ok(_) => { | ||||
|                                 cors_response(request, 200, "Sucessfully flashed bq34z100")?; | ||||
|                             }, | ||||
|                             Err(err) => { | ||||
|                                 let info = format!("Could not flash bq34z100, could be bricked now! {filename} {err:?}"); | ||||
|                                 cors_response(request, 500, &info)?; | ||||
|                             }, | ||||
|                         } | ||||
|                     } else { | ||||
|                         cors_response(request, 200, "Sucessfully processed bq34z100")?; | ||||
|                     } | ||||
|                 }, | ||||
|                 Err(err) => { | ||||
|                     let info = format!("Could not process firmware file for, bq34z100, refusing to flash! {filename} {err:?}"); | ||||
|                     cors_response(request, 500, &info)?; | ||||
|                 }, | ||||
|             }; | ||||
|             anyhow::Ok(()) | ||||
|         }) | ||||
|         .unwrap(); | ||||
|     unsafe { vTaskDelay(1) }; | ||||
|     server | ||||
|         .fn_handler("/", Method::Get, move |request| { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user