diff --git a/rust/Cargo.toml b/rust/Cargo.toml index dc400e4..934993a 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -75,6 +75,7 @@ anyhow = { version = "1.0.75", features = ["std", "backtrace"] } schemars = "0.8.16" heapless = { version = "0.7", features = ["serde"] } serde_json = "1.0.108" +strum = { version = "0.25.0", features = ["derive"] } #?bq34z100 required [build-dependencies] diff --git a/rust/partitions.csv b/rust/partitions.csv index 2397298..3b2aae0 100644 --- a/rust/partitions.csv +++ b/rust/partitions.csv @@ -1,6 +1,6 @@ nvs, data, nvs, , 16k, otadata, data, ota, , 8k, phy_init, data, phy, , 4k, -factory, app, ota_0, , 1792K, +ota_0, app, ota_0, , 1792K, ota_1, app, ota_1, , 1792K, storage, data, spiffs, , 400K, \ No newline at end of file diff --git a/rust/src/config.rs b/rust/src/config.rs index cf4ac7d..b862051 100644 --- a/rust/src/config.rs +++ b/rust/src/config.rs @@ -6,28 +6,47 @@ use crate::PLANT_COUNT; #[derive(Serialize, Deserialize)] +#[derive(Debug)] pub struct Config { tank_sensor_enabled: bool, tank_full_ml: u32, tank_warn_percent: u8, - plantcount: u16, - night_lamp_hour_start: u8, night_lamp_hour_end: u8, - night_lamp_only_when_dark: u8, + night_lamp_only_when_dark: bool, plants: [Plant;PLANT_COUNT] } -#[derive(Serialize, Deserialize)] +impl Default for Config { + fn default() -> Self { + Self { tank_sensor_enabled: true, + tank_full_ml: 5000, + tank_warn_percent: 50, + night_lamp_hour_start: 21, + night_lamp_hour_end: 2, + night_lamp_only_when_dark: true, + plants: [Plant::default();PLANT_COUNT] + } + } +} + +#[derive(Serialize, Deserialize, Copy, Clone)] +#[derive(Debug)] pub struct Plant{ target_moisture: u8, pump_time_s: u16, pump_cooldown_min: u16, - pump_hour_start: heapless::String<5>, - pump_hour_end: heapless::String<5> + pump_hour_start: u8, + pump_hour_end: u8 } +impl Default for Plant { + fn default() -> Self { + Self { target_moisture: 40, pump_time_s: 60, pump_cooldown_min: 60, pump_hour_start: 8, pump_hour_end: 20 } + } +} + #[derive(Serialize, Deserialize)] #[derive(Debug)] pub struct WifiConfig { diff --git a/rust/src/main.rs b/rust/src/main.rs index 9d8ae24..6aaa15f 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -1,4 +1,4 @@ -use std::{sync::{Arc, Mutex}, env}; +use std::{sync::{Arc, Mutex, atomic::AtomicBool}, env}; use chrono::{Datelike, NaiveDateTime, Timelike}; @@ -9,7 +9,7 @@ use esp_idf_sys::{esp_restart, vTaskDelay}; use plant_hal::{CreatePlantHal, PlantCtrlBoard, PlantCtrlBoardInteraction, PlantHal, PLANT_COUNT}; -use crate::{config::{Config, WifiConfig}, webserver::webserver::httpd_initial}; +use crate::{config::{Config, WifiConfig}, webserver::webserver::{httpd_initial, httpd}}; mod config; pub mod plant_hal; mod webserver { @@ -27,32 +27,47 @@ enum OnlineMode { enum WaitType{ InitialConfig, - FlashError + FlashError, + NormalConfig } -fn wait_infinity(board_access: Arc>>, wait_type:WaitType) -> !{ +fn wait_infinity(wait_type:WaitType, reboot_now:Arc) -> !{ let delay = match wait_type { WaitType::InitialConfig => 250_u32, WaitType::FlashError => 100_u32, + WaitType::NormalConfig => 500_u32 }; - board_access.lock().unwrap().light(true).unwrap(); + let led_count = match wait_type { + WaitType::InitialConfig => 8, + WaitType::FlashError => 8, + WaitType::NormalConfig => 4 + }; + BOARD_ACCESS.lock().unwrap().light(true).unwrap(); loop { unsafe { //do not trigger watchdog for i in 0..8 { - board_access.lock().unwrap().fault(i, true); + BOARD_ACCESS.lock().unwrap().fault(i, i = Lazy::new(|| { + PlantHal::create()?; +}); + fn main() -> 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 @@ -67,8 +82,7 @@ fn main() -> Result<()> { println!("Version useing git has {}", git_hash); println!("Board hal init"); - let board_access = PlantHal::create()?; - let mut board = board_access.lock().unwrap(); + let mut board = BOARD_ACCESS.lock().unwrap(); println!("Mounting filesystem"); board.mountFileSystem()?; let free_space = board.fileSystemSize()?; @@ -103,16 +117,14 @@ fn main() -> Result<()> { if board.is_config_reset() { println!("Reset config is still pressed, deleting configs and reboot"); match board.remove_configs() { - Ok(_) => { - println!("Removed config files, restarting"); - unsafe { - esp_restart(); - } + Ok(case) => { + println!("Succeeded in deleting config {}", case); } Err(err) => { println!("Could not remove config files, system borked {}", err); //terminate main app and freeze - wait_infinity( board_access.clone(), WaitType::FlashError); + + wait_infinity(WaitType::FlashError, Arc::new(AtomicBool::new(false))); } } } @@ -130,14 +142,13 @@ fn main() -> Result<()> { board.wifi_ap().unwrap(); //config upload will trigger reboot! drop(board); - let _webserver = httpd_initial(board_access.clone()); - wait_infinity(board_access.clone(), WaitType::InitialConfig); + let reboot_now = Arc::new(AtomicBool::new(false)); + let _webserver = httpd_initial(BOARD_ACCESS.clone(), reboot_now.clone()); + wait_infinity(WaitType::InitialConfig, reboot_now.clone()); }, }; - // let proceed = config.unwrap(); - //check if we have a config file // if not found or parsing error -> error very fast blink general fault //if this happens after a firmeware upgrade (check image state), mark as invalid @@ -203,6 +214,21 @@ fn main() -> Result<()> { println!("Running logic at europe/berlin {}", europe_time); } + let config:Config; + match (board.get_config()){ + Ok(valid) => { + config = valid; + }, + Err(err) => { + println!("Missing normal config, entering config mode {}", err); + //config upload will trigger reboot! + drop(board); + let reboot_now = Arc::new(AtomicBool::new(false)); + let _webserver = httpd(BOARD_ACCESS.clone(),reboot_now.clone()); + wait_infinity(BOARD_ACCESS.clone(), WaitType::NormalConfig, reboot_now.clone()); + }, + } + if online_mode == OnlineMode::SnTp { //mqtt here } diff --git a/rust/src/plant_hal.rs b/rust/src/plant_hal.rs index 73dfa13..a6d84d7 100644 --- a/rust/src/plant_hal.rs +++ b/rust/src/plant_hal.rs @@ -11,6 +11,7 @@ use plant_ctrl2::sipo::ShiftRegister40; use anyhow::anyhow; use anyhow::{bail, Ok, Result}; +use strum::EnumString; use std::ffi::CString; use std::fs::File; use std::io::Read; @@ -23,7 +24,6 @@ use chrono::{DateTime, NaiveDateTime, Utc}; use ds18b20::Ds18b20; use embedded_hal::digital::v2::OutputPin; -use esp_idf_hal::adc::config::Config; use esp_idf_hal::adc::{attenuation, AdcChannelDriver, AdcDriver}; use esp_idf_hal::delay::Delay; use esp_idf_hal::gpio::{AnyInputPin, Gpio39, Gpio4, Level, PinDriver}; @@ -37,7 +37,7 @@ use esp_idf_svc::systime::EspSystemTime; use esp_idf_sys::{EspError, vTaskDelay}; use one_wire_bus::OneWire; -use crate::config::{self, WifiConfig}; +use crate::config::{self, WifiConfig, Config}; pub const PLANT_COUNT: usize = 8; const PINS_PER_PLANT: usize = 5; @@ -80,6 +80,14 @@ pub struct FileSystemSizeInfo { pub free_size: usize, } + +#[derive(strum::Display)] +pub enum ClearConfigType { + WifiConfig, + Config, + None +} + #[derive(Debug)] pub enum Sensor { A, @@ -121,8 +129,9 @@ pub trait PlantCtrlBoardInteraction { //config fn is_config_reset(&mut self) -> bool; - fn remove_configs(&mut self) -> Result<()>; + fn remove_configs(&mut self) -> Result; fn get_config(&mut self) -> Result; + fn set_config(&mut self, wifi: &Config) -> Result<()>; fn get_wifi(&mut self) -> Result; fn set_wifi(&mut self, wifi: &WifiConfig) -> Result<()>; fn wifi_ap(&mut self) -> Result<()>; @@ -137,7 +146,7 @@ pub trait CreatePlantHal<'a> { pub struct PlantHal {} impl CreatePlantHal<'_> for PlantHal { - fn create() -> Result>>> { + fn create() -> Result>> { let peripherals = Peripherals::take()?; let mut clock = PinDriver::output(peripherals.pins.gpio21)?; @@ -213,7 +222,7 @@ impl CreatePlantHal<'_> for PlantHal { let last_watering_timestamp = Mutex::new(unsafe { LAST_WATERING_TIMESTAMP }); let consecutive_watering_plant = Mutex::new(unsafe { CONSECUTIVE_WATERING_PLANT }); let low_voltage_detected = Mutex::new(unsafe { LOW_VOLTAGE_DETECTED }); - let tank_driver = AdcDriver::new(peripherals.adc1, &Config::new())?; + let tank_driver = AdcDriver::new(peripherals.adc1, &esp_idf_hal::adc::config::Config::new())?; let tank_channel: AdcChannelDriver<'_, { attenuation::DB_11 }, Gpio39> = AdcChannelDriver::new(peripherals.pins.gpio39)?; let solar_is_day = PinDriver::input(peripherals.pins.gpio25)?; @@ -228,7 +237,7 @@ impl CreatePlantHal<'_> for PlantHal { println!("After stuff"); - let rv = Arc::new(Mutex::new(PlantCtrlBoard { + let rv = Mutex::new(PlantCtrlBoard { shift_register : shift_register, last_watering_timestamp: last_watering_timestamp, consecutive_watering_plant: consecutive_watering_plant, @@ -244,7 +253,7 @@ impl CreatePlantHal<'_> for PlantHal { one_wire_bus: one_wire_bus, signal_counter: counter_unit1, wifi_driver: wifi_driver, - })); + }); return Ok(rv); } } @@ -534,19 +543,26 @@ impl PlantCtrlBoardInteraction for PlantCtrlBoard<'_> { return self.boot_button.get_level() == Level::Low; } - fn remove_configs(&mut self) -> Result<()> { - let wifi_config = Path::new(WIFI_CONFIG_FILE); - if wifi_config.exists() { - println!("Removing wifi config"); - std::fs::remove_file(wifi_config)?; - } + + fn remove_configs(&mut self) -> Result { let config = Path::new(CONFIG_FILE); if config.exists() { println!("Removing config"); std::fs::remove_file(config)?; + return Ok(ClearConfigType::Config); } - Ok(()) + + let wifi_config = Path::new(WIFI_CONFIG_FILE); + if wifi_config.exists() { + println!("Removing wifi config"); + std::fs::remove_file(wifi_config)?; + Ok(ClearConfigType::WifiConfig); + } + + Ok((ClearConfigType::None)); + + } fn get_wifi(&mut self) -> Result { @@ -563,12 +579,16 @@ impl PlantCtrlBoardInteraction for PlantCtrlBoard<'_> { } fn get_config(&mut self) -> Result { - let mut cfg = File::open(CONFIG_FILE)?; - let mut data: [u8; 512] = [0; 512]; - let read = cfg.read(&mut data)?; - println!("Read file {}", from_utf8(&data[0..read])?); + let cfg = File::open(CONFIG_FILE)?; + let config: Config = serde_json::from_reader(cfg)?; + return Ok(config); + } - bail!("todo") + fn set_config(&mut self, config: &Config) -> Result<()> { + let mut cfg = File::create(CONFIG_FILE)?; + serde_json::to_writer(&mut cfg, &config)?; + println!("Wrote config config {:?}", config); + return Ok(()); } fn wifi_scan(&mut self) -> Result> { diff --git a/rust/src/webserver/config.html b/rust/src/webserver/config.html index 0c27fe7..b360708 100644 --- a/rust/src/webserver/config.html +++ b/rust/src/webserver/config.html @@ -32,9 +32,11 @@

Light:

Start - + Stop - +
diff --git a/rust/src/webserver/favicon.ico b/rust/src/webserver/favicon.ico new file mode 100644 index 0000000..685e925 Binary files /dev/null and b/rust/src/webserver/favicon.ico differ diff --git a/rust/src/webserver/initial_config.html b/rust/src/webserver/initial_config.html index 7f95dea..e0bb009 100644 --- a/rust/src/webserver/initial_config.html +++ b/rust/src/webserver/initial_config.html @@ -23,7 +23,7 @@