i2c working, rtc working, eeprom working
This commit is contained in:
		@@ -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| {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user