From 4c02b99ea70342590a13d2d5d08fe0a0f8c601e2 Mon Sep 17 00:00:00 2001 From: Empire Date: Wed, 27 Dec 2023 17:33:11 +0100 Subject: [PATCH] webserver improvements --- rust/Cargo.toml | 1 + rust/partitions.csv | 2 +- rust/src/config.rs | 31 ++- rust/src/main.rs | 68 ++++-- rust/src/plant_hal.rs | 58 +++-- rust/src/webserver/config.html | 6 +- rust/src/webserver/favicon.ico | Bin 0 -> 15406 bytes rust/src/webserver/initial_config.html | 2 +- rust/src/webserver/webserver.rs | 73 +++++- rust/src_webpack/src/form.ts | 320 ++++++++++++++----------- rust/src_webpack/src/wifi.ts | 2 +- 11 files changed, 361 insertions(+), 202 deletions(-) create mode 100644 rust/src/webserver/favicon.ico 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 0000000000000000000000000000000000000000..685e92557afdede86912e38417c729aef201967e GIT binary patch literal 15406 zcmeHN2apv-5M2-zBUoyg^~{I~%N#+g45g^0mX-l610toCskBtYP$u965haHspW*?E zASfVF4g>|sh=SxS>Er^!J^TG(*xC8>clZCh<2Yri{@>l1?w+31-P7HkSIyhct6kfZ zXO`ErhUZ=8d0tkQ`mS??=jBLUy?VRa&+@$dqdl*VzzB+9sCs!g3tD<_$*)9yNb^UW zOmTHzs^1PL0e!CmTJ5Lnlb;_S26Qc^@r|3kmdoR7l4RQtTofr)Pe#hZaq&-t*UUsUZsf9BVj_$39Zonv5qPCa1h zSRXdGfoh-sCD8r^VAvzT;JX10zihl}?^?&l(QTyH`XCu!A+Ygx;ENl9x&49d+kn4U z13fQNv`FEn)eXfxCdrM%h@nLlTtzZa{jG5H|Ej4#_jAMhHfgmv80d33(7(R$ zkrfv&le4F3m*os5aGgRSjY`UyEcrE%ALUc3f_J>?viFk?qloxp&bfQ~1}8)v&CwRBO> z6_euOnf&E71U4@Y(&5+YpMin5htunFMi7sB(WD*qKHwHn6FY-6?5`PJQtNW+aJyt5 z5TA$T6K!mz`#2vvcld48b*`~|dbAIt-?%0&SXUkG2eZuV4p8nBmb#Ve$-+6xxzIsZ z^c;1wS%~NA%T;IVDB24q9POip-ze(TX=a;#Uo+k89q4g^8&1QU&y8Xan5F%3$++ZU z$q#NjfwX4!Fe!-MZjemF?9+UopBKdC88qAVJ9T<0-97w?AdT;zRy0OF1N=Tzt>K@e z0NRB%{h<#9afze*jQi|-dXNU^qr-{G_D2G$RM|JQ@kqaQO`u?~cB76W#=7dtMxb;n~ynG3Ec1R&H6C z>Ko$MQ>^ij6&^>#Ejs@89oH^SL*l)hOQoyX|cb6+fT+cxS6v zd077R^KJv^ulZ{Muxg6LDLxUqu#9&A*BmhZLf;&1;z{qx?_G&;JukkQ>ofO}(#Gb! z(k@$c#Ah2V*-IMxe9BQig!g#ky=Z;Dr1pOIy*--+Fe75ZT zlXv=3w#aowA?k^Wl2iWlq0jpOxK5V*m8CG1WKyUyQ5TIO%+^nc=2Q zeiw}Z80Q%F2r&8u@oS1tn0_{0wil?g9u{+_e}?kLRetG^-dS>ut7wypviM@Y1QI@Z z?W>U|{caqG>HlWD_|s!T=1}nHlXS%^X3DwNetQ>haq5aw@K2wd3!m%#-BZ3#iSmm6 zCl{=)8_`(#r|g-cKg7%d(D3OzH!ZfQs_)LHrRIryf@9GBMy^R%p9608{Y_?X%r|IT zI~+HRF54E5G%5P;QSl6}1N9tb(S+vVJaj!<Jg1AS zu}jGorO!#9e5{xZP(M=F)4$0l?F`Duaj&}9s{QdYH`8p&l7})rb^J1u@o+s|g|S-N zrn7pAj4E|O_;vJCesN>a2g-d+J8kDbJ{;c)R+gWA#qqYSrE;Evy{PyH^+*5fl+8G| zAFxtvR4p59c*^@9UJC9h#+z(8M7_({3iqy_?@p(vSX?^xo5tnyw?Q^HD!!y)I1b(s z=mV#Jn(-x<-Y?#~OK(IZ>{dug$OhO`YPh6~pu&^_y$mI0idj zM0?@aZfoV|7WDn zKY2^&Jc)IgWp?jqT9l)%!kj?Yn0tx+vuL>FAFc_m2jgs9HyuND;fB?5583$#t7SiC z%h?UXqwVV|%O|Dw2~Z#QyIS4z@ z^a;eV0oiYP`98RI1uJ}Rc&4Llkm((Lc=L_1-%R&H*gQwI4SB%VXWZM}>V~f=?^3Qp znVzXWisCWRUd_F3tz~ci^pVI3(Ua6Eu6$lipL@%^GnwOH-hhUyh-c!SAot995?k+m zsqEFmr9T-H`{EeJH&Z!6ZDmin`e#lEJ;l+5XXUna-2E4}Ux}8!U{^sJ7htz?{A!cVr)B zQs^sKI#%VIa$TePEBAIew>-bhhoeuFc5vLXR_~u> va2dZ=xe!$;_d(A^(JtO=?aGCyN;we4I)7t#4oVfxpCSMI%Dx%MbO!zfQj?u* literal 0 HcmV?d00001 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 @@