i2c working, rtc working, eeprom working
This commit is contained in:
		| @@ -111,6 +111,7 @@ embedded-hal-bus = { version = "0.3.0" } | ||||
| onewire = "0.4.0" | ||||
| #strum = { version = "0.27.0", default-feature = false, features = ["derive"] } | ||||
| measurements = "0.11.0" | ||||
| ds323x = "0.6.0" | ||||
|  | ||||
| #json | ||||
| serde = { version = "1.0.219", features = ["derive", "alloc"], default-features = false } | ||||
| @@ -141,6 +142,8 @@ littlefs2 = { version = "0.6.1", features = ["c-stubs", "alloc"] } | ||||
| littlefs2-core = "0.1.1" | ||||
| bytemuck = { version = "1.23.2", features = ["derive", "min_const_generics", "pod_saturating", "extern_crate_alloc"] } | ||||
| deranged = "0.5.3" | ||||
| embassy-embedded-hal = "0.5.0" | ||||
| bincode = { version = "2.0.1", default-features = false, features = ["derive"] } | ||||
|  | ||||
| [patch.crates-io] | ||||
| #bq34z100 = { path = "../../bq34z100_rust" } | ||||
|   | ||||
| @@ -1,8 +1,12 @@ | ||||
| use alloc::format; | ||||
| use alloc::string::{String, ToString}; | ||||
| use core::convert::Infallible; | ||||
| use core::fmt; | ||||
| use core::str::Utf8Error; | ||||
| use embassy_embedded_hal::shared_bus::I2cDeviceError; | ||||
| use embassy_executor::SpawnError; | ||||
| use embassy_sync::mutex::TryLockError; | ||||
| use esp_hal::i2c::master::ConfigError; | ||||
| use esp_wifi::wifi::WifiError; | ||||
| use littlefs2_core::PathError; | ||||
| use onewire::Error; | ||||
| @@ -41,6 +45,15 @@ pub enum FatError { | ||||
|     PartitionError { | ||||
|         error: esp_bootloader_esp_idf::partitions::Error, | ||||
|     }, | ||||
|     I2CConfigError { | ||||
|         error: ConfigError, | ||||
|     }, | ||||
|     DS323 { | ||||
|         error: String, | ||||
|     }, | ||||
|     Eeprom24x { | ||||
|         error: String, | ||||
|     }, | ||||
| } | ||||
|  | ||||
| pub type FatResult<T> = Result<T, FatError>; | ||||
| @@ -65,6 +78,9 @@ impl fmt::Display for FatError { | ||||
|             FatError::NoBatteryMonitor => { | ||||
|                 write!(f, "No Battery Monitor") | ||||
|             } | ||||
|             FatError::I2CConfigError { error } => write!(f, "I2CConfigError {:?}", error), | ||||
|             FatError::DS323 { error } => write!(f, "DS323 {:?}", error), | ||||
|             FatError::Eeprom24x { error } => write!(f, "Eeprom24x {:?}", error), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -150,3 +166,57 @@ impl From<esp_bootloader_esp_idf::partitions::Error> for FatError { | ||||
|         FatError::PartitionError { error: value } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Utf8Error> for FatError { | ||||
|     fn from(value: Utf8Error) -> Self { | ||||
|         FatError::String { | ||||
|             error: value.to_string(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<E: core::fmt::Debug> From<edge_http::io::Error<E>> for FatError { | ||||
|     fn from(value: edge_http::io::Error<E>) -> Self { | ||||
|         FatError::String { | ||||
|             error: format!("HttpIoError {:?}", value), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<E: core::fmt::Debug> From<ds323x::Error<E>> for FatError { | ||||
|     fn from(value: ds323x::Error<E>) -> Self { | ||||
|         FatError::DS323 { | ||||
|             error: format!("Ds323xError {:?}", value), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<E: core::fmt::Debug> From<eeprom24x::Error<E>> for FatError { | ||||
|     fn from(value: eeprom24x::Error<E>) -> Self { | ||||
|         FatError::Eeprom24x { | ||||
|             error: format!("Eeprom24xError {:?}", value), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<bincode::error::DecodeError> for FatError { | ||||
|     fn from(value: bincode::error::DecodeError) -> Self { | ||||
|         FatError::Eeprom24x { | ||||
|             error: format!("Eeprom24xError {:?}", value), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<bincode::error::EncodeError> for FatError { | ||||
|     fn from(value: bincode::error::EncodeError) -> Self { | ||||
|         FatError::Eeprom24x { | ||||
|             error: format!("Eeprom24xError {:?}", value), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<ConfigError> for FatError { | ||||
|     fn from(value: ConfigError) -> Self { | ||||
|         FatError::I2CConfigError { error: value } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| use crate::hal::Box; | ||||
| use crate::FatError::{FatError, FatResult}; | ||||
| use alloc::string::String; | ||||
| use async_trait::async_trait; | ||||
|   | ||||
| @@ -5,7 +5,7 @@ use alloc::vec::Vec; | ||||
| use crate::alloc::boxed::Box; | ||||
| use crate::hal::water::TankSensor; | ||||
| use crate::hal::{BoardInteraction, FreePeripherals, Sensor}; | ||||
| use crate::FatError::FatError; | ||||
| use crate::FatError::{FatError, FatResult}; | ||||
| use crate::{ | ||||
|     bail, | ||||
|     config::PlantControllerConfig, | ||||
| @@ -32,11 +32,15 @@ impl RTCModuleInteraction for NoRTC { | ||||
|         bail!("Please configure board revision") | ||||
|     } | ||||
|  | ||||
|     async fn get_backup_config(&mut self) -> Result<Vec<u8>, FatError> { | ||||
|     async fn get_backup_config(&mut self, chunk: usize) -> FatResult<([u8; 32], usize, u16)> { | ||||
|         bail!("Please configure board revision") | ||||
|     } | ||||
|  | ||||
|     async fn backup_config(&mut self, _bytes: &[u8]) -> Result<(), FatError> { | ||||
|     async fn backup_config(&mut self, offset: usize, bytes: &[u8]) -> FatResult<()> { | ||||
|         bail!("Please configure board revision") | ||||
|     } | ||||
|  | ||||
|     async fn backup_config_finalize(&mut self, crc: u16, length: usize) -> FatResult<()> { | ||||
|         bail!("Please configure board revision") | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -2,13 +2,13 @@ pub(crate) mod battery; | ||||
| pub mod esp; | ||||
| mod initial_hal; | ||||
| mod little_fs2storage_adapter; | ||||
| mod rtc; | ||||
| pub(crate) mod rtc; | ||||
| mod v4_hal; | ||||
| mod water; | ||||
| //mod water; | ||||
|  | ||||
| use crate::alloc::string::ToString; | ||||
| use crate::hal::rtc::RTCModuleInteraction; | ||||
| use crate::hal::rtc::{DS3231Module, RTCModuleInteraction}; | ||||
| use esp_hal::peripherals::Peripherals; | ||||
| use esp_hal::peripherals::GPIO0; | ||||
| use esp_hal::peripherals::GPIO1; | ||||
| @@ -54,10 +54,20 @@ use alloc::format; | ||||
| use alloc::sync::Arc; | ||||
| use async_trait::async_trait; | ||||
| use chrono::{DateTime, FixedOffset, Utc}; | ||||
| use core::cell::RefCell; | ||||
| use ds323x::ic::DS3231; | ||||
| use ds323x::interface::I2cInterface; | ||||
| use ds323x::{DateTimeAccess, Ds323x}; | ||||
| use eeprom24x::addr_size::TwoBytes; | ||||
| use eeprom24x::page_size::B32; | ||||
| use eeprom24x::unique_serial::No; | ||||
| use eeprom24x::{Eeprom24x, SlaveAddr, Storage}; | ||||
| use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_sync::blocking_mutex::{CriticalSectionMutex, NoopMutex}; | ||||
| //use battery::BQ34Z100G1; | ||||
| //use bq34z100::Bq34z100g1Driver; | ||||
| use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||||
| use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; | ||||
| use esp_bootloader_esp_idf::partitions::{ | ||||
|     AppPartitionSubType, DataPartitionSubType, FlashRegion, PartitionEntry, | ||||
| }; | ||||
| @@ -74,10 +84,14 @@ use embassy_sync::once_lock::OnceLock; | ||||
| use esp_alloc as _; | ||||
| use esp_backtrace as _; | ||||
| use esp_bootloader_esp_idf::ota::Slot; | ||||
| use esp_hal::delay::Delay; | ||||
| use esp_hal::i2c::master::{BusTimeout, Config, I2c}; | ||||
| use esp_hal::rng::Rng; | ||||
| use esp_hal::rtc_cntl::{Rtc, SocResetReason}; | ||||
| use esp_hal::system::reset_reason; | ||||
| use esp_hal::time::Rate; | ||||
| use esp_hal::timer::timg::TimerGroup; | ||||
| use esp_hal::Blocking; | ||||
| use esp_storage::FlashStorage; | ||||
| use esp_wifi::{init, EspWifiController}; | ||||
| use littlefs2::fs::{Allocation, Filesystem as lfs2Filesystem}; | ||||
| @@ -90,8 +104,9 @@ pub static TIME_ACCESS: OnceLock<Rtc> = OnceLock::new(); | ||||
| pub const PLANT_COUNT: usize = 8; | ||||
|  | ||||
| const TANK_MULTI_SAMPLE: usize = 11; | ||||
|  | ||||
| //pub static I2C_DRIVER: LazyLock<Mutex<CriticalSectionRawMutex,I2cDriver<'static>>> = LazyLock::new(PlantHal::create_i2c); | ||||
| static I2C_DRIVER: OnceLock< | ||||
|     embassy_sync::blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<I2c<Blocking>>>, | ||||
| > = OnceLock::new(); | ||||
|  | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum Sensor { | ||||
| @@ -127,22 +142,16 @@ pub trait BoardInteraction<'a> { | ||||
|     fn set_config(&mut self, config: PlantControllerConfig); | ||||
|     async fn get_mptt_voltage(&mut self) -> Result<Voltage, FatError>; | ||||
|     async fn get_mptt_current(&mut self) -> Result<Current, FatError>; | ||||
| } | ||||
|  | ||||
| impl dyn BoardInteraction<'_> { | ||||
|     //the counter is just some arbitrary number that increases whenever some progress was made, try to keep the updates < 10 per second for ux reasons | ||||
|     async fn _progress(&mut self, counter: u32) { | ||||
|     async fn progress(&mut self, counter: u32) { | ||||
|         let even = counter % 2 == 0; | ||||
|         let current = counter / (PLANT_COUNT as u32); | ||||
|         for led in 0..PLANT_COUNT { | ||||
|             match self.fault(led, current == led as u32).await { | ||||
|                 Result::Ok(_) => {} | ||||
|                 Err(err) => { | ||||
|                     warn!("Fault on plant {}: {:?}", led, err); | ||||
|                 } | ||||
|             if let Err(err) = self.fault(led, current == led as u32).await { | ||||
|                 warn!("Fault on plant {}: {:?}", led, err); | ||||
|             } | ||||
|         } | ||||
|         let _ = self.general_fault(even.into()); | ||||
|         let _ = self.general_fault(even.into()).await; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -196,7 +205,7 @@ macro_rules! mk_static { | ||||
| const GW_IP_ADDR_ENV: Option<&'static str> = option_env!("GATEWAY_IP"); | ||||
|  | ||||
| impl PlantHal { | ||||
|     // fn create_i2c() -> Mutex<CriticalSectionRawMutex, I2cDriver<'static>> { | ||||
|     //fn create_i2c() -> Mutex<CriticalSectionRawMutex, ()> { | ||||
|     //     let peripherals = unsafe { Peripherals::new() }; | ||||
|     // | ||||
|     //     let config = I2cConfig::new() | ||||
| @@ -210,7 +219,7 @@ impl PlantHal { | ||||
|     //     let sda = peripherals.pins.gpio20.downgrade(); | ||||
|     // | ||||
|     //     Mutex::new(I2cDriver::new(i2c, sda, scl, &config).unwrap()) | ||||
|     // } | ||||
|     //} | ||||
|  | ||||
|     pub async fn create( | ||||
|         spawner: Spawner, | ||||
| @@ -427,28 +436,55 @@ impl PlantHal { | ||||
|         let config = esp.load_config().await; | ||||
|  | ||||
|         log::info!("Init rtc driver"); | ||||
|         // let mut rtc = Ds323x::new_ds3231(MutexDevice::new(&I2C_DRIVER)); | ||||
|         // | ||||
|         // log::info!("Init rtc eeprom driver"); | ||||
|         // let eeprom = { | ||||
|         //     Eeprom24x::new_24x32( | ||||
|         //         MutexDevice::new(&I2C_DRIVER), | ||||
|         //         SlaveAddr::Alternative(true, true, true), | ||||
|         //     ) | ||||
|         // }; | ||||
|         // let rtc_time = rtc.datetime(); | ||||
|         // match rtc_time { | ||||
|         //     OkStd(tt) => { | ||||
|         //         log::info!("Rtc Module reports time at UTC {}", tt); | ||||
|         //     } | ||||
|         //     Err(err) => { | ||||
|         //         log::info!("Rtc Module could not be read {:?}", err); | ||||
|         //     } | ||||
|         // } | ||||
|  | ||||
|         // let storage = Storage::new(eeprom, Delay::new(1000)); | ||||
|         let rtc_module: Box<dyn RTCModuleInteraction + Send> = Box::new(initial_hal::NoRTC {}); | ||||
|         //    Box::new(DS3231Module { rtc, storage }) as Box<dyn RTCModuleInteraction + Send>; | ||||
|         let sda = peripherals.GPIO20; | ||||
|         let scl = peripherals.GPIO19; | ||||
|  | ||||
|         let i2c = I2c::new( | ||||
|             peripherals.I2C0, | ||||
|             Config::default() | ||||
|                 .with_frequency(Rate::from_hz(100)) | ||||
|                 .with_timeout(BusTimeout::Maximum), | ||||
|         )? | ||||
|         .with_scl(scl) | ||||
|         .with_sda(sda); | ||||
|         let i2c_bus: embassy_sync::blocking_mutex::Mutex< | ||||
|             CriticalSectionRawMutex, | ||||
|             RefCell<I2c<Blocking>>, | ||||
|         > = CriticalSectionMutex::new(RefCell::new(i2c)); | ||||
|  | ||||
|         I2C_DRIVER.init(i2c_bus).expect("Could not init i2c driver"); | ||||
|  | ||||
|         let i2c_bus = I2C_DRIVER.get().await; | ||||
|         let rtc_device = I2cDevice::new(&i2c_bus); | ||||
|         let eeprom_device = I2cDevice::new(&i2c_bus); | ||||
|  | ||||
|         let mut rtc: Ds323x< | ||||
|             I2cInterface<I2cDevice<CriticalSectionRawMutex, I2c<Blocking>>>, | ||||
|             DS3231, | ||||
|         > = Ds323x::new_ds3231(rtc_device); | ||||
|  | ||||
|         info!("Init rtc eeprom driver"); | ||||
|         let eeprom = Eeprom24x::new_24x32(eeprom_device, SlaveAddr::Alternative(true, true, true)); | ||||
|         let rtc_time = rtc.datetime(); | ||||
|         match rtc_time { | ||||
|             Ok(tt) => { | ||||
|                 log::info!("Rtc Module reports time at UTC {}", tt); | ||||
|             } | ||||
|             Err(err) => { | ||||
|                 log::info!("Rtc Module could not be read {:?}", err); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         let storage: Storage< | ||||
|             I2cDevice<'static, CriticalSectionRawMutex, I2c<Blocking>>, | ||||
|             B32, | ||||
|             TwoBytes, | ||||
|             No, | ||||
|             Delay, | ||||
|         > = Storage::new(eeprom, Delay::new()); | ||||
|         let rtc_module: Box<dyn RTCModuleInteraction + Send> = | ||||
|             Box::new(DS3231Module { rtc, storage }) as Box<dyn RTCModuleInteraction + Send>; | ||||
|  | ||||
|         let hal = match config { | ||||
|             Result::Ok(config) => { | ||||
|   | ||||
| @@ -1,139 +1,138 @@ | ||||
| use crate::bail; | ||||
| use crate::hal::Box; | ||||
| use crate::FatError::FatResult; | ||||
| use alloc::vec; | ||||
| use alloc::vec::Vec; | ||||
| use async_trait::async_trait; | ||||
| use bincode::config::Configuration; | ||||
| use bincode::{config, Decode, Encode}; | ||||
| use chrono::{DateTime, Utc}; | ||||
| use crc::Digest; | ||||
| use ds323x::ic::DS3231; | ||||
| use ds323x::interface::I2cInterface; | ||||
| use ds323x::{DateTimeAccess, Ds323x}; | ||||
| use eeprom24x::addr_size::TwoBytes; | ||||
| use eeprom24x::page_size::B32; | ||||
| use eeprom24x::unique_serial::No; | ||||
| use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice; | ||||
| use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; | ||||
| use embedded_storage::{ReadStorage, Storage}; | ||||
| use esp_hal::delay::Delay; | ||||
| use esp_hal::i2c::master::I2c; | ||||
| use esp_hal::Blocking; | ||||
| use littlefs2_core::{PathBuf, SeekFrom}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
|  | ||||
| // use crate::hal::Box; | ||||
| // use alloc::vec::Vec; | ||||
| // use anyhow::{anyhow, bail}; | ||||
| // use async_trait::async_trait; | ||||
| // use bincode::config::Configuration; | ||||
| // use bincode::{config, Decode, Encode}; | ||||
| // use chrono::{DateTime, Utc}; | ||||
| // use ds323x::{DateTimeAccess, Ds323x}; | ||||
| // use eeprom24x::addr_size::TwoBytes; | ||||
| // use eeprom24x::page_size::B32; | ||||
| // use eeprom24x::unique_serial::No; | ||||
| // use eeprom24x::Storage; | ||||
| // use embedded_storage::ReadStorage as embedded_storage_ReadStorage; | ||||
| // use embedded_storage::Storage as embedded_storage_Storage; | ||||
| // use serde::{Deserialize, Serialize}; | ||||
| // | ||||
| // const X25: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_IBM_SDLC); | ||||
| // const CONFIG: Configuration = config::standard(); | ||||
| pub const X25: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_IBM_SDLC); | ||||
| const CONFIG: Configuration = config::standard(); | ||||
| // | ||||
| #[async_trait] | ||||
| pub trait RTCModuleInteraction { | ||||
|     async fn get_backup_info(&mut self) -> FatResult<BackupHeader>; | ||||
|     async fn get_backup_config(&mut self) -> FatResult<Vec<u8>>; | ||||
|     async fn backup_config(&mut self, bytes: &[u8]) -> FatResult<()>; | ||||
|     async fn get_backup_config(&mut self, chunk: usize) -> FatResult<([u8; 32], usize, u16)>; | ||||
|     async fn backup_config(&mut self, offset: usize, bytes: &[u8]) -> FatResult<()>; | ||||
|     async fn backup_config_finalize(&mut self, crc: u16, length: usize) -> FatResult<()>; | ||||
|     async fn get_rtc_time(&mut self) -> FatResult<DateTime<Utc>>; | ||||
|     async fn set_rtc_time(&mut self, time: &DateTime<Utc>) -> FatResult<()>; | ||||
| } | ||||
| // | ||||
| // const BACKUP_HEADER_MAX_SIZE: usize = 64; | ||||
| // #[derive(Serialize, Deserialize, PartialEq, Debug, Default, Encode, Decode)] | ||||
| const BACKUP_HEADER_MAX_SIZE: usize = 64; | ||||
|  | ||||
| #[derive(Serialize, Deserialize, PartialEq, Debug, Default, Encode, Decode)] | ||||
| pub struct BackupHeader { | ||||
|     pub timestamp: i64, | ||||
|     crc16: u16, | ||||
|     pub size: u16, | ||||
| } | ||||
| // | ||||
| // pub struct DS3231Module<'a> { | ||||
| //     pub(crate) rtc: | ||||
| //         Ds323x<ds323x::interface::I2cInterface<MutexDevice<'a, I2cDriver<'a>>>, ds323x::ic::DS3231>, | ||||
| // | ||||
| //     pub(crate) storage: Storage<MutexDevice<'a, I2cDriver<'a>>, B32, TwoBytes, No, Delay>, | ||||
| // } | ||||
| // | ||||
| // impl RTCModuleInteraction for DS3231Module<'_> { | ||||
| //     fn get_backup_info(&mut self) -> anyhow::Result<BackupHeader> { | ||||
| //         let mut header_page_buffer = [0_u8; BACKUP_HEADER_MAX_SIZE]; | ||||
| // | ||||
| //         self.storage | ||||
| //             .read(0, &mut header_page_buffer) | ||||
| //             .map_err(|err| anyhow!("Error reading eeprom header {:?}", err))?; | ||||
| // | ||||
| //         let (header, len): (BackupHeader, usize) = | ||||
| //             bincode::decode_from_slice(&header_page_buffer[..], CONFIG)?; | ||||
| // | ||||
| //         log::info!("Raw header is {:?} with size {}", header_page_buffer, len); | ||||
| //         anyhow::Ok(header) | ||||
| //     } | ||||
| // | ||||
| //     fn get_backup_config(&mut self) -> anyhow::Result<Vec<u8>> { | ||||
| //         let mut header_page_buffer = [0_u8; BACKUP_HEADER_MAX_SIZE]; | ||||
| // | ||||
| //         self.storage | ||||
| //             .read(0, &mut header_page_buffer) | ||||
| //             .map_err(|err| anyhow!("Error reading eeprom header {:?}", err))?; | ||||
| //         let (header, _header_size): (BackupHeader, usize) = | ||||
| //             bincode::decode_from_slice(&header_page_buffer[..], CONFIG)?; | ||||
| // | ||||
| //         let mut data_buffer = vec![0_u8; header.size as usize]; | ||||
| //         //read the specified number of bytes after the header | ||||
| //         self.storage | ||||
| //             .read(BACKUP_HEADER_MAX_SIZE as u32, &mut data_buffer) | ||||
| //             .map_err(|err| anyhow!("Error reading eeprom data {:?}", err))?; | ||||
| // | ||||
| //         let checksum = X25.checksum(&data_buffer); | ||||
| //         if checksum != header.crc16 { | ||||
| //             bail!( | ||||
| //                 "Invalid checksum, got {} but expected {}", | ||||
| //                 checksum, | ||||
| //                 header.crc16 | ||||
| //             ); | ||||
| //         } | ||||
| // | ||||
| //         anyhow::Ok(data_buffer) | ||||
| //     } | ||||
| //     fn backup_config(&mut self, bytes: &[u8]) -> anyhow::Result<()> { | ||||
| //         let mut header_page_buffer = [0_u8; BACKUP_HEADER_MAX_SIZE]; | ||||
| // | ||||
| //         let time = self.get_rtc_time()?.timestamp_millis(); | ||||
| //         let checksum = X25.checksum(bytes); | ||||
| // | ||||
| //         let header = BackupHeader { | ||||
| //             crc16: checksum, | ||||
| //             timestamp: time, | ||||
| //             size: bytes.len() as u16, | ||||
| //         }; | ||||
| //         let config = config::standard(); | ||||
| //         let encoded = bincode::encode_into_slice(&header, &mut header_page_buffer, config)?; | ||||
| //         log::info!( | ||||
| //             "Raw header is {:?} with size {}", | ||||
| //             header_page_buffer, | ||||
| //             encoded | ||||
| //         ); | ||||
| //         self.storage | ||||
| //             .write(0, &header_page_buffer) | ||||
| //             .map_err(|err| anyhow!("Error writing header {:?}", err))?; | ||||
| // | ||||
| //         //write rest after the header | ||||
| //         self.storage | ||||
| //             .write(BACKUP_HEADER_MAX_SIZE as u32, &bytes) | ||||
| //             .map_err(|err| anyhow!("Error writing body {:?}", err))?; | ||||
| // | ||||
| //         anyhow::Ok(()) | ||||
| //     } | ||||
| // | ||||
| //     fn get_rtc_time(&mut self) -> anyhow::Result<DateTime<Utc>> { | ||||
| //         match self.rtc.datetime() { | ||||
| //             OkStd(rtc_time) => anyhow::Ok(rtc_time.and_utc()), | ||||
| //             Err(err) => { | ||||
| //                 bail!("Error getting rtc time {:?}", err) | ||||
| //             } | ||||
| //         } | ||||
| //     } | ||||
| // | ||||
| //     fn set_rtc_time(&mut self, time: &DateTime<Utc>) -> anyhow::Result<()> { | ||||
| //         let naive_time = time.naive_utc(); | ||||
| //         match self.rtc.set_datetime(&naive_time) { | ||||
| //             OkStd(_) => anyhow::Ok(()), | ||||
| //             Err(err) => { | ||||
| //                 bail!("Error getting rtc time {:?}", err) | ||||
| //             } | ||||
| //         } | ||||
| //     } | ||||
| // } | ||||
| pub struct DS3231Module { | ||||
|     pub(crate) rtc: Ds323x< | ||||
|         I2cInterface<I2cDevice<'static, CriticalSectionRawMutex, I2c<'static, Blocking>>>, | ||||
|         DS3231, | ||||
|     >, | ||||
|  | ||||
|     pub(crate) storage: eeprom24x::Storage< | ||||
|         I2cDevice<'static, CriticalSectionRawMutex, I2c<'static, Blocking>>, | ||||
|         B32, | ||||
|         TwoBytes, | ||||
|         No, | ||||
|         Delay, | ||||
|     >, | ||||
| } | ||||
|  | ||||
| #[async_trait] | ||||
| impl RTCModuleInteraction for DS3231Module { | ||||
|     async fn get_backup_info(&mut self) -> FatResult<BackupHeader> { | ||||
|         let mut header_page_buffer = [0_u8; BACKUP_HEADER_MAX_SIZE]; | ||||
|  | ||||
|         self.storage.read(0, &mut header_page_buffer)?; | ||||
|  | ||||
|         let (header, len): (BackupHeader, usize) = | ||||
|             bincode::decode_from_slice(&header_page_buffer[..], CONFIG)?; | ||||
|  | ||||
|         log::info!("Raw header is {:?} with size {}", header_page_buffer, len); | ||||
|         Ok(header) | ||||
|     } | ||||
|  | ||||
|     async fn get_backup_config(&mut self, chunk: usize) -> FatResult<([u8; 32], usize, u16)> { | ||||
|         let mut header_page_buffer = [0_u8; BACKUP_HEADER_MAX_SIZE]; | ||||
|  | ||||
|         self.storage.read(0, &mut header_page_buffer)?; | ||||
|         let (header, _header_size): (BackupHeader, usize) = | ||||
|             bincode::decode_from_slice(&header_page_buffer[..], CONFIG)?; | ||||
|  | ||||
|         let mut buf = [0_u8; 32]; | ||||
|         let offset = chunk * buf.len() + BACKUP_HEADER_MAX_SIZE; | ||||
|  | ||||
|         let end: usize = header.size as usize + BACKUP_HEADER_MAX_SIZE; | ||||
|         let current_end = offset + buf.len(); | ||||
|         let chunk_size = if current_end > end { | ||||
|             end - offset | ||||
|         } else { | ||||
|             buf.len() | ||||
|         }; | ||||
|         if chunk_size == 0 { | ||||
|             Ok((buf, 0, header.crc16)) | ||||
|         } else { | ||||
|             self.storage.read(offset as u32, &mut buf)?; | ||||
|             let data = &buf[..chunk_size]; | ||||
|             Ok((buf, chunk_size, header.crc16)) | ||||
|         } | ||||
|     } | ||||
|     async fn backup_config(&mut self, offset: usize, bytes: &[u8]) -> FatResult<()> { | ||||
|         //skip header and write after | ||||
|         self.storage | ||||
|             .write((BACKUP_HEADER_MAX_SIZE + offset) as u32, &bytes)?; | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     async fn backup_config_finalize(&mut self, crc: u16, length: usize) -> FatResult<()> { | ||||
|         let mut header_page_buffer = [0_u8; BACKUP_HEADER_MAX_SIZE]; | ||||
|  | ||||
|         let time = self.get_rtc_time().await?.timestamp_millis(); | ||||
|         let header = BackupHeader { | ||||
|             crc16: crc, | ||||
|             timestamp: time, | ||||
|             size: length as u16, | ||||
|         }; | ||||
|         let config = config::standard(); | ||||
|         let encoded = bincode::encode_into_slice(&header, &mut header_page_buffer, config)?; | ||||
|         log::info!( | ||||
|             "Raw header is {:?} with size {}", | ||||
|             header_page_buffer, | ||||
|             encoded | ||||
|         ); | ||||
|         self.storage.write(0, &header_page_buffer)?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     async fn get_rtc_time(&mut self) -> FatResult<DateTime<Utc>> { | ||||
|         Ok(self.rtc.datetime()?.and_utc()) | ||||
|     } | ||||
|  | ||||
|     async fn set_rtc_time(&mut self, time: &DateTime<Utc>) -> FatResult<()> { | ||||
|         let naive_time = time.naive_utc(); | ||||
|         Ok(self.rtc.set_datetime(&naive_time)?) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -384,10 +384,10 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|     // } | ||||
|  | ||||
|     let mut _water_frozen = false; | ||||
|     let water_temp = board | ||||
|         .board_hal | ||||
|         .get_tank_sensor() | ||||
|         .and_then(async |sensor| sensor.water_temperature_c().await); | ||||
|     let water_temp: FatResult<f32> = match board.board_hal.get_tank_sensor() { | ||||
|         Ok(sensor) => sensor.water_temperature_c().await, | ||||
|         Err(e) => Err(e), | ||||
|     }; | ||||
|  | ||||
|     if let Ok(res) = water_temp { | ||||
|         if res < WATER_FROZEN_THRESH { | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| //offer ota and config mode | ||||
|  | ||||
| use crate::config::PlantControllerConfig; | ||||
| use crate::hal::rtc::X25; | ||||
| use crate::hal::{esp_set_time, esp_time}; | ||||
| use crate::log::LOG_ACCESS; | ||||
| use crate::tank::{determine_tank_state, TankInfo}; | ||||
| @@ -123,56 +124,9 @@ pub struct NightLampCommand { | ||||
| //     anyhow::Ok(Some(json)) | ||||
| // } | ||||
| // | ||||
|  | ||||
| // | ||||
| // fn backup_config( | ||||
| //     request: &mut Request<&mut EspHttpConnection>, | ||||
| // ) -> Result<Option<std::string::String>, anyhow::Error> { | ||||
| //     let all = read_up_to_bytes_from_request(request, Some(3072))?; | ||||
| //     let mut board = BOARD_ACCESS.lock().expect("board access"); | ||||
| // | ||||
| //     //TODO how to handle progress here? prior versions animated the fault leds while running | ||||
| //     board.board_hal.get_rtc_module().backup_config(&all)?; | ||||
| //     anyhow::Ok(Some("saved".to_owned())) | ||||
| // } | ||||
| // | ||||
| // fn get_backup_config( | ||||
| //     _request: &mut Request<&mut EspHttpConnection>, | ||||
| // ) -> Result<Option<std::string::String>, anyhow::Error> { | ||||
| //     let mut board = BOARD_ACCESS.lock().expect("board access"); | ||||
| //     let json = match board.board_hal.get_rtc_module().get_backup_config() { | ||||
| //         Ok(config) => from_utf8(&config)?.to_owned(), | ||||
| //         Err(err) => { | ||||
| //             log::info!("Error get backup config {:?}", err); | ||||
| //             err.to_string() | ||||
| //         } | ||||
| //     }; | ||||
| //     anyhow::Ok(Some(json)) | ||||
| // } | ||||
| // | ||||
| // fn backup_info( | ||||
| //     _request: &mut Request<&mut EspHttpConnection>, | ||||
| // ) -> Result<Option<std::string::String>, anyhow::Error> { | ||||
| //     let mut board = BOARD_ACCESS.lock().expect("Should never fail"); | ||||
| //     let header = board.board_hal.get_rtc_module().get_backup_info(); | ||||
| //     let json = match header { | ||||
| //         Ok(h) => { | ||||
| //             let timestamp = DateTime::from_timestamp_millis(h.timestamp).unwrap(); | ||||
| //             let wbh = WebBackupHeader { | ||||
| //                 timestamp: timestamp.to_rfc3339(), | ||||
| //                 size: h.size, | ||||
| //             }; | ||||
| //             serde_json::to_string(&wbh)? | ||||
| //         } | ||||
| //         Err(err) => { | ||||
| //             let wbh = WebBackupHeader { | ||||
| //                 timestamp: err.to_string(), | ||||
| //                 size: 0, | ||||
| //             }; | ||||
| //             serde_json::to_string(&wbh)? | ||||
| //         } | ||||
| //     }; | ||||
| //     anyhow::Ok(Some(json)) | ||||
| // } | ||||
|  | ||||
| // | ||||
|  | ||||
| // | ||||
| @@ -415,6 +369,10 @@ impl Handler for HttpHandler { | ||||
|                         let buf = get_log(conn).await; | ||||
|                         Some(200) | ||||
|                     } | ||||
|                     "/get_backup_config" => { | ||||
|                         get_backup_config(conn).await?; | ||||
|                         Some(200) | ||||
|                     } | ||||
|                     &_ => { | ||||
|                         let json = match path { | ||||
|                             "/version" => Some(get_version_web(conn).await), | ||||
| @@ -424,7 +382,8 @@ impl Handler for HttpHandler { | ||||
|                             "/get_config" => Some(get_config(conn).await), | ||||
|                             "/files" => Some(list_files(conn).await), | ||||
|                             "/log_localization" => Some(get_log_localization_config(conn).await), | ||||
|                             "/wifiscan" => Some(wifi_scan(conn).await), | ||||
|                             "/tank" => Some(tank_info(conn).await), | ||||
|                             "/backup_info" => Some(backup_info(conn).await), | ||||
|                             _ => None, | ||||
|                         }; | ||||
|                         match json { | ||||
| @@ -438,6 +397,7 @@ impl Handler for HttpHandler { | ||||
|                         "/wifiscan" => Some(wifi_scan(conn).await), | ||||
|                         "/set_config" => Some(set_config(conn).await), | ||||
|                         "/time" => Some(write_time(conn).await), | ||||
|                         "/backup_config" => Some(backup_config(conn).await), | ||||
|                         "/reboot" => { | ||||
|                             let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|                             board.board_hal.get_esp().set_restart_to_conf(true); | ||||
| @@ -471,101 +431,173 @@ impl Handler for HttpHandler { | ||||
|     } | ||||
| } | ||||
|  | ||||
| //     .fn_handler("/file", Method::Get, move |request| { | ||||
| //         let filename = query_param(request.uri(), "filename").unwrap(); | ||||
| //         let file_handle = BOARD_ACCESS | ||||
| //             .lock() | ||||
| //             .unwrap() | ||||
| //             .board_hal | ||||
| //             .get_esp() | ||||
| //             .get_file_handle(&filename, false); | ||||
| //         match file_handle { | ||||
| //             Ok(mut file_handle) => { | ||||
| //                 let headers = [("Access-Control-Allow-Origin", "*")]; | ||||
| //                 let mut response = request.into_response(200, None, &headers)?; | ||||
| //                 const BUFFER_SIZE: usize = 512; | ||||
| //                 let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; | ||||
| //                 let mut total_read: usize = 0; | ||||
| //                 loop { | ||||
| //                     unsafe { vTaskDelay(1) }; | ||||
| //                     let read = std::io::Read::read(&mut file_handle, &mut buffer)?; | ||||
| //                     total_read += read; | ||||
| //                     let to_write = &buffer[0..read]; | ||||
| //                     response.write(to_write)?; | ||||
| //                     if read == 0 { | ||||
| //                         break; | ||||
| //                     } | ||||
| //                 } | ||||
| //                 log::info!("wrote {total_read} for file {filename}"); | ||||
| //                 drop(file_handle); | ||||
| //                 response.flush()?; | ||||
| //             } | ||||
| //             Err(err) => { | ||||
| //                 //todo set headers here for filename to be error | ||||
| //                 let error_text = err.to_string(); | ||||
| //                 log::info!("error handling get file {}", error_text); | ||||
| //                 cors_response(request, 500, &error_text)?; | ||||
| //             } | ||||
| //         } | ||||
| //         anyhow::Ok(()) | ||||
| //     }) | ||||
| //     .unwrap(); | ||||
| // server | ||||
| //     .fn_handler("/file", Method::Post, move |mut request| { | ||||
| //         let filename = query_param(request.uri(), "filename").unwrap(); | ||||
| //         let mut board = BOARD_ACCESS.lock().unwrap(); | ||||
| //         let file_handle = board.board_hal.get_esp().get_file_handle(&filename, true); | ||||
| //         match file_handle { | ||||
| //             //TODO get free filesystem size, check against during write if not to large | ||||
| //             Ok(mut file_handle) => { | ||||
| //                 const BUFFER_SIZE: usize = 512; | ||||
| //                 let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; | ||||
| //                 let mut total_read: usize = 0; | ||||
| //                 let mut lastiter = 0; | ||||
| //                 loop { | ||||
| //                     let iter = (total_read / 1024) % 8; | ||||
| //                     if iter != lastiter { | ||||
| //                         for i in 0..PLANT_COUNT { | ||||
| //                             let _ = board.board_hal.fault(i, iter == i); | ||||
| //                         } | ||||
| //                         lastiter = iter; | ||||
| //                     } | ||||
| // | ||||
| //                     let read = request.read(&mut buffer)?; | ||||
| //                     total_read += read; | ||||
| //                     let to_write = &buffer[0..read]; | ||||
| //                     std::io::Write::write(&mut file_handle, to_write)?; | ||||
| //                     if read == 0 { | ||||
| //                         break; | ||||
| //                     } | ||||
| //                 } | ||||
| //                 cors_response(request, 200, &format!("saved {total_read} bytes"))?; | ||||
| //             } | ||||
| //             Err(err) => { | ||||
| //                 //todo set headers here for filename to be error | ||||
| //                 let error_text = err.to_string(); | ||||
| //                 log::info!("error handling get file {}", error_text); | ||||
| //                 cors_response(request, 500, &error_text)?; | ||||
| //             } | ||||
| //         } | ||||
| //         drop(board); | ||||
| //         anyhow::Ok(()) | ||||
| //     }) | ||||
| //     .unwrap(); | ||||
| async fn get_backup_config<T, const N: usize>( | ||||
|     conn: &mut Connection<'_, T, { N }>, | ||||
| ) -> Result<(), FatError> | ||||
| where | ||||
|     T: Read + Write, | ||||
| { | ||||
|     let mut checksum = X25.digest(); | ||||
|     let mut chunk = 0_usize; | ||||
|     loop { | ||||
|         info!("Chunk {}", chunk); | ||||
|         let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|         board.board_hal.progress(chunk as u32).await; | ||||
|         let read_chunk = board | ||||
|             .board_hal | ||||
|             .get_rtc_module() | ||||
|             .get_backup_config(chunk) | ||||
|             .await?; | ||||
|         let length = read_chunk.1; | ||||
|         info!("Length {}", length); | ||||
|         if length == 0 { | ||||
|             let check = checksum.finalize(); | ||||
|             if check != read_chunk.2 { | ||||
|                 if check != read_chunk.2 { | ||||
|                     conn.initiate_response( | ||||
|                         409, | ||||
|                         Some( | ||||
|                             format!("Checksum mismatch expected {} got {}", read_chunk.2, check) | ||||
|                                 .as_str(), | ||||
|                         ), | ||||
|                         &[], | ||||
|                     ) | ||||
|                     .await?; | ||||
|                 } | ||||
|             } | ||||
|             info!("file request for backup finished"); | ||||
|             break; | ||||
|         } | ||||
|         let data = &read_chunk.0[0..length]; | ||||
|         checksum.update(&data); | ||||
|         if length < read_chunk.0.len() { | ||||
|             let check = checksum.finalize(); | ||||
|             if check != read_chunk.2 { | ||||
|                 conn.initiate_response( | ||||
|                     409, | ||||
|                     Some( | ||||
|                         format!("Checksum mismatch expected {} got {}", read_chunk.2, check) | ||||
|                             .as_str(), | ||||
|                     ), | ||||
|                     &[], | ||||
|                 ) | ||||
|                 .await?; | ||||
|             } | ||||
|             info!("file request for backup finished"); | ||||
|             break; | ||||
|         } | ||||
|         chunk = chunk + 1; | ||||
|     } | ||||
|     conn.initiate_response(200, Some("OK"), &[]).await?; | ||||
|  | ||||
| async fn tank_info<T, const N: usize>( | ||||
|     request: &mut Connection<'_, T, N>, | ||||
|     let mut chunk = 0; | ||||
|     loop { | ||||
|         let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|         board.board_hal.progress(chunk as u32).await; | ||||
|         let read_chunk = board | ||||
|             .board_hal | ||||
|             .get_rtc_module() | ||||
|             .get_backup_config(chunk) | ||||
|             .await?; | ||||
|         let length = read_chunk.1; | ||||
|         if length == 0 { | ||||
|             info!("file request for backup finished"); | ||||
|             break; | ||||
|         } | ||||
|         let data = &read_chunk.0[0..length]; | ||||
|         conn.write_all(data).await?; | ||||
|         if length < read_chunk.0.len() { | ||||
|             info!("file request for backup finished"); | ||||
|             break; | ||||
|         } | ||||
|         chunk = chunk + 1; | ||||
|     } | ||||
|  | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| async fn backup_config<T, const N: usize>( | ||||
|     conn: &mut Connection<'_, T, N>, | ||||
| ) -> FatResult<Option<String>> | ||||
| where | ||||
|     T: Read + Write, | ||||
| { | ||||
|     let mut offset = 0_usize; | ||||
|     let mut buf = [0_u8; 32]; | ||||
|  | ||||
|     let mut checksum = crate::hal::rtc::X25.digest(); | ||||
|  | ||||
|     let mut counter = 0; | ||||
|     loop { | ||||
|         let to_write = conn.read(&mut buf).await?; | ||||
|         if to_write == 0 { | ||||
|             info!("backup finished"); | ||||
|             break; | ||||
|         } else { | ||||
|             let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|             board.board_hal.progress(counter).await; | ||||
|  | ||||
|             counter = counter + 1; | ||||
|             board | ||||
|                 .board_hal | ||||
|                 .get_rtc_module() | ||||
|                 .backup_config(offset, &buf[0..to_write]) | ||||
|                 .await?; | ||||
|             checksum.update(&buf[0..to_write]); | ||||
|         } | ||||
|         offset = offset + to_write; | ||||
|     } | ||||
|  | ||||
|     let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|     board | ||||
|         .board_hal | ||||
|         .get_rtc_module() | ||||
|         .backup_config_finalize(checksum.finalize(), offset) | ||||
|         .await?; | ||||
|     Ok(Some("saved".to_owned())) | ||||
| } | ||||
|  | ||||
| async fn backup_info<T, const N: usize>( | ||||
|     _request: &mut Connection<'_, T, N>, | ||||
| ) -> Result<Option<String>, FatError> | ||||
| where | ||||
|     T: Read + Write, | ||||
| { | ||||
|     let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|     determine_tank_state(&mut board); | ||||
|     //should be multsampled | ||||
|     let header = board.board_hal.get_rtc_module().get_backup_info().await; | ||||
|     let json = match header { | ||||
|         Ok(h) => { | ||||
|             let timestamp = DateTime::from_timestamp_millis(h.timestamp).unwrap(); | ||||
|             let wbh = WebBackupHeader { | ||||
|                 timestamp: timestamp.to_rfc3339(), | ||||
|                 size: h.size, | ||||
|             }; | ||||
|             serde_json::to_string(&wbh)? | ||||
|         } | ||||
|         Err(err) => { | ||||
|             let wbh = WebBackupHeader { | ||||
|                 timestamp: err.to_string(), | ||||
|                 size: 0, | ||||
|             }; | ||||
|             serde_json::to_string(&wbh)? | ||||
|         } | ||||
|     }; | ||||
|     Ok(Some(json)) | ||||
| } | ||||
|  | ||||
| async fn tank_info<T, const N: usize>( | ||||
|     _request: &mut Connection<'_, T, N>, | ||||
| ) -> Result<Option<String>, FatError> | ||||
| where | ||||
|     T: Read + Write, | ||||
| { | ||||
|     let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|     let tank_state = determine_tank_state(&mut board).await; | ||||
|     //should be multisampled | ||||
|     let sensor = board.board_hal.get_tank_sensor()?; | ||||
|  | ||||
|     let water_temp = sensor.water_temperature_c().await?; | ||||
|     Ok(Some(serde_json::to_string(&tank_info.as_mqtt_info( | ||||
|     let water_temp: FatResult<f32> = sensor.water_temperature_c().await; | ||||
|     Ok(Some(serde_json::to_string(&tank_state.as_mqtt_info( | ||||
|         &board.board_hal.get_config().tank, | ||||
|         &water_temp, | ||||
|     ))?)) | ||||
| @@ -892,29 +924,6 @@ pub async fn httpd(reboot_now: Arc<AtomicBool>, stack: Stack<'static>) { | ||||
|     // server | ||||
|  | ||||
|     // | ||||
|     // server | ||||
|     //     .fn_handler("/file", Method::Delete, move |request| { | ||||
|     //         let filename = query_param(request.uri(), "filename").unwrap(); | ||||
|     //         let copy = filename.clone(); | ||||
|     //         let mut board = BOARD_ACCESS.lock().unwrap(); | ||||
|     //         match board.board_hal.get_esp().delete_file(&filename) { | ||||
|     //             Ok(_) => { | ||||
|     //                 let info = format!("Deleted file {copy}"); | ||||
|     //                 cors_response(request, 200, &info)?; | ||||
|     //             } | ||||
|     //             Err(err) => { | ||||
|     //                 let info = format!("Could not delete file {copy} {err:?}"); | ||||
|     //                 cors_response(request, 400, &info)?; | ||||
|     //             } | ||||
|     //         } | ||||
|     //         anyhow::Ok(()) | ||||
|     //     }) | ||||
|     //     .unwrap(); | ||||
|     // server | ||||
|     //     .fn_handler("/file", Method::Options, |request| { | ||||
|     //         cors_response(request, 200, "") | ||||
|     //     }) | ||||
|     //     .unwrap(); | ||||
|     // unsafe { vTaskDelay(1) }; | ||||
|     // server | ||||
|     //     .fn_handler("/", Method::Get, move |request| { | ||||
|   | ||||
| @@ -69,45 +69,44 @@ export class Controller { | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|     getBackupInfo(): Promise<void> { | ||||
|         return fetch(PUBLIC_URL + "/backup_info") | ||||
|             .then(response => response.json()) | ||||
|             .then(json => json as BackupHeader) | ||||
|             .then(header => { | ||||
|                 controller.submitView.setBackupInfo(header) | ||||
|             }) | ||||
|             .catch(error => { | ||||
|                 console.log(error); | ||||
|             }); | ||||
|     async getBackupInfo(): Promise<void> { | ||||
|         try { | ||||
|             const response = await fetch(PUBLIC_URL + "/backup_info"); | ||||
|             const json = await response.json(); | ||||
|             const header = json as BackupHeader; | ||||
|             controller.submitView.setBackupInfo(header); | ||||
|         } catch (error) { | ||||
|             console.log(error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     populateTimezones(): Promise<void> { | ||||
|         return fetch(PUBLIC_URL + '/timezones') | ||||
|             .then(response => response.json()) | ||||
|             .then(json => json as string[]) | ||||
|             .then(timezones => { | ||||
|                 controller.timeView.timezones(timezones) | ||||
|             }) | ||||
|             .catch(error => console.error('Error fetching timezones:', error)); | ||||
|     async populateTimezones(): Promise<void> { | ||||
|         try { | ||||
|             const response = await fetch(PUBLIC_URL + '/timezones'); | ||||
|             const json = await response.json(); | ||||
|             const timezones = json as string[]; | ||||
|             controller.timeView.timezones(timezones); | ||||
|         } catch (error) { | ||||
|             return console.error('Error fetching timezones:', error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     updateFileList(): Promise<void> { | ||||
|         return fetch(PUBLIC_URL + "/files") | ||||
|             .then(response => response.json()) | ||||
|             .then(json => json as FileList) | ||||
|             .then(filelist => { | ||||
|                 controller.fileview.setFileList(filelist, PUBLIC_URL) | ||||
|             }) | ||||
|             .catch(error => { | ||||
|                 console.log(error); | ||||
|             }); | ||||
|     async updateFileList(): Promise<void> { | ||||
|         try { | ||||
|             const response = await fetch(PUBLIC_URL + "/files"); | ||||
|             const json = await response.json(); | ||||
|             const filelist = json as FileList; | ||||
|             controller.fileview.setFileList(filelist, PUBLIC_URL); | ||||
|         } catch (error) { | ||||
|             console.log(error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     uploadFile(file: File, name: string) { | ||||
|         var current = 0; | ||||
|         var max = 100; | ||||
|         let current = 0; | ||||
|         let max = 100; | ||||
|         controller.progressview.addProgress("file_upload", (current / max) * 100, "Uploading File " + name + "(" + current + "/" + max + ")") | ||||
|         var ajax = new XMLHttpRequest(); | ||||
|         const ajax = new XMLHttpRequest(); | ||||
|         ajax.upload.addEventListener("progress", event => { | ||||
|             current = event.loaded / 1000; | ||||
|             max = event.total / 1000; | ||||
| @@ -133,7 +132,7 @@ export class Controller { | ||||
|  | ||||
|     deleteFile(name: string) { | ||||
|         controller.progressview.addIndeterminate("file_delete", "Deleting " + name); | ||||
|         var ajax = new XMLHttpRequest(); | ||||
|         const ajax = new XMLHttpRequest(); | ||||
|         ajax.open("DELETE", PUBLIC_URL + "/file?filename=" + name); | ||||
|         ajax.send(); | ||||
|         ajax.addEventListener("error", () => { | ||||
| @@ -153,50 +152,47 @@ export class Controller { | ||||
|         controller.updateFileList() | ||||
|     } | ||||
|  | ||||
|     updateRTCData(): Promise<void> { | ||||
|         return fetch(PUBLIC_URL + "/time") | ||||
|             .then(response => response.json()) | ||||
|             .then(json => json as GetTime) | ||||
|             .then(time => { | ||||
|                 controller.timeView.update(time.native, time.rtc) | ||||
|             }) | ||||
|             .catch(error => { | ||||
|                 controller.timeView.update("n/a", "n/a") | ||||
|                 console.log(error); | ||||
|             }); | ||||
|     async updateRTCData(): Promise<void> { | ||||
|         try { | ||||
|             const response = await fetch(PUBLIC_URL + "/time"); | ||||
|             const json = await response.json(); | ||||
|             const time = json as GetTime; | ||||
|             controller.timeView.update(time.native, time.rtc); | ||||
|         } catch (error) { | ||||
|             controller.timeView.update("n/a", "n/a"); | ||||
|             console.log(error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     updateBatteryData(): Promise<void> { | ||||
|         return fetch(PUBLIC_URL + "/battery") | ||||
|             .then(response => response.json()) | ||||
|             .then(json => json as BatteryState) | ||||
|             .then(battery => { | ||||
|                 controller.batteryView.update(battery) | ||||
|             }) | ||||
|             .catch(error => { | ||||
|                 controller.batteryView.update(null) | ||||
|                 console.log(error); | ||||
|             }) | ||||
|     async updateBatteryData(): Promise<void> { | ||||
|         try { | ||||
|             const response = await fetch(PUBLIC_URL + "/battery"); | ||||
|             const json = await response.json(); | ||||
|             const battery = json as BatteryState; | ||||
|             controller.batteryView.update(battery); | ||||
|         } catch (error) { | ||||
|             controller.batteryView.update(null); | ||||
|             console.log(error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     updateSolarData(): Promise<void> { | ||||
|         return fetch(PUBLIC_URL + "/solar") | ||||
|             .then(response => response.json()) | ||||
|             .then(json => json as SolarState) | ||||
|             .then(solar => { | ||||
|                 controller.solarView.update(solar) | ||||
|             }) | ||||
|             .catch(error => { | ||||
|                 controller.solarView.update(null) | ||||
|                 console.log(error); | ||||
|             }) | ||||
|     async updateSolarData(): Promise<void> { | ||||
|         try { | ||||
|             const response = await fetch(PUBLIC_URL + "/solar"); | ||||
|             const json = await response.json(); | ||||
|             const solar = json as SolarState; | ||||
|             controller.solarView.update(solar); | ||||
|         } catch (error) { | ||||
|             controller.solarView.update(null); | ||||
|             console.log(error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     uploadNewFirmware(file: File) { | ||||
|         var current = 0; | ||||
|         var max = 100; | ||||
|         let current = 0; | ||||
|         let max = 100; | ||||
|         controller.progressview.addProgress("ota_upload", (current / max) * 100, "Uploading firmeware (" + current + "/" + max + ")") | ||||
|         var ajax = new XMLHttpRequest(); | ||||
|         const ajax = new XMLHttpRequest(); | ||||
|         ajax.upload.addEventListener("progress", event => { | ||||
|             current = event.loaded / 1000; | ||||
|             max = event.total / 1000; | ||||
| @@ -218,15 +214,13 @@ export class Controller { | ||||
|         ajax.send(file); | ||||
|     } | ||||
|  | ||||
|     version(): Promise<void> { | ||||
|     async version(): Promise<void> { | ||||
|         controller.progressview.addIndeterminate("version", "Getting buildVersion") | ||||
|         return fetch(PUBLIC_URL + "/version") | ||||
|             .then(response => response.json()) | ||||
|             .then(json => json as VersionInfo) | ||||
|             .then(versionInfo => { | ||||
|                 controller.progressview.removeProgress("version") | ||||
|                 controller.firmWareView.setVersion(versionInfo); | ||||
|             }) | ||||
|         const response = await fetch(PUBLIC_URL + "/version"); | ||||
|         const json = await response.json(); | ||||
|         const versionInfo = json as VersionInfo; | ||||
|         controller.progressview.removeProgress("version"); | ||||
|         controller.firmWareView.setVersion(versionInfo); | ||||
|     } | ||||
|  | ||||
|     getBackupConfig() { | ||||
| @@ -243,7 +237,7 @@ export class Controller { | ||||
|         controller.progressview.addIndeterminate("get_config", "Downloading Config") | ||||
|         const response = await fetch(PUBLIC_URL + "/get_config"); | ||||
|         const loaded = await response.json(); | ||||
|         var currentConfig = loaded as PlantControllerConfig; | ||||
|         const currentConfig = loaded as PlantControllerConfig; | ||||
|         controller.setInitialConfig(currentConfig); | ||||
|         controller.setConfig(currentConfig); | ||||
|         //sync json view initially | ||||
| @@ -268,12 +262,12 @@ export class Controller { | ||||
|         controller.downloadConfig() | ||||
|     } | ||||
|  | ||||
|     backupConfig(json: string): Promise<string> { | ||||
|         return fetch(PUBLIC_URL + "/backup_config", { | ||||
|     async backupConfig(json: string): Promise<string> { | ||||
|         const response = await fetch(PUBLIC_URL + "/backup_config", { | ||||
|             method: "POST", | ||||
|             body: json, | ||||
|         }) | ||||
|             .then(response => response.text()); | ||||
|         }); | ||||
|         return await response.text(); | ||||
|     } | ||||
|  | ||||
|     syncRTCFromBrowser() { | ||||
| @@ -310,9 +304,9 @@ export class Controller { | ||||
|     } | ||||
|  | ||||
|     testNightLamp(active: boolean) { | ||||
|         var body: NightLampCommand = { | ||||
|         const body: NightLampCommand = { | ||||
|             active: active | ||||
|         } | ||||
|         }; | ||||
|         var pretty = JSON.stringify(body, undefined, 1); | ||||
|         fetch(PUBLIC_URL + "/lamptest", { | ||||
|             method: "POST", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user