bring selftest online, bring tz online, fix set_config/get_config race
This commit is contained in:
		| @@ -94,10 +94,10 @@ impl fmt::Display for FatError { | ||||
| #[macro_export] | ||||
| macro_rules! bail { | ||||
|     ($msg:literal $(,)?) => { | ||||
|         return $crate::FatError::fat_bail($msg) | ||||
|         return $crate::fat_error::fat_bail($msg) | ||||
|     }; | ||||
|     ($fmt:literal, $($arg:tt)*) => { | ||||
|         return $crate::FatError::fat_bail(&alloc::format!($fmt, $($arg)*)) | ||||
|         return $crate::fat_error::fat_bail(&alloc::format!($fmt, $($arg)*)) | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| @@ -1,5 +1,5 @@ | ||||
| use crate::hal::Box; | ||||
| use crate::FatError::{FatError, FatResult}; | ||||
| use crate::fat_error::{FatError, FatResult}; | ||||
| use alloc::string::String; | ||||
| use async_trait::async_trait; | ||||
| use bq34z100::{Bq34z100g1, Bq34z100g1Driver, Flags}; | ||||
|   | ||||
| @@ -6,10 +6,11 @@ use chrono::{DateTime, Utc}; | ||||
| use serde::Serialize; | ||||
|  | ||||
| use crate::hal::little_fs2storage_adapter::LittleFs2Filesystem; | ||||
| use crate::FatError::{ContextExt, FatError, FatResult}; | ||||
| use crate::fat_error::{ContextExt, FatError, FatResult}; | ||||
| use alloc::string::ToString; | ||||
| use alloc::sync::Arc; | ||||
| use alloc::{format, string::String, vec::Vec}; | ||||
| use alloc::borrow::ToOwned; | ||||
| use core::marker::PhantomData; | ||||
| use core::net::{IpAddr, Ipv4Addr}; | ||||
| use core::str::FromStr; | ||||
| @@ -317,7 +318,7 @@ impl Esp<'_> { | ||||
|         let stack = mk_static!(Stack, stack); | ||||
|  | ||||
|         spawner | ||||
|             .spawn(connection(self.controller.clone(), ssid)) | ||||
|             .spawn(connection(self.controller.clone(), ssid.to_owned())) | ||||
|             .ok(); | ||||
|         spawner.spawn(net_task(runner)).ok(); | ||||
|         spawner.spawn(run_dhcp(stack.clone(), gw_ip_addr_str)).ok(); | ||||
| @@ -328,13 +329,12 @@ impl Esp<'_> { | ||||
|             } | ||||
|             Timer::after(Duration::from_millis(500)).await; | ||||
|         } | ||||
|         println!( | ||||
|             "Connect to the AP `esp-wifi` and point your browser to http://{gw_ip_addr_str}:8080/" | ||||
|         ); | ||||
|         println!("DHCP is enabled so there's no need to configure a static IP, just in case:"); | ||||
|         while !stack.is_config_up() { | ||||
|             Timer::after(Duration::from_millis(100)).await | ||||
|         } | ||||
|         println!( | ||||
|             "Connect to the AP `${ssid}` and point your browser to http://{gw_ip_addr_str}/" | ||||
|         ); | ||||
|         stack | ||||
|             .config_v4() | ||||
|             .inspect(|c| println!("ipv4 config: {c:?}")); | ||||
| @@ -801,11 +801,6 @@ async fn connection( | ||||
|     controller: Arc<Mutex<CriticalSectionRawMutex, WifiController<'static>>>, | ||||
|     ssid: String, | ||||
| ) { | ||||
|     println!("start connection task"); | ||||
|     println!( | ||||
|         "Device capabilities: {:?}", | ||||
|         controller.lock().await.capabilities() | ||||
|     ); | ||||
|     let client_config = Configuration::AccessPoint(AccessPointConfiguration { | ||||
|         ssid: ssid.clone(), | ||||
|         ..Default::default() | ||||
| @@ -815,7 +810,6 @@ async fn connection( | ||||
|         .await | ||||
|         .set_configuration(&client_config) | ||||
|         .unwrap(); | ||||
|     println!("Starting wifi"); | ||||
|     controller.lock().await.start_async().await.unwrap(); | ||||
|     println!("Wifi started!"); | ||||
| } | ||||
|   | ||||
| @@ -3,7 +3,7 @@ use crate::hal::esp::Esp; | ||||
| use crate::hal::rtc::{BackupHeader, RTCModuleInteraction}; | ||||
| use crate::hal::water::TankSensor; | ||||
| use crate::hal::{BoardInteraction, FreePeripherals, Sensor}; | ||||
| use crate::FatError::{FatError, FatResult}; | ||||
| use crate::fat_error::{FatError, FatResult}; | ||||
| use crate::{ | ||||
|     bail, | ||||
|     config::PlantControllerConfig, | ||||
| @@ -100,7 +100,7 @@ impl<'a> BoardInteraction<'a> for Initial<'a> { | ||||
|     fn is_day(&self) -> bool { | ||||
|         false | ||||
|     } | ||||
|     fn light(&mut self, _enable: bool) -> Result<(), FatError> { | ||||
|     async fn light(&mut self, _enable: bool) -> Result<(), FatError> { | ||||
|         bail!("Please configure board revision") | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -84,7 +84,7 @@ use crate::hal::battery::{print_battery_bq34z100, BQ34Z100G1}; | ||||
| use crate::hal::little_fs2storage_adapter::LittleFs2Filesystem; | ||||
| use crate::hal::water::TankSensor; | ||||
| use crate::log::LOG_ACCESS; | ||||
| use crate::FatError::FatError; | ||||
| use crate::fat_error::FatError; | ||||
| use embassy_sync::mutex::Mutex; | ||||
| use embassy_sync::once_lock::OnceLock; | ||||
| use esp_alloc as _; | ||||
| @@ -140,7 +140,7 @@ pub trait BoardInteraction<'a> { | ||||
|  | ||||
|     fn is_day(&self) -> bool; | ||||
|     //should be multsampled | ||||
|     fn light(&mut self, enable: bool) -> Result<(), FatError>; | ||||
|     async fn light(&mut self, enable: bool) -> Result<(), FatError>; | ||||
|     async fn pump(&mut self, plant: usize, enable: bool) -> Result<(), FatError>; | ||||
|     async fn pump_current(&mut self, plant: usize) -> Result<Current, FatError>; | ||||
|     async fn fault(&mut self, plant: usize, enable: bool) -> Result<(), FatError>; | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| use crate::hal::Box; | ||||
| use crate::FatError::FatResult; | ||||
| use crate::fat_error::FatResult; | ||||
| use async_trait::async_trait; | ||||
| use bincode::config::Configuration; | ||||
| use bincode::{config, Decode, Encode}; | ||||
|   | ||||
| @@ -3,17 +3,19 @@ use crate::hal::battery::BatteryInteraction; | ||||
| use crate::hal::esp::Esp; | ||||
| use crate::hal::rtc::RTCModuleInteraction; | ||||
| use crate::hal::water::TankSensor; | ||||
| use crate::hal::{BoardInteraction, FreePeripherals, Sensor, I2C_DRIVER}; | ||||
| use crate::hal::{BoardInteraction, FreePeripherals, Sensor, I2C_DRIVER, PLANT_COUNT}; | ||||
| use alloc::boxed::Box; | ||||
| use alloc::string::ToString; | ||||
| use async_trait::async_trait; | ||||
| use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice; | ||||
| use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||||
| use embassy_time::Timer; | ||||
| use esp_hal::analog::adc::{Adc, AdcConfig, Attenuation}; | ||||
| use esp_hal::{twai, Blocking}; | ||||
| //use embedded_hal_bus::i2c::MutexDevice; | ||||
| use crate::bail; | ||||
| use crate::hal::v4_sensor::{SensorImpl, SensorInteraction}; | ||||
| use crate::FatError::{FatError, FatResult}; | ||||
| use crate::fat_error::{FatError, FatResult}; | ||||
| use esp_hal::gpio::{Flex, Input, InputConfig, Level, Output, OutputConfig, Pull}; | ||||
| use esp_hal::i2c::master::I2c; | ||||
| use esp_hal::pcnt::Pcnt; | ||||
| @@ -26,6 +28,7 @@ use ina219::SyncIna219; | ||||
| use measurements::Resistance; | ||||
| use measurements::{Current, Voltage}; | ||||
| use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface}; | ||||
| use crate::log::{LogMessage, LOG_ACCESS}; | ||||
|  | ||||
| const MPPT_CURRENT_SHUNT_OHMS: f64 = 0.05_f64; | ||||
| const TWAI_BAUDRATE: twai::BaudRate = twai::BaudRate::B125K; | ||||
| @@ -348,16 +351,14 @@ impl<'a> BoardInteraction<'a> for V4<'a> { | ||||
|     } | ||||
|  | ||||
|     fn is_day(&self) -> bool { | ||||
|         false | ||||
|         //self.charger.is_day() | ||||
|         self.charger.is_day() | ||||
|     } | ||||
|  | ||||
|     fn light(&mut self, enable: bool) -> Result<(), FatError> { | ||||
|         bail!("not implemented"); | ||||
|     async fn light(&mut self, enable: bool) -> Result<(), FatError> { | ||||
|         // unsafe { gpio_hold_dis(self.light.pin()) }; | ||||
|         // self.light.set_state(enable.into())?; | ||||
|          self.light.set_level(enable.into()); | ||||
|         // unsafe { gpio_hold_en(self.light.pin()) }; | ||||
|         // anyhow::Ok(()) | ||||
|          Ok(()) | ||||
|     } | ||||
|  | ||||
|     async fn pump(&mut self, plant: usize, enable: bool) -> FatResult<()> { | ||||
| @@ -417,40 +418,40 @@ impl<'a> BoardInteraction<'a> for V4<'a> { | ||||
|     } | ||||
|  | ||||
|     async fn test(&mut self) -> Result<(), FatError> { | ||||
|         // self.general_fault(true); | ||||
|         // self.esp.delay.delay_ms(100); | ||||
|         // self.general_fault(false); | ||||
|         // self.esp.delay.delay_ms(500); | ||||
|         // self.light(true)?; | ||||
|         // self.esp.delay.delay_ms(500); | ||||
|         // self.light(false)?; | ||||
|         // self.esp.delay.delay_ms(500); | ||||
|         // for i in 0..PLANT_COUNT { | ||||
|         //     self.fault(i, true)?; | ||||
|         //     self.esp.delay.delay_ms(500); | ||||
|         //     self.fault(i, false)?; | ||||
|         //     self.esp.delay.delay_ms(500); | ||||
|         // } | ||||
|         // for i in 0..PLANT_COUNT { | ||||
|         //     self.pump(i, true)?; | ||||
|         //     self.esp.delay.delay_ms(100); | ||||
|         //     self.pump(i, false)?; | ||||
|         //     self.esp.delay.delay_ms(100); | ||||
|         // } | ||||
|         // for plant in 0..PLANT_COUNT { | ||||
|         //     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, | ||||
|         //     }; | ||||
|         //     let bb = match b { | ||||
|         //         OkStd(b) => b as u32, | ||||
|         //         Err(_) => u32::MAX, | ||||
|         //     }; | ||||
|         //     log(LogMessage::TestSensor, aa, bb, &plant.to_string(), ""); | ||||
|         // } | ||||
|         // self.esp.delay.delay_ms(10); | ||||
|          self.general_fault(true).await; | ||||
|         Timer::after_millis(100).await; | ||||
|          self.general_fault(false).await; | ||||
|         Timer::after_millis(500).await; | ||||
|          self.light(true).await?; | ||||
|         Timer::after_millis(500).await; | ||||
|         self.light(false).await?; | ||||
|         Timer::after_millis(500).await; | ||||
|         for i in 0..PLANT_COUNT { | ||||
|             self.fault(i, true).await?; | ||||
|             Timer::after_millis(500).await; | ||||
|             self.fault(i, false).await?; | ||||
|             Timer::after_millis(500).await; | ||||
|         } | ||||
|          for i in 0..PLANT_COUNT { | ||||
|              self.pump(i, true).await?; | ||||
|              Timer::after_millis(100).await; | ||||
|              self.pump(i, false).await?; | ||||
|              Timer::after_millis(100).await; | ||||
|          } | ||||
|          for plant in 0..PLANT_COUNT { | ||||
|              let a = self.measure_moisture_hz(plant, Sensor::A).await; | ||||
|              let b = self.measure_moisture_hz(plant, Sensor::B).await; | ||||
|              let aa = match a { | ||||
|                  Ok(a) => a as u32, | ||||
|                  Err(_) => u32::MAX, | ||||
|              }; | ||||
|              let bb = match b { | ||||
|                  Ok(b) => b as u32, | ||||
|                  Err(_) => u32::MAX, | ||||
|              }; | ||||
|              LOG_ACCESS.lock().await.log(LogMessage::TestSensor, aa, bb, &plant.to_string(), "").await; | ||||
|          } | ||||
|         Timer::after_millis(10).await; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| use crate::hal::Box; | ||||
| use crate::hal::Sensor; | ||||
| use crate::log::{LogMessage, LOG_ACCESS}; | ||||
| use crate::FatError::FatResult; | ||||
| use crate::fat_error::{FatError, FatResult}; | ||||
| use alloc::format; | ||||
| use alloc::string::ToString; | ||||
| use async_trait::async_trait; | ||||
| @@ -121,8 +121,8 @@ impl SensorInteraction for SensorImpl { | ||||
|                 let median = results[mid]; | ||||
|                 Ok(median) | ||||
|             } | ||||
|             SensorImpl::CanBus { .. } => { | ||||
|                 todo!() | ||||
|             SensorImpl::CanBus { twai } => { | ||||
|                 Err(FatError::String {error: "Not yet implemented".to_string()}) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| use crate::bail; | ||||
| use crate::hal::{ADC1, TANK_MULTI_SAMPLE}; | ||||
| use crate::FatError::FatError; | ||||
| use crate::fat_error::FatError; | ||||
| use embassy_time::Timer; | ||||
| use esp_hal::analog::adc::{Adc, AdcConfig, AdcPin, Attenuation}; | ||||
| use esp_hal::delay::Delay; | ||||
|   | ||||
							
								
								
									
										223
									
								
								rust/src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										223
									
								
								rust/src/main.rs
									
									
									
									
									
								
							| @@ -17,7 +17,7 @@ use crate::hal::{esp_time, TIME_ACCESS}; | ||||
| use crate::log::LOG_ACCESS; | ||||
| use crate::tank::{determine_tank_state, TankError, WATER_FROZEN_THRESH}; | ||||
| use crate::webserver::httpd; | ||||
| use crate::FatError::FatResult; | ||||
| use crate::fat_error::FatResult; | ||||
| use crate::{ | ||||
|     config::BoardVersion::INITIAL, | ||||
|     hal::{PlantHal, HAL, PLANT_COUNT}, | ||||
| @@ -28,9 +28,10 @@ use alloc::string::{String, ToString}; | ||||
| use alloc::sync::Arc; | ||||
| use alloc::{format, vec}; | ||||
| use chrono::{DateTime, Datelike, Timelike, Utc}; | ||||
| use chrono_tz::Tz::{self}; | ||||
| use chrono_tz::Tz::{self, UTC}; | ||||
| use core::sync::atomic::{AtomicBool, Ordering}; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_net::Stack; | ||||
| use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||||
| use embassy_sync::mutex::{Mutex, MutexGuard}; | ||||
| use embassy_sync::once_lock::OnceLock; | ||||
| @@ -57,7 +58,7 @@ extern "C" fn custom_halt() -> ! { | ||||
| } | ||||
|  | ||||
| //use tank::*; | ||||
| mod FatError; | ||||
| mod fat_error; | ||||
| mod config; | ||||
| mod hal; | ||||
| mod log; | ||||
| @@ -185,10 +186,8 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|             .log(LogMessage::YearInplausibleForceConfig, 0, 0, "", "") | ||||
|             .await; | ||||
|     } | ||||
|  | ||||
|     info!("cur is {}", cur); | ||||
|     update_charge_indicator(&mut board).await; | ||||
|     println!("faul led3"); | ||||
|     if board.board_hal.get_esp().get_restart_to_conf() { | ||||
|         LOG_ACCESS | ||||
|             .lock() | ||||
| @@ -228,10 +227,10 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|         info!("no mode override"); | ||||
|     } | ||||
|  | ||||
|     //TODO hack | ||||
|     if true | ||||
|         || (board.board_hal.get_config().hardware.board == INITIAL | ||||
|             && board.board_hal.get_config().network.ssid.is_none()) | ||||
|  | ||||
|  | ||||
|     if (board.board_hal.get_config().hardware.board == INITIAL | ||||
|         && board.board_hal.get_config().network.ssid.is_none()) | ||||
|     { | ||||
|         info!("No wifi configured, starting initial config mode"); | ||||
|  | ||||
| @@ -241,13 +240,14 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|         println!("starting webserver"); | ||||
|  | ||||
|         spawner.spawn(httpd(reboot_now.clone(), stack))?; | ||||
|  | ||||
|         wait_infinity(board, WaitType::MissingConfig, reboot_now.clone()).await; | ||||
|     } | ||||
|  | ||||
|     info!("attempting to connect wifi"); | ||||
|     info!("attempting to connect wifi "); | ||||
|  | ||||
|     let mut stack = Option::None; | ||||
|     let network_mode = if board.board_hal.get_config().network.ssid.is_some() { | ||||
|         try_connect_wifi_sntp_mqtt().await | ||||
|         try_connect_wifi_sntp_mqtt(&mut board, *&mut stack).await | ||||
|     } else { | ||||
|         info!("No wifi configured"); | ||||
|         //the current sensors require this amount to stabilize, in case of wifi this is already handles for sure; | ||||
| @@ -255,34 +255,38 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|         NetworkMode::OFFLINE | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     if matches!(network_mode, NetworkMode::OFFLINE) && to_config { | ||||
|         info!("Could not connect to station and config mode forced, switching to ap mode!"); | ||||
|  | ||||
|         let res = { | ||||
|             let esp = board.board_hal.get_esp(); | ||||
|             esp.wifi_ap().await | ||||
|  | ||||
|         }; | ||||
|         match res { | ||||
|             Ok(_) => { | ||||
|             Ok(ap_stack) => { | ||||
|                 stack = Some(ap_stack); | ||||
|                 info!("Started ap, continuing") | ||||
|             } | ||||
|             Err(err) => info!("Could not start config override ap mode due to {}", err), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // let timezone = match &board.board_hal.get_config().timezone { | ||||
|     //     Some(tz_str) => tz_str.parse::<Tz>().unwrap_or_else(|_| { | ||||
|     //         info!("Invalid timezone '{}', falling back to UTC", tz_str); | ||||
|     //         UTC | ||||
|     //     }), | ||||
|     //     None => UTC, // Fallback to UTC if no timezone is set | ||||
|     // }; | ||||
|     let tz = & board.board_hal.get_config().timezone; | ||||
|      let timezone = match tz { | ||||
|          Some(tz_str) => tz_str.parse::<Tz>().unwrap_or_else(|_| { | ||||
|              info!("Invalid timezone '{}', falling back to UTC", tz_str); | ||||
|              UTC | ||||
|          }), | ||||
|          None => UTC, // Fallback to UTC if no timezone is set | ||||
|      }; | ||||
|     let _timezone = Tz::UTC; | ||||
|  | ||||
|     let timezone_time = cur; //TODO.with_timezone(&timezone); | ||||
|     let timezone_time = cur.with_timezone(&timezone); | ||||
|     info!( | ||||
|         "Running logic at utc {} and {} {}", | ||||
|         cur, "todo timezone.name()", timezone_time | ||||
|         cur, timezone.name(), timezone_time | ||||
|     ); | ||||
|  | ||||
|     if let NetworkMode::WIFI { ref ip_address, .. } = network_mode { | ||||
| @@ -316,8 +320,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|         info!("executing config mode override"); | ||||
|         //config upload will trigger reboot! | ||||
|         let reboot_now = Arc::new(AtomicBool::new(false)); | ||||
|         //spawner.spawn(httpd(reboot_now.clone(), stack))?; | ||||
|         let board = BOARD_ACCESS.get().await.lock().await; | ||||
|         spawner.spawn(httpd(reboot_now.clone(), stack.unwrap()))?; | ||||
|         wait_infinity(board, WaitType::ConfigButton, reboot_now.clone()).await; | ||||
|     } else { | ||||
|         LOG_ACCESS | ||||
| @@ -496,71 +499,71 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|         .await | ||||
|         .unwrap_or(BatteryState::Unknown); | ||||
|  | ||||
|     let light_state = LightState { | ||||
|     let mut light_state = LightState { | ||||
|         enabled: board.board_hal.get_config().night_lamp.enabled, | ||||
|         ..Default::default() | ||||
|     }; | ||||
|     // if light_state.enabled { | ||||
|     //     light_state.is_day = is_day; | ||||
|     //     light_state.out_of_work_hour = !in_time_range( | ||||
|     //         &timezone_time, | ||||
|     //         board | ||||
|     //             .board_hal | ||||
|     //             .get_config() | ||||
|     //             .night_lamp | ||||
|     //             .night_lamp_hour_start, | ||||
|     //         board.board_hal.get_config().night_lamp.night_lamp_hour_end, | ||||
|     //     ); | ||||
|     // | ||||
|     //     if state_of_charge | ||||
|     //         < board | ||||
|     //             .board_hal | ||||
|     //             .get_config() | ||||
|     //             .night_lamp | ||||
|     //             .low_soc_cutoff | ||||
|     //             .into() | ||||
|     //     { | ||||
|     //         board.board_hal.get_esp().set_low_voltage_in_cycle(); | ||||
|     //     } else if state_of_charge | ||||
|     //         > board | ||||
|     //             .board_hal | ||||
|     //             .get_config() | ||||
|     //             .night_lamp | ||||
|     //             .low_soc_restore | ||||
|     //             .into() | ||||
|     //     { | ||||
|     //         board.board_hal.get_esp().clear_low_voltage_in_cycle(); | ||||
|     //     } | ||||
|     //     light_state.battery_low = board.board_hal.get_esp().low_voltage_in_cycle(); | ||||
|     // | ||||
|     //     if !light_state.out_of_work_hour { | ||||
|     //         if board | ||||
|     //             .board_hal | ||||
|     //             .get_config() | ||||
|     //             .night_lamp | ||||
|     //             .night_lamp_only_when_dark | ||||
|     //         { | ||||
|     //             if !light_state.is_day { | ||||
|     //                 if light_state.battery_low { | ||||
|     //                     board.board_hal.light(false)?; | ||||
|     //                 } else { | ||||
|     //                     light_state.active = true; | ||||
|     //                     board.board_hal.light(true)?; | ||||
|     //                 } | ||||
|     //             } | ||||
|     //         } else if light_state.battery_low { | ||||
|     //             board.board_hal.light(false)?; | ||||
|     //         } else { | ||||
|     //             light_state.active = true; | ||||
|     //             board.board_hal.light(true)?; | ||||
|     //         } | ||||
|     //     } else { | ||||
|     //         light_state.active = false; | ||||
|     //         board.board_hal.light(false)?; | ||||
|     //     } | ||||
|     // | ||||
|     //     info!("Lightstate is {:?}", light_state); | ||||
|     // } | ||||
|     if light_state.enabled { | ||||
|         light_state.is_day = is_day; | ||||
|         light_state.out_of_work_hour = !in_time_range( | ||||
|             &timezone_time, | ||||
|             board | ||||
|                 .board_hal | ||||
|                 .get_config() | ||||
|                 .night_lamp | ||||
|                 .night_lamp_hour_start, | ||||
|             board.board_hal.get_config().night_lamp.night_lamp_hour_end, | ||||
|         ); | ||||
|  | ||||
|         if state_of_charge | ||||
|             < board | ||||
|                 .board_hal | ||||
|                 .get_config() | ||||
|                 .night_lamp | ||||
|                 .low_soc_cutoff | ||||
|                 .into() | ||||
|         { | ||||
|             board.board_hal.get_esp().set_low_voltage_in_cycle(); | ||||
|         } else if state_of_charge | ||||
|             > board | ||||
|                 .board_hal | ||||
|                 .get_config() | ||||
|                 .night_lamp | ||||
|                 .low_soc_restore | ||||
|                 .into() | ||||
|         { | ||||
|             board.board_hal.get_esp().clear_low_voltage_in_cycle(); | ||||
|         } | ||||
|         light_state.battery_low = board.board_hal.get_esp().low_voltage_in_cycle(); | ||||
|  | ||||
|         if !light_state.out_of_work_hour { | ||||
|             if board | ||||
|                 .board_hal | ||||
|                 .get_config() | ||||
|                 .night_lamp | ||||
|                 .night_lamp_only_when_dark | ||||
|             { | ||||
|                 if !light_state.is_day { | ||||
|                     if light_state.battery_low { | ||||
|                         board.board_hal.light(false).await?; | ||||
|                     } else { | ||||
|                         light_state.active = true; | ||||
|                         board.board_hal.light(true).await?; | ||||
|                     } | ||||
|                 } | ||||
|             } else if light_state.battery_low { | ||||
|                 board.board_hal.light(false).await?; | ||||
|             } else { | ||||
|                 light_state.active = true; | ||||
|                 board.board_hal.light(true).await?; | ||||
|             } | ||||
|         } else { | ||||
|             light_state.active = false; | ||||
|             board.board_hal.light(false).await?; | ||||
|         } | ||||
|  | ||||
|         info!("Lightstate is {:?}", light_state); | ||||
|     } | ||||
|  | ||||
|     match serde_json::to_string(&light_state) { | ||||
|         Ok(state) => { | ||||
| @@ -629,7 +632,9 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| pub async fn do_secure_pump( | ||||
|     board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'_>>, | ||||
|     plant_id: usize, | ||||
|     plant_config: &PlantConfig, | ||||
|     dry_run: bool, | ||||
| @@ -639,26 +644,22 @@ pub async fn do_secure_pump( | ||||
|     let mut error = false; | ||||
|     let mut first_error = true; | ||||
|     let mut pump_time_s = 0; | ||||
|     let board = &mut BOARD_ACCESS.get().await.lock().await; | ||||
|     if !dry_run { | ||||
|         // board | ||||
|         //     .board_hal | ||||
|         //     .get_tank_sensor() | ||||
|         //     .unwrap() | ||||
|         //     .reset_flow_meter(); | ||||
|         // board | ||||
|         //     .board_hal | ||||
|         //     .get_tank_sensor() | ||||
|         //     .unwrap() | ||||
|         //     .start_flow_meter(); | ||||
|          board | ||||
|              .board_hal | ||||
|              .get_tank_sensor()? | ||||
|              .reset_flow_meter(); | ||||
|          board | ||||
|              .board_hal | ||||
|              .get_tank_sensor()? | ||||
|              .start_flow_meter(); | ||||
|         board.board_hal.pump(plant_id, true).await?; | ||||
|         Timer::after_millis(10).await; | ||||
|         for step in 0..plant_config.pump_time_s as usize { | ||||
|             // let flow_value = board | ||||
|             //     .board_hal | ||||
|             //     .get_tank_sensor() | ||||
|             //     .unwrap() | ||||
|             //     .get_flow_meter_value(); | ||||
|              let flow_value = board | ||||
|                  .board_hal | ||||
|                  .get_tank_sensor()? | ||||
|                  .get_flow_meter_value(); | ||||
|             let flow_value = 1; | ||||
|             flow_collector[step] = flow_value; | ||||
|             let flow_value_ml = flow_value as f32 * board.board_hal.get_config().tank.ml_per_pulse; | ||||
| @@ -749,12 +750,11 @@ pub async fn do_secure_pump( | ||||
|             pump_time_s += 1; | ||||
|         } | ||||
|     } | ||||
|     // board.board_hal.get_tank_sensor().unwrap().stop_flow_meter(); | ||||
|     // let final_flow_value = board | ||||
|     //     .board_hal | ||||
|     //     .get_tank_sensor() | ||||
|     //     .unwrap() | ||||
|     //     .get_flow_meter_value(); | ||||
|      board.board_hal.get_tank_sensor().unwrap().stop_flow_meter(); | ||||
|      let final_flow_value = board | ||||
|          .board_hal | ||||
|          .get_tank_sensor()? | ||||
|          .get_flow_meter_value(); | ||||
|     let final_flow_value = 12; | ||||
|     let flow_value_ml = final_flow_value as f32 * board.board_hal.get_config().tank.ml_per_pulse; | ||||
|     info!( | ||||
| @@ -898,8 +898,7 @@ async fn publish_firmware_info( | ||||
|     let _ = esp.mqtt_publish("/state", "online".as_bytes()).await; | ||||
| } | ||||
|  | ||||
| async fn try_connect_wifi_sntp_mqtt() -> NetworkMode { | ||||
|     let board = &mut BOARD_ACCESS.get().await.lock().await; | ||||
| async fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard<'static, CriticalSectionRawMutex, HAL<'static>>, stack_store:Option<Stack<'_>>) -> NetworkMode { | ||||
|     let nw_conf = &board.board_hal.get_config().network.clone(); | ||||
|     match board.board_hal.get_esp().wifi(nw_conf).await { | ||||
|         Ok(ip_info) => { | ||||
| @@ -936,8 +935,8 @@ async fn try_connect_wifi_sntp_mqtt() -> NetworkMode { | ||||
|                 ip_address: ip_info.ip.to_string(), | ||||
|             } | ||||
|         } | ||||
|         Err(_) => { | ||||
|             info!("Offline mode"); | ||||
|         Err(err) => { | ||||
|             info!("Offline mode due to {}", err); | ||||
|             board.board_hal.general_fault(true).await; | ||||
|             NetworkMode::OFFLINE | ||||
|         } | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| use crate::alloc::string::{String, ToString}; | ||||
| use crate::config::TankConfig; | ||||
| use crate::hal::HAL; | ||||
| use crate::FatError::FatResult; | ||||
| use crate::fat_error::FatResult; | ||||
| use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||||
| use embassy_sync::mutex::MutexGuard; | ||||
| use serde::Serialize; | ||||
|   | ||||
| @@ -5,8 +5,8 @@ use crate::hal::rtc::X25; | ||||
| use crate::hal::{esp_set_time, esp_time}; | ||||
| use crate::log::LOG_ACCESS; | ||||
| use crate::tank::determine_tank_state; | ||||
| use crate::FatError::{FatError, FatResult}; | ||||
| use crate::{bail, get_version, log::LogMessage, BOARD_ACCESS}; | ||||
| use crate::fat_error::{FatError, FatResult}; | ||||
| use crate::{bail, do_secure_pump, get_version, log::LogMessage, BOARD_ACCESS}; | ||||
| use alloc::borrow::ToOwned; | ||||
| use alloc::format; | ||||
| use alloc::string::{String, ToString}; | ||||
| @@ -16,8 +16,9 @@ use chrono::DateTime; | ||||
| use core::fmt::{Debug, Display}; | ||||
| use core::net::{IpAddr, Ipv4Addr, SocketAddr}; | ||||
| use core::result::Result::Ok; | ||||
| use core::str::from_utf8; | ||||
| use core::str::{from_utf8, FromStr}; | ||||
| use core::sync::atomic::{AtomicBool, Ordering}; | ||||
| use chrono_tz::Tz; | ||||
| use edge_http::io::server::{Connection, Handler, Server}; | ||||
| use edge_http::Method; | ||||
| use edge_nal::TcpBind; | ||||
| @@ -77,16 +78,7 @@ pub struct NightLampCommand { | ||||
| // | ||||
|  | ||||
| // | ||||
| // fn get_timezones( | ||||
| //     _request: &mut Request<&mut EspHttpConnection>, | ||||
| // ) -> Result<Option<std::string::String>, anyhow::Error> { | ||||
| //     // Get all timezones using chrono-tz | ||||
| //     let timezones: Vec<&'static str> = chrono_tz::TZ_VARIANTS.iter().map(|tz| tz.name()).collect(); | ||||
| // | ||||
| //     // Convert to JSON | ||||
| //     let json = serde_json::to_string(&timezones)?; | ||||
| //     anyhow::Ok(Some(json)) | ||||
| // } | ||||
|  | ||||
| // | ||||
| // fn get_live_moisture( | ||||
| //     _request: &mut Request<&mut EspHttpConnection>, | ||||
| @@ -124,51 +116,6 @@ pub struct NightLampCommand { | ||||
| //     anyhow::Ok(Some(json)) | ||||
| // } | ||||
| // | ||||
|  | ||||
| // | ||||
|  | ||||
| // | ||||
|  | ||||
| // | ||||
|  | ||||
| // | ||||
| // fn pump_test( | ||||
| //     request: &mut Request<&mut EspHttpConnection>, | ||||
| // ) -> Result<Option<std::string::String>, anyhow::Error> { | ||||
| //     let actual_data = read_up_to_bytes_from_request(request, None)?; | ||||
| //     let pump_test: TestPump = serde_json::from_slice(&actual_data)?; | ||||
| //     let mut board = BOARD_ACCESS.lock().unwrap(); | ||||
| // | ||||
| //     let config = &board.board_hal.get_config().plants[pump_test.pump].clone(); | ||||
| //     let pump_result = do_secure_pump(&mut board, pump_test.pump, config, false)?; | ||||
| //     board.board_hal.pump(pump_test.pump, false)?; | ||||
| //     anyhow::Ok(Some(serde_json::to_string(&pump_result)?)) | ||||
| // } | ||||
| // | ||||
| // | ||||
| // fn night_lamp_test( | ||||
| //     request: &mut Request<&mut EspHttpConnection>, | ||||
| // ) -> Result<Option<std::string::String>, anyhow::Error> { | ||||
| //     let actual_data = read_up_to_bytes_from_request(request, None)?; | ||||
| //     let light_command: NightLampCommand = serde_json::from_slice(&actual_data)?; | ||||
| //     let mut board = BOARD_ACCESS.lock().unwrap(); | ||||
| //     board.board_hal.light(light_command.active)?; | ||||
| //     anyhow::Ok(None) | ||||
| // } | ||||
| // | ||||
| // fn wifi_scan( | ||||
| //     _request: &mut Request<&mut EspHttpConnection>, | ||||
| // ) -> Result<Option<std::string::String>, anyhow::Error> { | ||||
| //     let mut board = BOARD_ACCESS.lock().unwrap(); | ||||
| //     let scan_result = board.board_hal.get_esp().wifi_scan()?; | ||||
| //     let mut ssids: Vec<&String<32>> = Vec::new(); | ||||
| //     scan_result.iter().for_each(|s| ssids.push(&s.ssid)); | ||||
| //     let ssid_json = serde_json::to_string(&SSIDList { ssids })?; | ||||
| //     log::info!("Sending ssid list {}", &ssid_json); | ||||
| //     anyhow::Ok(Some(ssid_json)) | ||||
| // } | ||||
| // | ||||
|  | ||||
| // | ||||
| // fn ota( | ||||
| //     request: &mut Request<&mut EspHttpConnection>, | ||||
| @@ -403,6 +350,7 @@ impl Handler for HttpHandler { | ||||
|                             "/log_localization" => Some(get_log_localization_config(conn).await), | ||||
|                             "/tank" => Some(tank_info(conn).await), | ||||
|                             "/backup_info" => Some(backup_info(conn).await), | ||||
|                             "/timezones" => Some(get_timezones(conn).await), | ||||
|                             _ => None, | ||||
|                         }; | ||||
|                         match json { | ||||
| @@ -417,6 +365,9 @@ impl Handler for HttpHandler { | ||||
|                         "/set_config" => Some(set_config(conn).await), | ||||
|                         "/time" => Some(write_time(conn).await), | ||||
|                         "/backup_config" => Some(backup_config(conn).await), | ||||
|                         "/pumptest" => Some(pump_test(conn).await), | ||||
|                         "/lamptest" => Some(night_lamp_test(conn).await), | ||||
|                         "/boardtest" => Some(board_test(conn).await), | ||||
|                         "/reboot" => { | ||||
|                             let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|                             board.board_hal.get_esp().set_restart_to_conf(true); | ||||
| @@ -450,6 +401,60 @@ impl Handler for HttpHandler { | ||||
|     } | ||||
| } | ||||
|  | ||||
| async fn get_timezones<T, const N: usize>( | ||||
|     request: &mut Connection<'_, T, N>, | ||||
| ) ->  FatResult<Option<String>> | ||||
| where | ||||
|     T: Read + Write, | ||||
| { | ||||
|     // Get all timezones using chrono-tz | ||||
|     let timezones: Vec<&'static str> = chrono_tz::TZ_VARIANTS.iter().map(|tz| tz.name()).collect(); | ||||
|     let json = serde_json::to_string(&timezones)?; | ||||
|     Ok(Some(json)) | ||||
| } | ||||
|  | ||||
| async fn board_test<T, const N: usize>( | ||||
|     request: &mut Connection<'_, T, N>, | ||||
| ) ->  FatResult<Option<String>> | ||||
| where | ||||
|     T: Read + Write, | ||||
|  { | ||||
|      let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|      board.board_hal.test().await?; | ||||
|      Ok(None) | ||||
|  } | ||||
|  | ||||
| async fn pump_test<T, const N: usize>( | ||||
|     request: &mut Connection<'_, T, N>, | ||||
| ) ->  FatResult<Option<String>> | ||||
| where | ||||
|     T: Read + Write, | ||||
| { | ||||
|     let actual_data = read_up_to_bytes_from_request(request, None).await?; | ||||
|     let pump_test: TestPump = serde_json::from_slice(&actual_data)?; | ||||
|     let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|  | ||||
|     let config = &board.board_hal.get_config().plants[pump_test.pump].clone(); | ||||
|     let pump_result = do_secure_pump(&mut board, pump_test.pump, config, false).await; | ||||
|     //ensure it is disabled before unwrapping | ||||
|     board.board_hal.pump(pump_test.pump, false).await?; | ||||
|  | ||||
|     Ok(Some(serde_json::to_string(&pump_result?)?)) | ||||
| } | ||||
|  | ||||
| async fn night_lamp_test<T, const N: usize>( | ||||
|     request: &mut Connection<'_, T, N>, | ||||
| ) -> FatResult<Option<String>> | ||||
| where | ||||
|     T: Read + Write, | ||||
| { | ||||
|          let actual_data = read_up_to_bytes_from_request(request, None).await?; | ||||
|          let light_command: NightLampCommand = serde_json::from_slice(&actual_data)?; | ||||
|          let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|          board.board_hal.light(light_command.active).await?; | ||||
|          Ok(None) | ||||
| } | ||||
|  | ||||
| async fn get_backup_config<T, const N: usize>( | ||||
|     conn: &mut Connection<'_, T, { N }>, | ||||
| ) -> Result<(), FatError> | ||||
| @@ -777,11 +782,12 @@ async fn get_time<T, const N: usize>( | ||||
|     _request: &mut Connection<'_, T, N>, | ||||
| ) -> FatResult<Option<String>> { | ||||
|     let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|  | ||||
|     let native = esp_time().await.to_rfc3339(); | ||||
|     let conf = board.board_hal.get_config(); | ||||
|     let tz = Tz::from_str(conf.timezone.as_ref().unwrap().as_str()).unwrap(); | ||||
|     let native = esp_time().await.with_timezone(&tz).to_rfc3339(); | ||||
|  | ||||
|     let rtc = match board.board_hal.get_rtc_module().get_rtc_time().await { | ||||
|         Ok(time) => time.to_rfc3339(), | ||||
|         Ok(time) => time.with_timezone(&tz).to_rfc3339(), | ||||
|         Err(err) => { | ||||
|             format!("Error getting time: {}", err) | ||||
|         } | ||||
| @@ -812,77 +818,12 @@ pub async fn httpd(reboot_now: Arc<AtomicBool>, stack: Stack<'static>) { | ||||
|         .expect("TODO: panic message"); | ||||
|     println!("Wait for connection..."); | ||||
|  | ||||
|     // let server_config = Configuration { | ||||
|     //     stack_size: 32768, | ||||
|     //     ..Default::default() | ||||
|     // }; | ||||
|     // let mut server: Box<EspHttpServer<'static>> = | ||||
|     //     Box::new(EspHttpServer::new(&server_config).unwrap()); | ||||
|     // server | ||||
|     //     .fn_handler("/version", Method::Get, |request| { | ||||
|     //         handle_error_to500(request, get_version_web) | ||||
|     //     }) | ||||
|     //     .unwrap(); | ||||
|     // server | ||||
|     //     .fn_handler("/log", Method::Get, |request| { | ||||
|     //         handle_error_to500(request, get_log) | ||||
|     //     }) | ||||
|     //     .unwrap(); | ||||
|     // server | ||||
|     //     .fn_handler("/log_localization", Method::Get, |request| { | ||||
|     //         cors_response(request, 200, &get_log_localization_config().unwrap()) | ||||
|     //     }) | ||||
|     //     .unwrap(); | ||||
|     // server | ||||
|     //     .fn_handler("/battery", Method::Get, |request| { | ||||
|     //         handle_error_to500(request, get_battery_state) | ||||
|     //     }) | ||||
|     //     .unwrap(); | ||||
|     // server | ||||
|     //     .fn_handler("/solar", Method::Get, |request| { | ||||
|     //         handle_error_to500(request, get_solar_state) | ||||
|     //     }) | ||||
|     //     .unwrap(); | ||||
|     // server | ||||
|     //     .fn_handler("/time", Method::Get, |request| { | ||||
|     //         handle_error_to500(request, get_time) | ||||
|     //     }) | ||||
|     //     .unwrap(); | ||||
|     // server | ||||
|     //     .fn_handler("/moisture", Method::Get, |request| { | ||||
|     //         handle_error_to500(request, get_live_moisture) | ||||
|     //     }) | ||||
|     //     .unwrap(); | ||||
|     // server | ||||
|     //     .fn_handler("/time", Method::Post, |request| { | ||||
|     //         handle_error_to500(request, write_time) | ||||
|     //     }) | ||||
|     //     .unwrap(); | ||||
|     // server | ||||
|     //     .fn_handler("/tank", Method::Get, |request| { | ||||
|     //         handle_error_to500(request, tank_info) | ||||
|     //     }) | ||||
|     //     .unwrap(); | ||||
|     // server | ||||
|     //     .fn_handler("/pumptest", Method::Post, |request| { | ||||
|     //         handle_error_to500(request, pump_test) | ||||
|     //     }) | ||||
|     //     .unwrap(); | ||||
|     // server | ||||
|     //     .fn_handler("/lamptest", Method::Post, |request| { | ||||
|     //         handle_error_to500(request, night_lamp_test) | ||||
|     //     }) | ||||
|     //     .unwrap(); | ||||
|     // server | ||||
|     //     .fn_handler("/boardtest", Method::Post, move |_| { | ||||
|     //         BOARD_ACCESS.lock().unwrap().board_hal.test() | ||||
|     //     }) | ||||
|     //     .unwrap(); | ||||
|     // server | ||||
|     //     .fn_handler("/wifiscan", Method::Post, move |request| { | ||||
|     //         handle_error_to500(request, wifi_scan) | ||||
|     //     }) | ||||
|     //     .unwrap(); | ||||
|  | ||||
|     // server | ||||
|     //     .fn_handler("/ota", Method::Post, |request| { | ||||
|     //         handle_error_to500(request, ota) | ||||
| @@ -917,40 +858,7 @@ pub async fn httpd(reboot_now: Arc<AtomicBool>, stack: Stack<'static>) { | ||||
|     //     }) | ||||
|     //     .unwrap(); | ||||
|     // server | ||||
|  | ||||
|     // | ||||
|     // unsafe { vTaskDelay(1) }; | ||||
|     // server | ||||
|     //     .fn_handler("/", Method::Get, move |request| { | ||||
|     //         let mut response = request.into_ok_response()?; | ||||
|     //         response.write(include_bytes!("index.html"))?; | ||||
|     //         anyhow::Ok(()) | ||||
|     //     }) | ||||
|     //     .unwrap(); | ||||
|     // server | ||||
|     //     .fn_handler("/favicon.ico", Method::Get, |request| { | ||||
|     //         request | ||||
|     //             .into_ok_response()? | ||||
|     //             .write(include_bytes!("favicon.ico"))?; | ||||
|     //         anyhow::Ok(()) | ||||
|     //     }) | ||||
|     //     .unwrap(); | ||||
|     // server | ||||
|     //     .fn_handler("/bundle.js", Method::Get, |request| { | ||||
|     //         request | ||||
|     //             .into_ok_response()? | ||||
|     //             .write(include_bytes!("bundle.js"))?; | ||||
|     //         anyhow::Ok(()) | ||||
|     //     }) | ||||
|     //     .unwrap(); | ||||
|     // server | ||||
|     //     .fn_handler("/timezones", Method::Get, move |request| { | ||||
|     //         handle_error_to500(request, get_timezones) | ||||
|     //     }) | ||||
|     //     .unwrap(); | ||||
|     //server | ||||
| } | ||||
| // | ||||
|  | ||||
| async fn handle_json<'a, T, const N: usize>( | ||||
|     conn: &mut Connection<'a, T, N>, | ||||
|   | ||||
| @@ -255,11 +255,12 @@ export class Controller { | ||||
|             method: "POST", | ||||
|             body: json, | ||||
|         }) | ||||
|             .then(response => response.text()) | ||||
|             .then(text => statusCallback(text)) | ||||
|         controller.progressview.removeProgress("set_config") | ||||
|         //load from remote to be clean | ||||
|         controller.downloadConfig() | ||||
|           .then(response => response.text()) | ||||
|           .then(text => statusCallback(text)) | ||||
|           .then( _ => { | ||||
|             controller.progressview.removeProgress("set_config"); | ||||
|             setTimeout(() => { controller.downloadConfig() }, 250) | ||||
|           }) | ||||
|     } | ||||
|  | ||||
|     async backupConfig(json: string): Promise<string> { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user