clippy happier
This commit is contained in:
parent
a401d4de7b
commit
26da6b39cc
7
rust/.idea/dictionaries/project.xml
generated
Normal file
7
rust/.idea/dictionaries/project.xml
generated
Normal file
@ -0,0 +1,7 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="project">
|
||||
<words>
|
||||
<w>sntp</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
@ -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,7 +455,7 @@ 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(),
|
||||
@ -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).unwrap();
|
||||
} else if light_state.battery_low {
|
||||
board.light(false)?;
|
||||
} else {
|
||||
light_state.active = true;
|
||||
board.light(true).unwrap();
|
||||
}
|
||||
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,11 +624,9 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
|
||||
drop(lock);
|
||||
vTaskDelay(delay);
|
||||
|
||||
if wait_type == WaitType::MqttConfig {
|
||||
if !STAY_ALIVE.load(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
|
||||
Delay::new_default().delay_ms(500);
|
||||
@ -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<T: Display>(value: Result<T>) -> String {
|
||||
return match value {
|
||||
match value {
|
||||
Ok(v) => v.to_string(),
|
||||
Err(err) => {
|
||||
format!("{:?}", err)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn in_time_range(cur: &DateTime<Tz>, 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)]
|
||||
|
@ -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<PinDriver<'a, Gpio18, esp_idf_hal::gpio::InputOutput>>,
|
||||
one_wire_bus: OneWire<PinDriver<'a, Gpio18, InputOutput>>,
|
||||
mqtt_client: Option<EspMqttClient<'a>>,
|
||||
battery_driver: Bq34z100g1Driver<MutexDevice<'a, I2cDriver<'a>>, 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<File> {
|
||||
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<chrono::DateTime<Utc>> {
|
||||
pub fn last_pump_time(&self, plant: usize) -> Option<DateTime<Utc>> {
|
||||
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<Utc>) {
|
||||
pub fn store_last_pump_time(&mut self, plant: usize, time: DateTime<Utc>) {
|
||||
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<chrono::DateTime<Utc>> {
|
||||
pub fn time(&mut self) -> Result<DateTime<Utc>> {
|
||||
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<chrono::DateTime<Utc>> {
|
||||
pub fn sntp(&mut self, max_wait_ms: u32) -> Result<DateTime<Utc>> {
|
||||
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<DateTime<Utc>> {
|
||||
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<config::PlantControllerConfig> {
|
||||
pub fn get_config(&mut self) -> Result<PlantControllerConfig> {
|
||||
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<u16> {
|
||||
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<I2cError>> {
|
||||
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<Mutex<I2cDriver<'static>>> = Lazy::new(|| PlantHal::create_i2c());
|
||||
pub static I2C_DRIVER: Lazy<Mutex<I2cDriver<'static>>> = Lazy::new(PlantHal::create_i2c);
|
||||
impl PlantHal {
|
||||
fn create_i2c() -> Mutex<I2cDriver<'static>> {
|
||||
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)
|
||||
|
@ -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,8 +225,7 @@ 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 {
|
||||
} else if moisture_percent < plant_conf.target_moisture {
|
||||
in_time_range(
|
||||
current_time,
|
||||
plant_conf.pump_hour_start,
|
||||
@ -235,18 +234,13 @@ impl PlantState {
|
||||
} else {
|
||||
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<PumpError>,
|
||||
/// last time when pump was active
|
||||
/// last time when the pump was active
|
||||
last_pump: Option<DateTime<Tz>>,
|
||||
/// next time when pump should activate
|
||||
next_pump: Option<DateTime<Tz>>,
|
||||
|
@ -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<f32, TankError> {
|
||||
fn raw_voltage_to_divider_percent(raw_value_mv: f32) -> Result<f32, TankError> {
|
||||
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<f32, TankError> {
|
||||
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<f32, TankError> {
|
||||
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<bool, TankError> {
|
||||
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<bool, TankError> {
|
||||
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<TankError> {
|
||||
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<f32>,
|
||||
/// if there is was an issue with the water level sensor
|
||||
/// if there is an issue with the water level sensor
|
||||
sensor_error: Option<TankError>,
|
||||
/// raw water sensor value
|
||||
raw: Option<f32>,
|
||||
/// percent value
|
||||
percent: Option<f32>,
|
||||
/// water in tank might be frozen
|
||||
/// water in the tank might be frozen
|
||||
water_frozen: bool,
|
||||
/// water temperature
|
||||
water_temp: Option<f32>,
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -82,12 +82,10 @@ fn get_time(
|
||||
) -> Result<Option<std::string::String>, 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<Option<std::string::String>, 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<std::string::String> {
|
||||
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(
|
||||
|
@ -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;
|
||||
|
@ -45,7 +45,7 @@
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
animation: indeterminateAnimation 1s infinite linear;
|
||||
transform-origin: 0% 50%;
|
||||
transform-origin: 0 50%;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
});
|
@ -3,8 +3,8 @@ import { Controller } from "./main";
|
||||
export class NetworkConfigView {
|
||||
setScanResult(ssidList: SSIDList) {
|
||||
this.ssidlist.innerHTML = ''
|
||||
for (var ssid of ssidList.ssids) {
|
||||
var wi = document.createElement("option");
|
||||
for (const ssid of ssidList.ssids) {
|
||||
const wi = document.createElement("option");
|
||||
wi.value = ssid;
|
||||
this.ssidlist.appendChild(wi);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -14,7 +14,7 @@
|
||||
}
|
||||
.plantcheckbox{
|
||||
min-width: 20px;
|
||||
margin: 0px;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -59,7 +59,7 @@
|
||||
</div>
|
||||
<div class="flexcontainer">
|
||||
<div class="plantkey">Warn Pump Count:</div>
|
||||
<input class="plantvalue" id="plant_${plantId}_max_consecutive_pump_count" type="number" min="1" max="50" ,
|
||||
<input class="plantvalue" id="plant_${plantId}_max_consecutive_pump_count" type="number" min="1" max="50"
|
||||
placeholder="10">
|
||||
</div>
|
||||
<div class="flexcontainer">
|
||||
|
@ -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.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);
|
||||
}
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
<style>
|
||||
.tankcheckbox {
|
||||
min-width: 20px;
|
||||
margin: 0px;
|
||||
margin: 0;
|
||||
}
|
||||
.tankkey{
|
||||
min-width: 250px;
|
||||
}
|
||||
.tankvalue{
|
||||
flex-grow: 1;
|
||||
margin: 0px;
|
||||
margin: 0;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
|
@ -38,7 +38,7 @@ export class TimeView {
|
||||
update(native: string, rtc: string) {
|
||||
this.esp_time.innerText = native;
|
||||
this.rtc_time.innerText = rtc;
|
||||
var date = new Date();
|
||||
const date = new Date();
|
||||
this.browser_time.innerText = date.toISOString();
|
||||
if(this.auto_refresh.checked){
|
||||
this.timer = setTimeout(this.controller.updateRTCData, 1000);
|
||||
|
9
website/.idea/website.iml
generated
Normal file
9
website/.idea/website.iml
generated
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
Loading…
x
Reference in New Issue
Block a user