diff --git a/rust/.idea/dictionaries/project.xml b/rust/.idea/dictionaries/project.xml new file mode 100644 index 0000000..e169be7 --- /dev/null +++ b/rust/.idea/dictionaries/project.xml @@ -0,0 +1,7 @@ + + + + sntp + + + \ No newline at end of file diff --git a/rust/src/main.rs b/rust/src/main.rs index f811190..29b92ef 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -65,7 +65,7 @@ struct LightState { active: bool, /// led should not be on at this time of day out_of_work_hour: bool, - /// battery is low so do not use led + /// the battery is low so do not use led battery_low: bool, /// the sun is up is_day: bool, @@ -79,8 +79,8 @@ enum SensorError { OpenCircuit { hz: f32, min: f32 }, } -fn safe_main() -> anyhow::Result<()> { - // It is necessary to call this function once. Otherwise some patches to the runtime +fn safe_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 esp_idf_svc::sys::link_patches(); @@ -100,7 +100,7 @@ fn safe_main() -> anyhow::Result<()> { let version = get_version(); println!( - "Version useing git has {} build on {}", + "Version using git has {} build on {}", version.git_hash, version.build_time ); @@ -129,16 +129,16 @@ fn safe_main() -> anyhow::Result<()> { &format!("unknown {ota_state}") } }; - log(log::LogMessage::PartitionState, 0, 0, "", ota_state_string); + log(LogMessage::PartitionState, 0, 0, "", ota_state_string); let mut board: std::sync::MutexGuard<'_, PlantCtrlBoard<'_>> = BOARD_ACCESS.lock().unwrap(); board.general_fault(false); - log(log::LogMessage::MountingFilesystem, 0, 0, "", ""); + log(LogMessage::MountingFilesystem, 0, 0, "", ""); board.mount_file_system()?; let free_space = board.file_system_size()?; log( - log::LogMessage::FilesystemMount, + LogMessage::FilesystemMount, free_space.free_size as u32, free_space.total_size as u32, &free_space.used_size.to_string(), @@ -157,17 +157,17 @@ fn safe_main() -> anyhow::Result<()> { }) .unwrap(); - //check if we know the time current > 2020 (plausibility check, this code is newer than 2020) + //check if we know the time current > 2020 (plausibility checks, this code is newer than 2020) if cur.year() < 2020 { to_config = true; - log(log::LogMessage::YearInplausibleForceConfig, 0, 0, "", ""); + log(LogMessage::YearInplausibleForceConfig, 0, 0, "", ""); } println!("cur is {}", cur); board.update_charge_indicator(); if board.get_restart_to_conf() { - log(log::LogMessage::ConfigModeSoftwareOverride, 0, 0, "", ""); + log(LogMessage::ConfigModeSoftwareOverride, 0, 0, "", ""); for _i in 0..2 { board.general_fault(true); Delay::new_default().delay_ms(100); @@ -177,9 +177,9 @@ fn safe_main() -> anyhow::Result<()> { to_config = true; board.general_fault(true); board.set_restart_to_conf(false); - } else if board.is_mode_override() { + } else if board.mode_override_pressed() { board.general_fault(true); - log(log::LogMessage::ConfigModeButtonOverride, 0, 0, "", ""); + log(LogMessage::ConfigModeButtonOverride, 0, 0, "", ""); for _i in 0..5 { board.general_fault(true); Delay::new_default().delay_ms(100); @@ -187,7 +187,7 @@ fn safe_main() -> anyhow::Result<()> { Delay::new_default().delay_ms(100); } - if board.is_mode_override() { + if board.mode_override_pressed() { board.general_fault(true); to_config = true; } else { @@ -195,27 +195,26 @@ fn safe_main() -> anyhow::Result<()> { } } - let config: PlantControllerConfig; - match board.get_config() { + let config: PlantControllerConfig = match board.get_config() { Ok(valid) => { - config = valid; + valid } Err(err) => { log( - log::LogMessage::ConfigModeMissingConfig, + LogMessage::ConfigModeMissingConfig, 0, 0, "", &err.to_string(), ); //config upload will trigger reboot! - let _ = board.wifi_ap(Option::None); + let _ = board.wifi_ap(None); drop(board); let reboot_now = Arc::new(AtomicBool::new(false)); let _webserver = httpd(reboot_now.clone()); wait_infinity(WaitType::MissingConfig, reboot_now.clone()); } - } + }; let mut wifi = false; let mut mqtt = false; @@ -273,7 +272,7 @@ fn safe_main() -> anyhow::Result<()> { } Err(err) => println!( "Could not start config override ap mode due to {}", - err.to_string() + err ), } } @@ -321,7 +320,7 @@ fn safe_main() -> anyhow::Result<()> { } log( - log::LogMessage::StartupInfo, + LogMessage::StartupInfo, wifi as u32, sntp as u32, &mqtt.to_string(), @@ -329,7 +328,7 @@ fn safe_main() -> anyhow::Result<()> { ); if to_config { - //check if client or ap mode and init wifi + //check if client or ap mode and init Wi-Fi println!("executing config mode override"); //config upload will trigger reboot! drop(board); @@ -337,7 +336,7 @@ fn safe_main() -> anyhow::Result<()> { let _webserver = httpd(reboot_now.clone()); wait_infinity(WaitType::ConfigButton, reboot_now.clone()); } else { - log(log::LogMessage::NormalRun, 0, 0, "", ""); + log(LogMessage::NormalRun, 0, 0, "", ""); } let dry_run = false; @@ -367,10 +366,10 @@ fn safe_main() -> anyhow::Result<()> { 0, 0, "", - &format!("{}", &err.to_string()), + &err.to_string() ), } - // disabled can not trigger this because of wrapping if is_enabled + // 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) { log(LogMessage::TankWaterLevelLow, 0, 0, "", ""); @@ -432,15 +431,15 @@ fn safe_main() -> anyhow::Result<()> { let pump_required = plantstate .iter() .zip(&config.plants) - .any(|(it, conf)| it.needs_to_be_watered(&conf, &timezone_time)) + .any(|(it, conf)| it.needs_to_be_watered(conf, &timezone_time)) && !water_frozen; if pump_required { - log(log::LogMessage::EnableMain, dry_run as u32, 0, "", ""); + log(LogMessage::EnableMain, dry_run as u32, 0, "", ""); if !dry_run { board.any_pump(true)?; // what does this do? Does it need to be reset? } for (plant_id, (state, plant_config)) in plantstate.iter().zip(&config.plants).enumerate() { - if state.needs_to_be_watered(&plant_config, &timezone_time) { + 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); //TODO(judge) where to put this? @@ -456,11 +455,11 @@ fn safe_main() -> anyhow::Result<()> { // board.fault(plant, true); //} log( - log::LogMessage::PumpPlant, + LogMessage::PumpPlant, (plant_id + 1) as u32, plant_config.pump_time_s as u32, &dry_run.to_string(), - "", + "", ); board.store_last_pump_time(plant_id, cur); board.last_pump_time(plant_id); @@ -470,7 +469,7 @@ fn safe_main() -> anyhow::Result<()> { Delay::new_default().delay_ms(1000 * plant_config.pump_time_s as u32); board.pump(plant_id, false)?; } - } else if !state.pump_in_timeout(&plant_config, &timezone_time) { + } else if !state.pump_in_timeout(plant_config, &timezone_time) { // plant does not need to be watered and is not in timeout // -> reset consecutive pump count board.store_consecutive_pump_count(plant_id, 0); @@ -504,23 +503,21 @@ fn safe_main() -> anyhow::Result<()> { if config.night_lamp.night_lamp_only_when_dark { if !light_state.is_day { if light_state.battery_low { - board.light(false).unwrap(); + board.light(false)?; } else { light_state.active = true; - board.light(true).unwrap(); + board.light(true)?; } } + } else if light_state.battery_low { + board.light(false)?; } else { - if light_state.battery_low { - board.light(false).unwrap(); - } else { - light_state.active = true; - board.light(true).unwrap(); - } + light_state.active = true; + board.light(true)?; } } else { light_state.active = false; - board.light(false).unwrap(); + board.light(false)?; } println!("Lightstate is {:?}", light_state); @@ -547,10 +544,6 @@ fn safe_main() -> anyhow::Result<()> { }; let _ = board.mqtt_publish(&config, "/state", "sleep".as_bytes()); - //determine next event - //is light out of work trigger soon? - //is battery low ?? - //is deep sleep mark_app_valid(); let stay_alive_mqtt = STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed); @@ -576,7 +569,7 @@ fn publish_battery_state( let bat = board.get_battery_state(); match serde_json::to_string(&bat) { Ok(state) => { - let _ = board.mqtt_publish(&config, "/battery", state.as_bytes()); + let _ = board.mqtt_publish(config, "/battery", state.as_bytes()); } Err(err) => { println!("Error publishing battery_state {}", err); @@ -631,10 +624,8 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc) -> ! { drop(lock); vTaskDelay(delay); - if wait_type == WaitType::MqttConfig { - if !STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed) { - reboot_now.store(true, std::sync::atomic::Ordering::Relaxed); - } + if wait_type == WaitType::MqttConfig && !STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed) { + reboot_now.store(true, std::sync::atomic::Ordering::Relaxed); } if reboot_now.load(std::sync::atomic::Ordering::Relaxed) { //ensure clean http answer @@ -655,7 +646,7 @@ fn main() { BOARD_ACCESS.lock().unwrap().set_restart_to_conf(false); BOARD_ACCESS.lock().unwrap().deep_sleep(1); } - // if safe_main exists with error, rollback to known good ota version + // if safe_main exists with an error, rollback to a known good ota version Err(err) => { println!("Failed main {}", err); let _rollback_successful = rollback_and_reboot(); @@ -665,22 +656,22 @@ fn main() { } fn to_string(value: Result) -> String { - return match value { + match value { Ok(v) => v.to_string(), Err(err) => { format!("{:?}", err) } - }; + } } pub fn in_time_range(cur: &DateTime, start: u8, end: u8) -> bool { let curhour = cur.hour() as u8; //eg 10-14 if start < end { - return curhour > start && curhour < end; + curhour > start && curhour < end } else { //eg 20-05 - return curhour > start || curhour < end; + curhour > start || curhour < end } } @@ -695,11 +686,11 @@ fn get_version() -> VersionInfo { } else { "ota_0 @ " }; - return VersionInfo { - git_hash: (branch + "@" + hash), + VersionInfo { + git_hash: branch + "@" + hash, build_time: env!("VERGEN_BUILD_TIMESTAMP").to_owned(), partition: partition.to_owned() + &address.to_string(), - }; + } } #[derive(Serialize, Debug)] diff --git a/rust/src/plant_hal.rs b/rust/src/plant_hal.rs index 7496caf..9fb0bc2 100644 --- a/rust/src/plant_hal.rs +++ b/rust/src/plant_hal.rs @@ -60,9 +60,9 @@ use esp_idf_svc::systime::EspSystemTime; use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, vTaskDelay, EspError}; use one_wire_bus::OneWire; -use crate::config::{self, PlantControllerConfig}; +use crate::config::PlantControllerConfig; use crate::log::log; -use crate::{plant_hal, to_string, STAY_ALIVE}; +use crate::{to_string, STAY_ALIVE}; //Only support for 8 right now! pub const PLANT_COUNT: usize = 8; @@ -168,7 +168,7 @@ pub struct PlantCtrlBoard<'a> { tank_power: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, wifi_driver: EspWifi<'a>, - one_wire_bus: OneWire>, + one_wire_bus: OneWire>, mqtt_client: Option>, battery_driver: Bq34z100g1Driver>, Delay>, rtc: @@ -229,9 +229,9 @@ impl PlantCtrlBoard<'_> { pub fn deep_sleep(&mut self, duration_in_ms: u64) -> ! { self.shift_register.decompose()[AWAKE].set_low().unwrap(); unsafe { - //if we dont do this here, we might just revert a newly flashed firmeware + //if we don't do this here, we might just revert newly flashed firmware mark_app_valid(); - //allow early wakup by pressing the boot button + //allow early wakeup by pressing the boot button if duration_in_ms == 0 { esp_restart(); } else { @@ -278,7 +278,8 @@ impl PlantCtrlBoard<'_> { }; let header: BackupHeader = bincode::deserialize(&header_page_buffer)?; - let data_start_address = 1 * self.eeprom.page_size() as u32; + //skip page 0, used by the header + let data_start_address = self.eeprom.page_size() as u32; let mut data_buffer = vec![0_u8; header.size]; match self.eeprom.read_data(data_start_address, &mut data_buffer) { OkStd(_) => {} @@ -337,9 +338,9 @@ impl PlantCtrlBoard<'_> { OkStd(_) => {} Err(err) => bail!("Error writing eeprom {:?}", err), }; - current_page = current_page + 1; + current_page += 1; - let iter = ((current_page / 1) % 8) as usize; + let iter = (current_page % 8) as usize; if iter != lastiter { for i in 0..PLANT_COUNT { self.fault(i, iter == i); @@ -350,11 +351,11 @@ impl PlantCtrlBoard<'_> { //update led here? delay.delay_ms(5); } - return Ok(()); + Ok(()) } pub fn get_battery_state(&mut self) -> BatteryState { - let bat = BatteryState { + BatteryState { voltage_milli_volt: to_string(self.voltage_milli_volt()), current_milli_ampere: to_string(self.average_current_milli_ampere()), cycle_count: to_string(self.cycle_count()), @@ -363,14 +364,13 @@ impl PlantCtrlBoard<'_> { state_of_charge: to_string(self.state_charge_percent()), state_of_health: to_string(self.state_health_percent()), temperature: to_string(self.bat_temperature()), - }; - return bat; + } } pub fn list_files(&self) -> FileList { let storage = CString::new(SPIFFS_PARTITION_NAME).unwrap(); - let mut file_system_corrupt = Option::None; + let mut file_system_corrupt = None; let mut iter_error = None; let mut result = Vec::new(); @@ -386,7 +386,7 @@ impl PlantCtrlBoard<'_> { filename: file.file_name().into_string().unwrap(), size: file .metadata() - .and_then(|it| core::result::Result::Ok(it.len())) + .and_then(|it| Result::Ok(it.len())) .unwrap_or_default() as usize, }; @@ -409,13 +409,13 @@ impl PlantCtrlBoard<'_> { esp_spiffs_info(storage.as_ptr(), &mut total, &mut used); } - return FileList { + FileList { total, used, file_system_corrupt, files: result, iter_error, - }; + } } pub fn delete_file(&self, filename: &str) -> Result<()> { @@ -430,11 +430,11 @@ impl PlantCtrlBoard<'_> { pub fn get_file_handle(&self, filename: &str, write: bool) -> Result { let filepath = Path::new(BASE_PATH).join(Path::new(filename)); - return Ok(if write { + Ok(if write { File::create(filepath)? } else { File::open(filepath)? - }); + }) } pub fn is_day(&self) -> bool { @@ -522,17 +522,17 @@ impl PlantCtrlBoard<'_> { 7 => PUMP8_BIT, _ => bail!("Invalid pump {plant}",), }; - //currently infailable error, keep for future as result anyway + //currently infallible error, keep for future as result anyway self.shift_register.decompose()[index].set_state(enable.into())?; Ok(()) } - pub fn last_pump_time(&self, plant: usize) -> Option> { + pub fn last_pump_time(&self, plant: usize) -> Option> { let ts = unsafe { LAST_WATERING_TIMESTAMP }[plant]; - return Some(DateTime::from_timestamp_millis(ts)?); + DateTime::from_timestamp_millis(ts) } - pub fn store_last_pump_time(&mut self, plant: usize, time: chrono::DateTime) { + pub fn store_last_pump_time(&mut self, plant: usize, time: DateTime) { unsafe { LAST_WATERING_TIMESTAMP[plant] = time.timestamp_millis(); } @@ -546,7 +546,7 @@ impl PlantCtrlBoard<'_> { pub fn consecutive_pump_count(&mut self, plant: usize) -> u32 { unsafe { - return CONSECUTIVE_WATERING_PLANT[plant]; + CONSECUTIVE_WATERING_PLANT[plant] } } @@ -569,18 +569,18 @@ impl PlantCtrlBoard<'_> { pub fn low_voltage_in_cycle(&mut self) -> bool { unsafe { - return LOW_VOLTAGE_DETECTED; + LOW_VOLTAGE_DETECTED } } pub fn any_pump(&mut self, enable: bool) -> Result<()> { { - self.main_pump.set_state(enable.into()).unwrap(); + self.main_pump.set_state(enable.into())?; Ok(()) } } - pub fn time(&mut self) -> Result> { + pub fn time(&mut self) -> Result> { let time = EspSystemTime {}.now().as_millis(); let smaller_time = time as i64; let local_time = DateTime::from_timestamp_millis(smaller_time) @@ -588,7 +588,7 @@ impl PlantCtrlBoard<'_> { Ok(local_time) } - pub fn sntp(&mut self, max_wait_ms: u32) -> Result> { + pub fn sntp(&mut self, max_wait_ms: u32) -> Result> { let sntp = sntp::EspSntp::new_default()?; let mut counter = 0; while sntp.get_sync_status() != SyncStatus::Completed { @@ -634,28 +634,26 @@ impl PlantCtrlBoard<'_> { self.signal_counter.counter_pause()?; self.signal_counter.counter_clear()?; //Disable all - self.shift_register.decompose()[MS_4].set_high().unwrap(); + self.shift_register.decompose()[MS_4].set_high()?; self.sensor_multiplexer(sensor_channel)?; - self.shift_register.decompose()[MS_4].set_low().unwrap(); + self.shift_register.decompose()[MS_4].set_low()?; self.shift_register.decompose()[SENSOR_ON] - .set_high() - .unwrap(); + .set_high()?; let delay = Delay::new_default(); let measurement = 100; // TODO what is this scaling factor? what is its purpose? - let factor = 1000 as f32 / measurement as f32; + let factor = 1000f32 / measurement as f32; //give some time to stabilize delay.delay_ms(10); self.signal_counter.counter_resume()?; delay.delay_ms(measurement); self.signal_counter.counter_pause()?; - self.shift_register.decompose()[MS_4].set_high().unwrap(); + self.shift_register.decompose()[MS_4].set_high()?; self.shift_register.decompose()[SENSOR_ON] - .set_low() - .unwrap(); + .set_low()?; delay.delay_ms(10); let unscaled = self.signal_counter.get_counter_value()? as i32; let hz = unscaled as f32 * factor; @@ -707,7 +705,7 @@ impl PlantCtrlBoard<'_> { //TODO expect error due to invalid pw or similar! //call this during configuration and check if works, revert to config mode if not self.wifi_driver.set_configuration(&Configuration::Client( ClientConfiguration { - ssid: ssid, + ssid, password: pw, ..Default::default() }, @@ -716,7 +714,7 @@ impl PlantCtrlBoard<'_> { None => { self.wifi_driver.set_configuration(&Configuration::Client( ClientConfiguration { - ssid: ssid, + ssid, auth_method: AuthMethod::None, ..Default::default() }, @@ -733,7 +731,7 @@ impl PlantCtrlBoard<'_> { delay.delay_ms(250); counter += 250; if counter > max_wait { - //ignore these errors, wifi will not be used this + //ignore these errors, Wi-Fi will not be used this self.wifi_driver.disconnect().unwrap_or(()); self.wifi_driver.stop().unwrap_or(()); bail!("Did not manage wifi connection within timeout"); @@ -745,7 +743,7 @@ impl PlantCtrlBoard<'_> { delay.delay_ms(250); counter += 250; if counter > max_wait { - //ignore these errors, wifi will not be used this + //ignore these errors, Wi-Fi will not be used this self.wifi_driver.disconnect().unwrap_or(()); self.wifi_driver.stop().unwrap_or(()); bail!("Did not manage wifi connection within timeout"); @@ -780,7 +778,7 @@ impl PlantCtrlBoard<'_> { let mut total_size = 0; let mut used_size = 0; unsafe { - esp_idf_sys::esp!(esp_idf_sys::esp_spiffs_info( + esp_idf_sys::esp!(esp_spiffs_info( storage.as_ptr(), &mut total_size, &mut used_size @@ -793,7 +791,7 @@ impl PlantCtrlBoard<'_> { }) } - pub fn is_mode_override(&mut self) -> bool { + pub fn mode_override_pressed(&mut self) -> bool { self.boot_button.get_level() == Level::Low } @@ -802,16 +800,20 @@ impl PlantCtrlBoard<'_> { let config = Path::new(CONFIG_FILE); if config.exists() { println!("Removing config"); - std::fs::remove_file(config)?; + fs::remove_file(config)?; } - //TODO clear eeprom + + //destroy backup header + let dummy : [u8;0] = []; + self.backup_config(&dummy)?; + Ok(()) } pub fn get_rtc_time(&mut self) -> Result> { match self.rtc.datetime() { OkStd(rtc_time) => { - return Ok(rtc_time.and_utc()); + Ok(rtc_time.and_utc()) } Err(err) => { bail!("Error getting rtc time {:?}", err) @@ -829,7 +831,7 @@ impl PlantCtrlBoard<'_> { } } - pub fn get_config(&mut self) -> Result { + pub fn get_config(&mut self) -> Result { let cfg = File::open(CONFIG_FILE)?; let config: PlantControllerConfig = serde_json::from_reader(cfg)?; Ok(config) @@ -889,8 +891,8 @@ impl PlantCtrlBoard<'_> { unsafe { vTaskDelay(100) }; } for plant in 0..PLANT_COUNT { - let a = self.measure_moisture_hz(plant, plant_hal::Sensor::A); - let b = self.measure_moisture_hz(plant, plant_hal::Sensor::B); + let a = self.measure_moisture_hz(plant, Sensor::A); + let b = self.measure_moisture_hz(plant, Sensor::B); let aa = match a { OkStd(a) => a as u32, Err(_) => u32::MAX, @@ -905,11 +907,6 @@ impl PlantCtrlBoard<'_> { Ok(()) } - pub fn is_wifi_config_file_existant(&mut self) -> bool { - let config = Path::new(CONFIG_FILE); - config.exists() - } - pub fn mqtt(&mut self, config: &PlantControllerConfig) -> Result<()> { let base_topic = config .network @@ -956,8 +953,8 @@ impl PlantCtrlBoard<'_> { let round_trip_topic_copy = round_trip_topic.clone(); let round_trip_ok_copy = round_trip_ok.clone(); let client_id = mqtt_client_config.client_id.unwrap_or("not set"); - log(LogMessage::MqttInfo, 0, 0, client_id, &mqtt_url); - let mut client = EspMqttClient::new_cb(&mqtt_url, &mqtt_client_config, move |event| { + log(LogMessage::MqttInfo, 0, 0, client_id, mqtt_url); + let mut client = EspMqttClient::new_cb(mqtt_url, &mqtt_client_config, move |event| { let payload = event.payload(); match payload { embedded_svc::mqtt::client::EventPayload::Received { @@ -977,7 +974,7 @@ impl PlantCtrlBoard<'_> { log(LogMessage::MqttStayAliveRec, 0, 0, &data, ""); STAY_ALIVE.store(value, std::sync::atomic::Ordering::Relaxed); } else { - log(LogMessage::UnknownTopic, 0, 0, "", &topic); + log(LogMessage::UnknownTopic, 0, 0, "", topic); } } } @@ -1018,8 +1015,9 @@ impl PlantCtrlBoard<'_> { } })?; - let wait_for_connections_event = 0; + let mut wait_for_connections_event = 0; while wait_for_connections_event < 100 { + wait_for_connections_event += 1; match mqtt_connected_event_received.load(std::sync::atomic::Ordering::Relaxed) { true => { println!("Mqtt connection callback received, progressing"); @@ -1037,8 +1035,9 @@ impl PlantCtrlBoard<'_> { "online_test".as_bytes(), )?; - let wait_for_roundtrip = 0; + let mut wait_for_roundtrip = 0; while wait_for_roundtrip < 100 { + wait_for_roundtrip+=1; match round_trip_ok.load(std::sync::atomic::Ordering::Relaxed) { true => { println!("Round trip registered, proceeding"); @@ -1085,7 +1084,7 @@ impl PlantCtrlBoard<'_> { let client = self.mqtt_client.as_mut().unwrap(); let mut full_topic: heapless::String<256> = heapless::String::new(); if full_topic - .push_str(&config.network.base_topic.as_ref().unwrap()) + .push_str(config.network.base_topic.as_ref().unwrap()) .is_err() { println!("Some error assembling full_topic 1"); @@ -1097,7 +1096,7 @@ impl PlantCtrlBoard<'_> { }; let publish = client.publish( &full_topic, - embedded_svc::mqtt::client::QoS::ExactlyOnce, + ExactlyOnce, true, message, ); @@ -1110,7 +1109,7 @@ impl PlantCtrlBoard<'_> { String::from_utf8_lossy(message), message_id ); - return Ok(()); + Ok(()) } Err(err) => { println!( @@ -1119,13 +1118,13 @@ impl PlantCtrlBoard<'_> { String::from_utf8_lossy(message), err ); - return Err(err)?; + Err(err)? } - }; + } } pub fn get_restart_to_conf(&mut self) -> bool { - return unsafe { RESTART_TO_CONF }; + unsafe { RESTART_TO_CONF } } pub fn set_restart_to_conf(&mut self, to_conf: bool) { @@ -1192,7 +1191,7 @@ impl PlantCtrlBoard<'_> { pub fn bat_temperature(&mut self) -> Result { match self.battery_driver.temperature() { - OkStd(r) => Ok(r as u16), + OkStd(r) => Ok(r), Err(err) => bail!("Error reading Temperature {:?}", err), } } @@ -1241,7 +1240,7 @@ fn print_battery( ) -> Result<(), Bq34Z100Error> { println!("Try communicating with battery"); let fwversion = battery_driver.fw_version().unwrap_or_else(|e| { - println!("Firmeware {:?}", e); + println!("Firmware {:?}", e); 0 }); println!("fw version is {}", fwversion); @@ -1291,10 +1290,10 @@ fn print_battery( println!("ChemId: {} Current voltage {} and current {} with charge {}% and temp {} CVolt: {} CCur {}", chem_id, voltage, current, state, temp_c, charge_voltage, charge_current); let _ = battery_driver.unsealed(); let _ = battery_driver.it_enable(); - return Result::Ok(()); + Result::Ok(()) } -pub static I2C_DRIVER: Lazy>> = Lazy::new(|| PlantHal::create_i2c()); +pub static I2C_DRIVER: Lazy>> = Lazy::new(PlantHal::create_i2c); impl PlantHal { fn create_i2c() -> Mutex> { let peripherals = unsafe { Peripherals::new() }; @@ -1315,15 +1314,15 @@ impl PlantHal { let peripherals = Peripherals::take()?; let mut clock = PinDriver::input_output(peripherals.pins.gpio15.downgrade())?; - clock.set_pull(Pull::Floating).unwrap(); + clock.set_pull(Pull::Floating)?; let mut latch = PinDriver::input_output(peripherals.pins.gpio3.downgrade())?; - latch.set_pull(Pull::Floating).unwrap(); + latch.set_pull(Pull::Floating)?; let mut data = PinDriver::input_output(peripherals.pins.gpio23.downgrade())?; - data.set_pull(Pull::Floating).unwrap(); - let shift_register = ShiftRegister40::new(clock.into(), latch.into(), data.into()); + data.set_pull(Pull::Floating)?; + let shift_register = ShiftRegister40::new(clock, latch, data); //disable all for mut pin in shift_register.decompose() { - pin.set_low().unwrap(); + pin.set_low()?; } let awake = &mut shift_register.decompose()[AWAKE]; @@ -1363,7 +1362,7 @@ impl PlantHal { }; let mut one_wire_pin = PinDriver::input_output_od(peripherals.pins.gpio18)?; - one_wire_pin.set_pull(Pull::Floating).unwrap(); + one_wire_pin.set_pull(Pull::Floating)?; let rtc_time = rtc.datetime(); match rtc_time { @@ -1498,7 +1497,7 @@ impl PlantHal { boot_button.set_pull(Pull::Floating)?; let mut light = PinDriver::input_output(peripherals.pins.gpio10.downgrade())?; - light.set_pull(Pull::Floating).unwrap(); + light.set_pull(Pull::Floating)?; let mut main_pump = PinDriver::input_output(peripherals.pins.gpio2.downgrade())?; main_pump.set_pull(Pull::Floating)?; @@ -1518,7 +1517,7 @@ impl PlantHal { Err(err) => { log( LogMessage::BatteryCommunicationError, - 0 as u32, + 0u32, 0, "", &format!("{err:?})"), @@ -1541,16 +1540,16 @@ impl PlantHal { signal_counter: counter_unit1, wifi_driver, mqtt_client: None, - battery_driver: battery_driver, - rtc: rtc, - eeprom: eeprom, + battery_driver, + rtc, + eeprom, }); let _ = rv.lock().is_ok_and(|mut board| { unsafe { gpio_hold_dis(board.shift_register_enable_invert.pin()) }; board.shift_register_enable_invert.set_low().unwrap(); unsafe { gpio_hold_en(board.shift_register_enable_invert.pin()) }; - return true; + true }); Ok(rv) diff --git a/rust/src/plant_state.rs b/rust/src/plant_state.rs index de94140..86b41a9 100644 --- a/rust/src/plant_state.rs +++ b/rust/src/plant_state.rs @@ -3,12 +3,12 @@ use chrono_tz::Tz; use serde::{Deserialize, Serialize}; use crate::{ - config::{self, PlantConfig}, + config::PlantConfig, in_time_range, plant_hal, }; const MOIST_SENSOR_MAX_FREQUENCY: f32 = 6500.; // 60kHz (500Hz margin) -const MOIST_SENSOR_MIN_FREQUENCY: f32 = 150.; // this is really really dry, think like cactus levels +const MOIST_SENSOR_MIN_FREQUENCY: f32 = 150.; // this is really, really dry, think like cactus levels #[derive(Debug, PartialEq, Serialize)] pub enum MoistureSensorError { @@ -117,7 +117,7 @@ impl PlantState { pub fn read_hardware_state( plant_id: usize, board: &mut plant_hal::PlantCtrlBoard, - config: &config::PlantConfig, + config: &PlantConfig, ) -> Self { let sensor_a = if config.sensor_a { match board.measure_moisture_hz(plant_id, plant_hal::Sensor::A) { @@ -225,28 +225,22 @@ impl PlantState { if let Some(moisture_percent) = moisture_percent { if self.pump_in_timeout(plant_conf, current_time) { false + } else if moisture_percent < plant_conf.target_moisture { + in_time_range( + current_time, + plant_conf.pump_hour_start, + plant_conf.pump_hour_end, + ) } else { - if moisture_percent < plant_conf.target_moisture { - in_time_range( - current_time, - plant_conf.pump_hour_start, - plant_conf.pump_hour_end, - ) - } else { - false - } + false } } else { - // in case no moisture can be determined do not water plant - return false; + // in case no moisture can be determined, do not water the plant + false } } PlantWateringMode::TimerOnly => { - if self.pump_in_timeout(plant_conf, current_time) { - false - } else { - true - } + !self.pump_in_timeout(plant_conf, current_time) } } } @@ -299,9 +293,9 @@ pub struct PlantInfo<'a> { sensor_b: &'a MoistureSensorState, /// configured plant watering mode mode: PlantWateringMode, - /// plant needs to be watered + /// the plant needs to be watered do_water: bool, - /// is plant considerd to be dry according to settings + /// plant is considered to be dry according to settings dry: bool, /// plant irrigation cooldown is active cooldown: bool, @@ -310,7 +304,7 @@ pub struct PlantInfo<'a> { /// how often has the pump been watered without reaching target moisture consecutive_pump_count: u32, pump_error: Option, - /// last time when pump was active + /// last time when the pump was active last_pump: Option>, /// next time when pump should activate next_pump: Option>, diff --git a/rust/src/tank.rs b/rust/src/tank.rs index 3ace7b9..6f77ac0 100644 --- a/rust/src/tank.rs +++ b/rust/src/tank.rs @@ -17,12 +17,12 @@ pub enum TankError { } pub enum TankState { - TankSensorPresent(f32), - TankSensorError(TankError), - TankSensorDisabled, + Present(f32), + Error(TankError), + Disabled, } -fn raw_volatge_to_divider_percent(raw_value_mv: f32) -> Result { +fn raw_voltage_to_divider_percent(raw_value_mv: f32) -> Result { if raw_value_mv > OPEN_TANK_VOLTAGE { return Err(TankError::SensorMissing(raw_value_mv)); } @@ -37,7 +37,7 @@ fn raw_voltage_to_tank_fill_percent( raw_value_mv: f32, config: &TankConfig, ) -> Result { - let divider_percent = raw_volatge_to_divider_percent(raw_value_mv)?; + let divider_percent = raw_voltage_to_divider_percent(raw_value_mv)?; if divider_percent < config.tank_empty_percent.into() || divider_percent > config.tank_full_percent.into() { @@ -56,9 +56,9 @@ fn raw_voltage_to_tank_fill_percent( impl TankState { pub fn left_ml(&self, config: &TankConfig) -> Result { match self { - TankState::TankSensorDisabled => Err(TankError::SensorDisabled), - TankState::TankSensorError(err) => Err(err.clone()), - TankState::TankSensorPresent(raw_value_mv) => { + TankState::Disabled => Err(TankError::SensorDisabled), + TankState::Error(err) => Err(err.clone()), + TankState::Present(raw_value_mv) => { let tank_fill_percent = raw_voltage_to_tank_fill_percent(*raw_value_mv, config)?; Ok(config.tank_useable_ml as f32 * tank_fill_percent / 100.) } @@ -66,9 +66,9 @@ impl TankState { } pub fn enough_water(&self, config: &TankConfig) -> Result { match self { - TankState::TankSensorDisabled => Err(TankError::SensorDisabled), - TankState::TankSensorError(err) => Err(err.clone()), - TankState::TankSensorPresent(raw_value_mv) => { + TankState::Disabled => Err(TankError::SensorDisabled), + TankState::Error(err) => Err(err.clone()), + TankState::Present(raw_value_mv) => { let tank_fill_percent = raw_voltage_to_tank_fill_percent(*raw_value_mv, config)?; if tank_fill_percent > config.tank_empty_percent.into() { Ok(true) @@ -80,14 +80,14 @@ impl TankState { } pub fn is_enabled(&self) -> bool { - matches!(self, TankState::TankSensorDisabled) + matches!(self, TankState::Disabled) } pub fn warn_level(&self, config: &TankConfig) -> Result { match self { - TankState::TankSensorDisabled => Err(TankError::SensorDisabled), - TankState::TankSensorError(err) => Err(err.clone()), - TankState::TankSensorPresent(raw_value_mv) => { + TankState::Disabled => Err(TankError::SensorDisabled), + TankState::Error(err) => Err(err.clone()), + TankState::Present(raw_value_mv) => { let tank_fill_percent = raw_voltage_to_tank_fill_percent(*raw_value_mv, config); match tank_fill_percent { Ok(value) => { @@ -108,11 +108,11 @@ impl TankState { pub fn got_error(&self, config: &TankConfig) -> Option { match self { - TankState::TankSensorPresent(raw_value_mv) => { + TankState::Present(raw_value_mv) => { raw_voltage_to_tank_fill_percent(*raw_value_mv, config).err() } - TankState::TankSensorError(err) => Some(err.clone()), - TankState::TankSensorDisabled => Some(TankError::SensorDisabled), + TankState::Error(err) => Some(err.clone()), + TankState::Disabled => Some(TankError::SensorDisabled), } } @@ -130,10 +130,10 @@ impl TankState { Ok(left_ml) => Some(left_ml), }; let enough_water = self.enough_water(config).unwrap_or(false); //NOTE: is this correct if there is an error assume not enough water? - let warn_level = self.warn_level(config).unwrap_or(false); //NOTE: should no warn level be triggered if there is an error? + let warn_level = self.warn_level(config).unwrap_or(false); //NOTE: should warn level be triggered if there is an error? let raw = match self { - TankState::TankSensorDisabled | TankState::TankSensorError(_) => None, - TankState::TankSensorPresent(raw_value_mv) => Some(*raw_value_mv), + TankState::Disabled | TankState::Error(_) => None, + TankState::Present(raw_value_mv) => Some(*raw_value_mv), }; let percent = match raw { @@ -163,30 +163,30 @@ pub fn determine_tank_state( ) -> TankState { if config.tank.tank_sensor_enabled { match board.tank_sensor_voltage() { - Ok(raw_sensor_value_mv) => TankState::TankSensorPresent(raw_sensor_value_mv), - Err(err) => TankState::TankSensorError(TankError::BoardError(err.to_string())), + Ok(raw_sensor_value_mv) => TankState::Present(raw_sensor_value_mv), + Err(err) => TankState::Error(TankError::BoardError(err.to_string())), } } else { - TankState::TankSensorDisabled + TankState::Disabled } } #[derive(Debug, Serialize)] /// Information structure send to mqtt for monitoring purposes pub struct TankInfo { - /// is there enough water in the tank + /// there is enough water in the tank enough_water: bool, /// warning that water needs to be refilled soon warn_level: bool, - /// estimation how many ml are still in tank + /// estimation how many ml are still in the tank left_ml: Option, - /// if there is was an issue with the water level sensor + /// if there is an issue with the water level sensor sensor_error: Option, /// raw water sensor value raw: Option, /// percent value percent: Option, - /// water in tank might be frozen + /// water in the tank might be frozen water_frozen: bool, /// water temperature water_temp: Option, diff --git a/rust/src/util.rs b/rust/src/util.rs index 3d46fb4..fbb0728 100644 --- a/rust/src/util.rs +++ b/rust/src/util.rs @@ -1,9 +1,10 @@ pub trait LimitPrecision { - fn to_precision(self, presision: i32) -> Self; + fn to_precision(self, precision: i32) -> Self; } impl LimitPrecision for f32 { fn to_precision(self, precision: i32) -> Self { - (self * (10_f32).powi(precision)).round() / (10_f32).powi(precision) + let factor = 10_f32.powi(precision); + (self * factor).round() / factor } -} +} \ No newline at end of file diff --git a/rust/src/webserver/webserver.rs b/rust/src/webserver/webserver.rs index 38ddb0f..b6e59a1 100644 --- a/rust/src/webserver/webserver.rs +++ b/rust/src/webserver/webserver.rs @@ -82,12 +82,10 @@ fn get_time( ) -> Result, anyhow::Error> { let mut board = BOARD_ACCESS.lock().unwrap(); let native = board - .time() - .and_then(|t| Ok(t.to_rfc3339())) + .time().map(|t| t.to_rfc3339()) .unwrap_or("error".to_string()); let rtc = board - .get_rtc_time() - .and_then(|t| Ok(t.to_rfc3339())) + .get_rtc_time().map(|t| t.to_rfc3339()) .unwrap_or("error".to_string()); let data = LoadData { @@ -166,7 +164,7 @@ fn get_backup_config( ) -> Result, anyhow::Error> { let mut board = BOARD_ACCESS.lock().unwrap(); let json = match board.get_backup_config() { - Ok(config) => std::str::from_utf8(&config)?.to_owned(), + Ok(config) => from_utf8(&config)?.to_owned(), Err(err) => { println!("Error get backup config {:?}", err); err.to_string() @@ -290,7 +288,7 @@ fn list_files( let board = BOARD_ACCESS.lock().unwrap(); let result = board.list_files(); let file_list_json = serde_json::to_string(&result)?; - return anyhow::Ok(Some(file_list_json)); + anyhow::Ok(Some(file_list_json)) } fn ota( @@ -333,7 +331,7 @@ fn ota( board.set_restart_to_conf(true); drop(board); finalizer.set_as_boot_partition()?; - return anyhow::Ok(None); + anyhow::Ok(None) } fn flash_bq(filename: &str, dryrun: bool) -> anyhow::Result<()> { @@ -372,7 +370,7 @@ fn flash_bq(filename: &str, dryrun: bool) -> anyhow::Result<()> { } println!("Finished flashing file {line} lines processed"); board.general_fault(false); - return anyhow::Ok(()); + anyhow::Ok(()) } @@ -380,12 +378,12 @@ fn flash_bq(filename: &str, dryrun: bool) -> anyhow::Result<()> { fn query_param(uri: &str, param_name: &str) -> Option { println!("{uri} get {param_name}"); let parsed = Url::parse(&format!("http://127.0.0.1/{uri}")).unwrap(); - let value = parsed.query_pairs().filter(|it| it.0 == param_name).next(); + let value = parsed.query_pairs().find(|it| it.0 == param_name); match value { Some(found) => { - return Some(found.1.into_owned()); + Some(found.1.into_owned()) } - None => return None, + None => None, } } @@ -694,7 +692,7 @@ fn cors_response( let mut response = request.into_response(status, None, &headers)?; response.write(body.as_bytes())?; response.flush()?; - return anyhow::Ok(()); + anyhow::Ok(()) } type AnyhowHandler = @@ -719,7 +717,7 @@ fn handle_error_to500( cors_response(request, 500, &error_text)?; } } - return anyhow::Ok(()); + anyhow::Ok(()) } fn read_up_to_bytes_from_request( diff --git a/rust/src_webpack/src/fileview.ts b/rust/src_webpack/src/fileview.ts index 69645e2..2d5e870 100644 --- a/rust/src_webpack/src/fileview.ts +++ b/rust/src_webpack/src/fileview.ts @@ -1,4 +1,4 @@ -import { Controller } from "./main"; +import {Controller} from "./main"; const regex = /[^a-zA-Z0-9_.]/g; @@ -24,7 +24,7 @@ export class FileView { let fileuploadname = document.getElementById("fileuploadname") as HTMLInputElement let fileuploadbtn = document.getElementById("fileuploadbtn") as HTMLInputElement fileuploadfile.onchange = () => { - var selectedFile = fileuploadfile.files?.[0]; + const selectedFile = fileuploadfile.files?.[0]; if (selectedFile == null) { //TODO error dialog here return @@ -42,7 +42,7 @@ export class FileView { } fileuploadbtn.onclick = () => { - var selectedFile = fileuploadfile.files?.[0]; + const selectedFile = fileuploadfile.files?.[0]; if (selectedFile == null) { //TODO error dialog here return @@ -77,8 +77,7 @@ class FileEntry { this.view.classList.add("fileentryouter") const template = require('./fileviewentry.html') as string; - const fileRaw = template.replaceAll("${fileid}", String(fileid)); - this.view.innerHTML = fileRaw + this.view.innerHTML = template.replaceAll("${fileid}", String(fileid)) let name = document.getElementById("file_" + fileid + "_name") as HTMLElement; let size = document.getElementById("file_" + fileid + "_size") as HTMLElement; diff --git a/rust/src_webpack/src/main.html b/rust/src_webpack/src/main.html index 91b8b92..33f82cc 100644 --- a/rust/src_webpack/src/main.html +++ b/rust/src_webpack/src/main.html @@ -45,7 +45,7 @@ display: inline-block; height: 100%; animation: indeterminateAnimation 1s infinite linear; - transform-origin: 0% 50%; + transform-origin: 0 50%; } diff --git a/rust/src_webpack/src/main.ts b/rust/src_webpack/src/main.ts index e369a32..dd4dcec 100644 --- a/rust/src_webpack/src/main.ts +++ b/rust/src_webpack/src/main.ts @@ -1,4 +1,3 @@ - import { deepEqual } from 'fast-equals'; declare var PUBLIC_URL: string; @@ -8,7 +7,7 @@ document.body.innerHTML = require('./main.html') as string; import { TimeView } from "./timeview"; -import { PlantView, PlantViews } from "./plant"; +import { PlantViews } from "./plant"; import { NetworkConfigView } from "./network"; import { NightLampView } from "./nightlightview"; import { TankConfigView } from "./tankview"; @@ -261,7 +260,6 @@ export class Controller { configChanged() { const current = controller.getConfig(); var pretty = JSON.stringify(current, undefined, 0); - var initial = JSON.stringify(this.initialConfig, undefined, 0); controller.submitView.setJson(pretty); if (deepEqual(current, controller.initialConfig)) { @@ -307,7 +305,7 @@ export class Controller { }) .then(response => response.text()) .then( - text => { + _ => { clearTimeout(timerId); controller.progressview.removeProgress("test_pump"); } @@ -507,3 +505,16 @@ controller.populateTimezones().then(_ => { controller.progressview.removeProgress("rebooting"); +window.addEventListener("beforeunload", (event) => { + const currentConfig = controller.getConfig(); + + // Check if the current state differs from the initial configuration + if (!deepEqual(currentConfig, controller.initialConfig)) { + const confirmationMessage = "You have unsaved changes. Are you sure you want to leave this page?"; + + // Standard behavior for displaying the confirmation dialog + event.preventDefault(); + event.returnValue = confirmationMessage; // This will trigger the browser's default dialog + return confirmationMessage; + } +}); \ No newline at end of file diff --git a/rust/src_webpack/src/network.ts b/rust/src_webpack/src/network.ts index e7870fe..8f04063 100644 --- a/rust/src_webpack/src/network.ts +++ b/rust/src_webpack/src/network.ts @@ -3,9 +3,9 @@ import { Controller } from "./main"; export class NetworkConfigView { setScanResult(ssidList: SSIDList) { this.ssidlist.innerHTML = '' - for (var ssid of ssidList.ssids) { - var wi = document.createElement("option"); - wi.value = ssid; + for (const ssid of ssidList.ssids) { + const wi = document.createElement("option"); + wi.value = ssid; this.ssidlist.appendChild(wi); } } diff --git a/rust/src_webpack/src/ota.ts b/rust/src_webpack/src/ota.ts index a533e02..670f9e4 100644 --- a/rust/src_webpack/src/ota.ts +++ b/rust/src_webpack/src/ota.ts @@ -17,7 +17,7 @@ export class OTAView { const file = document.getElementById("firmware_file") as HTMLInputElement; this.file1Upload = file this.file1Upload.onchange = () => { - var selectedFile = file.files?.[0]; + const selectedFile = file.files?.[0]; if (selectedFile == null) { //TODO error dialog here return diff --git a/rust/src_webpack/src/plant.html b/rust/src_webpack/src/plant.html index 8acdc1b..b653193 100644 --- a/rust/src_webpack/src/plant.html +++ b/rust/src_webpack/src/plant.html @@ -14,7 +14,7 @@ } .plantcheckbox{ min-width: 20px; - margin: 0px; + margin: 0; } @@ -59,7 +59,7 @@
Warn Pump Count:
-
diff --git a/rust/src_webpack/src/plant.ts b/rust/src_webpack/src/plant.ts index 100e11e..e44ad7a 100644 --- a/rust/src_webpack/src/plant.ts +++ b/rust/src_webpack/src/plant.ts @@ -1,7 +1,7 @@ const PLANT_COUNT = 8; -import { Controller } from "./main"; +import {Controller} from "./main"; export class PlantViews { private readonly measure_moisture: HTMLButtonElement; @@ -61,12 +61,10 @@ export class PlantView { constructor(plantId: number, parent:HTMLDivElement, controller:Controller) { - const dummy = this; - this.plantId = plantId; + this.plantId = plantId; this.plantDiv = document.createElement("div")! as HTMLDivElement const template = require('./plant.html') as string; - const plantRaw = template.replaceAll("${plantId}", String(plantId)); - this.plantDiv.innerHTML = plantRaw + this.plantDiv.innerHTML = template.replaceAll("${plantId}", String(plantId)) this.plantDiv.classList.add("plantcontainer") parent.appendChild(this.plantDiv) @@ -184,7 +182,7 @@ export class PlantView { } getConfig(): PlantConfig { - const rv: PlantConfig = { + return { mode: this.mode.value, target_moisture: this.targetMoisture.valueAsNumber, pump_time_s: this.pumpTimeS.valueAsNumber, @@ -196,14 +194,5 @@ export class PlantView { moisture_sensor_min_frequency: this.moistureSensorMinFrequency.valueAsNumber || undefined, moisture_sensor_max_frequency: this.moistureSensorMaxFrequency.valueAsNumber || undefined, }; - return rv; - } - - setMoistureA(a: number) { - this.moistureA.innerText = String(a); - } - - setMoistureB(b: number) { - this.moistureB.innerText = String(b); } } \ No newline at end of file diff --git a/rust/src_webpack/src/tankview.html b/rust/src_webpack/src/tankview.html index 0234d86..e4d1475 100644 --- a/rust/src_webpack/src/tankview.html +++ b/rust/src_webpack/src/tankview.html @@ -1,14 +1,14 @@