remove anyhow
This commit is contained in:
		
							
								
								
									
										152
									
								
								rust/src/FatError.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								rust/src/FatError.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,152 @@
 | 
			
		||||
use alloc::string::{String, ToString};
 | 
			
		||||
use core::convert::Infallible;
 | 
			
		||||
use core::fmt;
 | 
			
		||||
use embassy_executor::SpawnError;
 | 
			
		||||
use embassy_sync::mutex::TryLockError;
 | 
			
		||||
use esp_wifi::wifi::WifiError;
 | 
			
		||||
use littlefs2_core::PathError;
 | 
			
		||||
use onewire::Error;
 | 
			
		||||
 | 
			
		||||
//All error superconstruct
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub enum FatError {
 | 
			
		||||
    OneWireError {
 | 
			
		||||
        error: Error<Infallible>,
 | 
			
		||||
    },
 | 
			
		||||
    String {
 | 
			
		||||
        error: String,
 | 
			
		||||
    },
 | 
			
		||||
    LittleFSError {
 | 
			
		||||
        error: littlefs2_core::Error,
 | 
			
		||||
    },
 | 
			
		||||
    PathError {
 | 
			
		||||
        error: PathError,
 | 
			
		||||
    },
 | 
			
		||||
    TryLockError {
 | 
			
		||||
        error: TryLockError,
 | 
			
		||||
    },
 | 
			
		||||
    WifiError {
 | 
			
		||||
        error: WifiError,
 | 
			
		||||
    },
 | 
			
		||||
    SerdeError {
 | 
			
		||||
        error: serde_json::Error,
 | 
			
		||||
    },
 | 
			
		||||
    PreconditionFailed {
 | 
			
		||||
        error: String,
 | 
			
		||||
    },
 | 
			
		||||
    NoBatteryMonitor,
 | 
			
		||||
    SpawnError {
 | 
			
		||||
        error: SpawnError,
 | 
			
		||||
    },
 | 
			
		||||
    PartitionError {
 | 
			
		||||
        error: esp_bootloader_esp_idf::partitions::Error,
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub type FatResult<T> = Result<T, FatError>;
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for FatError {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            FatError::SpawnError { error } => {
 | 
			
		||||
                write!(f, "SpawnError {:?}", error.to_string())
 | 
			
		||||
            }
 | 
			
		||||
            FatError::OneWireError { error } => write!(f, "OneWireError {:?}", error),
 | 
			
		||||
            FatError::String { error } => write!(f, "{}", error),
 | 
			
		||||
            FatError::LittleFSError { error } => write!(f, "LittleFSError {:?}", error),
 | 
			
		||||
            FatError::PathError { error } => write!(f, "PathError {:?}", error),
 | 
			
		||||
            FatError::TryLockError { error } => write!(f, "TryLockError {:?}", error),
 | 
			
		||||
            FatError::WifiError { error } => write!(f, "WifiError {:?}", error),
 | 
			
		||||
            FatError::SerdeError { error } => write!(f, "SerdeError {:?}", error),
 | 
			
		||||
            FatError::PreconditionFailed { error } => write!(f, "PreconditionFailed {:?}", error),
 | 
			
		||||
            FatError::PartitionError { error } => {
 | 
			
		||||
                write!(f, "PartitionError {:?}", error)
 | 
			
		||||
            }
 | 
			
		||||
            FatError::NoBatteryMonitor => {
 | 
			
		||||
                write!(f, "No Battery Monitor")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! bail {
 | 
			
		||||
    ($msg:literal $(,)?) => {
 | 
			
		||||
        return $crate::FatError::fat_bail($msg)
 | 
			
		||||
    };
 | 
			
		||||
    ($fmt:literal, $($arg:tt)*) => {
 | 
			
		||||
        return $crate::FatError::fat_bail(&alloc::format!($fmt, $($arg)*))
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn fat_bail<X>(message: &str) -> Result<X, FatError> {
 | 
			
		||||
    Err(FatError::String {
 | 
			
		||||
        error: message.to_string(),
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait ContextExt<T> {
 | 
			
		||||
    fn context<C>(self, context: C) -> Result<T, FatError>
 | 
			
		||||
    where
 | 
			
		||||
        C: AsRef<str>;
 | 
			
		||||
}
 | 
			
		||||
impl<T> ContextExt<T> for Option<T> {
 | 
			
		||||
    fn context<C>(self, context: C) -> Result<T, FatError>
 | 
			
		||||
    where
 | 
			
		||||
        C: AsRef<str>,
 | 
			
		||||
    {
 | 
			
		||||
        match self {
 | 
			
		||||
            Some(value) => Ok(value),
 | 
			
		||||
            None => Err(FatError::PreconditionFailed {
 | 
			
		||||
                error: context.as_ref().to_string(),
 | 
			
		||||
            }),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<Error<Infallible>> for FatError {
 | 
			
		||||
    fn from(error: Error<Infallible>) -> Self {
 | 
			
		||||
        FatError::OneWireError { error }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl From<littlefs2_core::Error> for FatError {
 | 
			
		||||
    fn from(value: littlefs2_core::Error) -> Self {
 | 
			
		||||
        FatError::LittleFSError { error: value }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<PathError> for FatError {
 | 
			
		||||
    fn from(value: PathError) -> Self {
 | 
			
		||||
        FatError::PathError { error: value }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<TryLockError> for FatError {
 | 
			
		||||
    fn from(value: TryLockError) -> Self {
 | 
			
		||||
        FatError::TryLockError { error: value }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<WifiError> for FatError {
 | 
			
		||||
    fn from(value: WifiError) -> Self {
 | 
			
		||||
        FatError::WifiError { error: value }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<serde_json::error::Error> for FatError {
 | 
			
		||||
    fn from(value: serde_json::Error) -> Self {
 | 
			
		||||
        FatError::SerdeError { error: value }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<SpawnError> for FatError {
 | 
			
		||||
    fn from(value: SpawnError) -> Self {
 | 
			
		||||
        FatError::SpawnError { error: value }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<esp_bootloader_esp_idf::partitions::Error> for FatError {
 | 
			
		||||
    fn from(value: esp_bootloader_esp_idf::partitions::Error) -> Self {
 | 
			
		||||
        FatError::PartitionError { error: value }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,27 +1,20 @@
 | 
			
		||||
use crate::hal::Box;
 | 
			
		||||
use crate::FatError::{FatError, FatResult};
 | 
			
		||||
use alloc::string::String;
 | 
			
		||||
use async_trait::async_trait;
 | 
			
		||||
use core::error::Error;
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
 | 
			
		||||
#[async_trait]
 | 
			
		||||
pub trait BatteryInteraction {
 | 
			
		||||
    async fn state_charge_percent(&mut self) -> Result<f32, BatteryError>;
 | 
			
		||||
    async fn remaining_milli_ampere_hour(&mut self) -> Result<u16, BatteryError>;
 | 
			
		||||
    async fn max_milli_ampere_hour(&mut self) -> Result<u16, BatteryError>;
 | 
			
		||||
    async fn design_milli_ampere_hour(&mut self) -> Result<u16, BatteryError>;
 | 
			
		||||
    async fn voltage_milli_volt(&mut self) -> Result<u16, BatteryError>;
 | 
			
		||||
    async fn average_current_milli_ampere(&mut self) -> Result<i16, BatteryError>;
 | 
			
		||||
    async fn cycle_count(&mut self) -> Result<u16, BatteryError>;
 | 
			
		||||
    async fn state_health_percent(&mut self) -> Result<u16, BatteryError>;
 | 
			
		||||
    async fn bat_temperature(&mut self) -> Result<u16, BatteryError>;
 | 
			
		||||
    async fn get_battery_state(&mut self) -> Result<BatteryState, BatteryError>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<BatteryError> for anyhow::Error {
 | 
			
		||||
    fn from(err: BatteryError) -> Self {
 | 
			
		||||
        anyhow::anyhow!(err)
 | 
			
		||||
    }
 | 
			
		||||
    async fn state_charge_percent(&mut self) -> FatResult<f32>;
 | 
			
		||||
    async fn remaining_milli_ampere_hour(&mut self) -> FatResult<u16>;
 | 
			
		||||
    async fn max_milli_ampere_hour(&mut self) -> FatResult<u16>;
 | 
			
		||||
    async fn design_milli_ampere_hour(&mut self) -> FatResult<u16>;
 | 
			
		||||
    async fn voltage_milli_volt(&mut self) -> FatResult<u16>;
 | 
			
		||||
    async fn average_current_milli_ampere(&mut self) -> FatResult<i16>;
 | 
			
		||||
    async fn cycle_count(&mut self) -> FatResult<u16>;
 | 
			
		||||
    async fn state_health_percent(&mut self) -> FatResult<u16>;
 | 
			
		||||
    async fn bat_temperature(&mut self) -> FatResult<u16>;
 | 
			
		||||
    async fn get_battery_state(&mut self) -> FatResult<BatteryState>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize)]
 | 
			
		||||
@@ -60,43 +53,43 @@ pub enum BatteryState {
 | 
			
		||||
pub struct NoBatteryMonitor {}
 | 
			
		||||
#[async_trait]
 | 
			
		||||
impl BatteryInteraction for NoBatteryMonitor {
 | 
			
		||||
    async fn state_charge_percent(&mut self) -> Result<f32, BatteryError> {
 | 
			
		||||
        Err(BatteryError::NoBatteryMonitor)
 | 
			
		||||
    async fn state_charge_percent(&mut self) -> FatResult<f32> {
 | 
			
		||||
        Err(FatError::NoBatteryMonitor)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn remaining_milli_ampere_hour(&mut self) -> Result<u16, BatteryError> {
 | 
			
		||||
        Err(BatteryError::NoBatteryMonitor)
 | 
			
		||||
    async fn remaining_milli_ampere_hour(&mut self) -> FatResult<u16> {
 | 
			
		||||
        Err(FatError::NoBatteryMonitor)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn max_milli_ampere_hour(&mut self) -> Result<u16, BatteryError> {
 | 
			
		||||
        Err(BatteryError::NoBatteryMonitor)
 | 
			
		||||
    async fn max_milli_ampere_hour(&mut self) -> FatResult<u16> {
 | 
			
		||||
        Err(FatError::NoBatteryMonitor)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn design_milli_ampere_hour(&mut self) -> Result<u16, BatteryError> {
 | 
			
		||||
        Err(BatteryError::NoBatteryMonitor)
 | 
			
		||||
    async fn design_milli_ampere_hour(&mut self) -> FatResult<u16> {
 | 
			
		||||
        Err(FatError::NoBatteryMonitor)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn voltage_milli_volt(&mut self) -> Result<u16, BatteryError> {
 | 
			
		||||
        Err(BatteryError::NoBatteryMonitor)
 | 
			
		||||
    async fn voltage_milli_volt(&mut self) -> FatResult<u16> {
 | 
			
		||||
        Err(FatError::NoBatteryMonitor)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn average_current_milli_ampere(&mut self) -> Result<i16, BatteryError> {
 | 
			
		||||
        Err(BatteryError::NoBatteryMonitor)
 | 
			
		||||
    async fn average_current_milli_ampere(&mut self) -> FatResult<i16> {
 | 
			
		||||
        Err(FatError::NoBatteryMonitor)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn cycle_count(&mut self) -> Result<u16, BatteryError> {
 | 
			
		||||
        Err(BatteryError::NoBatteryMonitor)
 | 
			
		||||
    async fn cycle_count(&mut self) -> FatResult<u16> {
 | 
			
		||||
        Err(FatError::NoBatteryMonitor)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn state_health_percent(&mut self) -> Result<u16, BatteryError> {
 | 
			
		||||
        Err(BatteryError::NoBatteryMonitor)
 | 
			
		||||
    async fn state_health_percent(&mut self) -> FatResult<u16> {
 | 
			
		||||
        Err(FatError::NoBatteryMonitor)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn bat_temperature(&mut self) -> Result<u16, BatteryError> {
 | 
			
		||||
        Err(BatteryError::NoBatteryMonitor)
 | 
			
		||||
    async fn bat_temperature(&mut self) -> FatResult<u16> {
 | 
			
		||||
        Err(FatError::NoBatteryMonitor)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn get_battery_state(&mut self) -> Result<BatteryState, BatteryError> {
 | 
			
		||||
    async fn get_battery_state(&mut self) -> FatResult<BatteryState> {
 | 
			
		||||
        Ok(BatteryState::Unknown)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,33 +1,31 @@
 | 
			
		||||
use crate::config::{NetworkConfig, PlantControllerConfig};
 | 
			
		||||
use crate::hal::{GW_IP_ADDR_ENV, PLANT_COUNT, TIME_ACCESS};
 | 
			
		||||
use crate::log::{ LogArray, LogMessage, LOG_ACCESS};
 | 
			
		||||
use crate::STAY_ALIVE;
 | 
			
		||||
use anyhow::{anyhow, bail, Context};
 | 
			
		||||
use chrono::{DateTime, FixedOffset, Utc};
 | 
			
		||||
use crate::log::{LogMessage, LOG_ACCESS};
 | 
			
		||||
use crate::{bail, STAY_ALIVE};
 | 
			
		||||
use chrono::{DateTime, Utc};
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
 | 
			
		||||
use crate::hal::little_fs2storage_adapter::LittleFs2Filesystem;
 | 
			
		||||
use crate::FatError::{ContextExt, FatError, FatResult};
 | 
			
		||||
use alloc::string::ToString;
 | 
			
		||||
use alloc::sync::Arc;
 | 
			
		||||
use alloc::{format, string::String, vec::Vec};
 | 
			
		||||
use core::marker::PhantomData;
 | 
			
		||||
use core::net::{IpAddr, Ipv4Addr};
 | 
			
		||||
use core::str::FromStr;
 | 
			
		||||
use embassy_executor::{Spawner};
 | 
			
		||||
use embassy_executor::Spawner;
 | 
			
		||||
use embassy_net::{Ipv4Cidr, Runner, Stack, StackResources, StaticConfigV4};
 | 
			
		||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
 | 
			
		||||
use embassy_sync::mutex::Mutex;
 | 
			
		||||
use embassy_time::{Duration, Instant, Timer};
 | 
			
		||||
use embassy_time::{Duration, Timer};
 | 
			
		||||
use embedded_storage::nor_flash::ReadNorFlash;
 | 
			
		||||
use embedded_storage::Storage;
 | 
			
		||||
use esp_bootloader_esp_idf::ota::{Ota, OtaImageState, Slot};
 | 
			
		||||
use esp_bootloader_esp_idf::partitions::{Error, FlashRegion, PartitionEntry, PartitionTable};
 | 
			
		||||
use esp_bootloader_esp_idf::ota::{Ota, OtaImageState};
 | 
			
		||||
use esp_bootloader_esp_idf::partitions::FlashRegion;
 | 
			
		||||
use esp_hal::gpio::Input;
 | 
			
		||||
use esp_hal::rng::Rng;
 | 
			
		||||
use esp_hal::rtc_cntl::Rtc;
 | 
			
		||||
use esp_hal::rtc_cntl::sleep::RtcSleepConfig;
 | 
			
		||||
use esp_hal::system::software_reset;
 | 
			
		||||
use esp_println::{println};
 | 
			
		||||
use esp_println::println;
 | 
			
		||||
use esp_storage::FlashStorage;
 | 
			
		||||
use esp_wifi::wifi::{
 | 
			
		||||
    AccessPointConfiguration, AccessPointInfo, Configuration, Interfaces, ScanConfig,
 | 
			
		||||
@@ -35,7 +33,7 @@ use esp_wifi::wifi::{
 | 
			
		||||
};
 | 
			
		||||
use littlefs2::fs::Filesystem;
 | 
			
		||||
use littlefs2_core::{DynFile, FileType, PathBuf, SeekFrom};
 | 
			
		||||
use log::{info};
 | 
			
		||||
use log::info;
 | 
			
		||||
 | 
			
		||||
#[esp_hal::ram(rtc_fast, persistent)]
 | 
			
		||||
static mut LAST_WATERING_TIMESTAMP: [i64; PLANT_COUNT] = [0; PLANT_COUNT];
 | 
			
		||||
@@ -113,12 +111,10 @@ macro_rules! mk_static {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Esp<'_> {
 | 
			
		||||
    pub(crate) async fn delete_file(&self, filename: String) -> anyhow::Result<()> {
 | 
			
		||||
    pub(crate) async fn delete_file(&self, filename: String) -> FatResult<()> {
 | 
			
		||||
        let file = PathBuf::try_from(filename.as_str()).unwrap();
 | 
			
		||||
        let access = self.fs.lock().await;
 | 
			
		||||
        access
 | 
			
		||||
            .remove(&*file)
 | 
			
		||||
            .map_err(|err| anyhow!("Could not delete file: {:?}", err))?;
 | 
			
		||||
        access.remove(&*file)?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) async fn write_file(
 | 
			
		||||
@@ -126,11 +122,10 @@ impl Esp<'_> {
 | 
			
		||||
        filename: String,
 | 
			
		||||
        offset: u32,
 | 
			
		||||
        buf: &[u8],
 | 
			
		||||
    ) -> anyhow::Result<()> {
 | 
			
		||||
        let file = PathBuf::try_from(filename.as_str()).unwrap();
 | 
			
		||||
    ) -> Result<(), FatError> {
 | 
			
		||||
        let file = PathBuf::try_from(filename.as_str())?;
 | 
			
		||||
        let access = self.fs.lock().await;
 | 
			
		||||
        info!("write file {} at offset {}", filename, offset);
 | 
			
		||||
        match access.open_file_with_options_and_then(
 | 
			
		||||
        access.open_file_with_options_and_then(
 | 
			
		||||
            |options| options.read(true).write(true).create(true),
 | 
			
		||||
            &*file,
 | 
			
		||||
            |file| {
 | 
			
		||||
@@ -138,51 +133,45 @@ impl Esp<'_> {
 | 
			
		||||
                file.write(buf)?;
 | 
			
		||||
                Ok(())
 | 
			
		||||
            },
 | 
			
		||||
        ) {
 | 
			
		||||
            Ok(_) => Ok(()),
 | 
			
		||||
            Err(err) => {
 | 
			
		||||
                bail!(format!("{err:?}"))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        )?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn get_size(&mut self, filename: String) -> FatResult<usize> {
 | 
			
		||||
        let file = PathBuf::try_from(filename.as_str())?;
 | 
			
		||||
        let access = self.fs.lock().await;
 | 
			
		||||
        let data = access.metadata(&*file)?;
 | 
			
		||||
        Ok(data.len())
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) async fn get_file(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        filename: String,
 | 
			
		||||
        chunk: u32,
 | 
			
		||||
    ) -> anyhow::Result<([u8; 128], usize)> {
 | 
			
		||||
    ) -> FatResult<([u8; 512], usize)> {
 | 
			
		||||
        use littlefs2::io::Error as lfs2Error;
 | 
			
		||||
 | 
			
		||||
        let file = PathBuf::try_from(filename.as_str()).unwrap();
 | 
			
		||||
        let file = PathBuf::try_from(filename.as_str())?;
 | 
			
		||||
        let access = self.fs.lock().await;
 | 
			
		||||
        let mut buf = [0_u8; 128];
 | 
			
		||||
        let mut buf = [0_u8; 512];
 | 
			
		||||
        let mut read = 0;
 | 
			
		||||
        let offset = chunk * 128;
 | 
			
		||||
        info!("read file {} at offset {}", filename, offset);
 | 
			
		||||
        match access.open_file_with_options_and_then(
 | 
			
		||||
        let offset = chunk * buf.len() as u32;
 | 
			
		||||
        access.open_file_with_options_and_then(
 | 
			
		||||
            |options| options.read(true),
 | 
			
		||||
            &*file,
 | 
			
		||||
            |file| {
 | 
			
		||||
                let length = file.len()? as u32;
 | 
			
		||||
                info!("file length {}", length);
 | 
			
		||||
                if length == 0 {
 | 
			
		||||
                    Err(lfs2Error::IO)
 | 
			
		||||
                } else if length > offset {
 | 
			
		||||
                    file.seek(SeekFrom::Start(offset))?;
 | 
			
		||||
                    info!("seek to {}", offset);
 | 
			
		||||
                    read = file.read(&mut buf)?;
 | 
			
		||||
                    info!("read {} bytes", read);
 | 
			
		||||
                    Ok(())
 | 
			
		||||
                } else {
 | 
			
		||||
                    //exactly at end, do nothing
 | 
			
		||||
                    Ok(())
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        ) {
 | 
			
		||||
            Ok(_) => {}
 | 
			
		||||
            Err(err) => {
 | 
			
		||||
                bail!(format!("{err:?}"))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        )?;
 | 
			
		||||
        Ok((buf, read))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -219,7 +208,7 @@ impl Esp<'_> {
 | 
			
		||||
    pub(crate) fn mode_override_pressed(&mut self) -> bool {
 | 
			
		||||
        self.boot_button.is_low()
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) async fn sntp(&mut self, _max_wait_ms: u32) -> anyhow::Result<DateTime<Utc>> {
 | 
			
		||||
    pub(crate) async fn sntp(&mut self, _max_wait_ms: u32) -> FatResult<DateTime<Utc>> {
 | 
			
		||||
        //let sntp = sntp::EspSntp::new_default()?;
 | 
			
		||||
        //let mut counter = 0;
 | 
			
		||||
        //while sntp.get_sync_status() != SyncStatus::Completed {
 | 
			
		||||
@@ -233,19 +222,15 @@ impl Esp<'_> {
 | 
			
		||||
        todo!();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn flash_ota(&mut self) -> anyhow::Result<()> {
 | 
			
		||||
    pub async fn flash_ota(&mut self) -> FatResult<()> {
 | 
			
		||||
        let capacity = self.ota_next.capacity();
 | 
			
		||||
 | 
			
		||||
        bail!("not implemented")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    pub(crate) async fn wifi_scan(&mut self) -> anyhow::Result<Vec<AccessPointInfo>> {
 | 
			
		||||
    pub(crate) async fn wifi_scan(&mut self) -> FatResult<Vec<AccessPointInfo>> {
 | 
			
		||||
        info!("start wifi scan");
 | 
			
		||||
        let mut lock = self
 | 
			
		||||
            .controller
 | 
			
		||||
            .try_lock()
 | 
			
		||||
            .map_err(|_| anyhow!("Could not lock wifi controller, currently in use"))?;
 | 
			
		||||
        let mut lock = self.controller.try_lock()?;
 | 
			
		||||
        info!("start wifi scan lock");
 | 
			
		||||
        let scan_config = ScanConfig {
 | 
			
		||||
            ssid: None,
 | 
			
		||||
@@ -254,10 +239,7 @@ impl Esp<'_> {
 | 
			
		||||
            show_hidden: false,
 | 
			
		||||
            scan_type: ScanTypeConfig::Passive(core::time::Duration::from_secs(2)),
 | 
			
		||||
        };
 | 
			
		||||
        let rv = lock
 | 
			
		||||
            .scan_with_config_async(scan_config)
 | 
			
		||||
            .await
 | 
			
		||||
            .map_err(|err| anyhow!("Could not scan wifi: {:?}", err))?;
 | 
			
		||||
        let rv = lock.scan_with_config_async(scan_config).await?;
 | 
			
		||||
        info!("end wifi scan lock");
 | 
			
		||||
        Ok(rv)
 | 
			
		||||
    }
 | 
			
		||||
@@ -294,7 +276,7 @@ impl Esp<'_> {
 | 
			
		||||
        unsafe { CONSECUTIVE_WATERING_PLANT[plant] }
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn get_restart_to_conf(&mut self) -> bool {
 | 
			
		||||
        unsafe { RESTART_TO_CONF  == 1}
 | 
			
		||||
        unsafe { RESTART_TO_CONF == 1 }
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn set_restart_to_conf(&mut self, to_conf: bool) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
@@ -306,7 +288,7 @@ impl Esp<'_> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) async fn wifi_ap(&mut self) -> anyhow::Result<Stack<'static>> {
 | 
			
		||||
    pub(crate) async fn wifi_ap(&mut self) -> FatResult<Stack<'static>> {
 | 
			
		||||
        let ssid = match self.load_config().await {
 | 
			
		||||
            Ok(config) => config.network.ap_ssid.as_str().to_string(),
 | 
			
		||||
            Err(_) => "PlantCtrl Emergency Mode".to_string(),
 | 
			
		||||
@@ -358,7 +340,7 @@ impl Esp<'_> {
 | 
			
		||||
            .config_v4()
 | 
			
		||||
            .inspect(|c| println!("ipv4 config: {c:?}"));
 | 
			
		||||
 | 
			
		||||
        anyhow::Ok(stack.clone())
 | 
			
		||||
        Ok(stack.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn deep_sleep(&mut self, duration_in_ms: u64) -> ! {
 | 
			
		||||
@@ -376,7 +358,6 @@ impl Esp<'_> {
 | 
			
		||||
        if duration_in_ms == 0 {
 | 
			
		||||
            software_reset();
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
            loop {
 | 
			
		||||
                info!("todo deepsleep")
 | 
			
		||||
            }
 | 
			
		||||
@@ -390,11 +371,19 @@ impl Esp<'_> {
 | 
			
		||||
        //};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) async fn wifi(&mut self, network_config: &NetworkConfig) -> anyhow::Result<IpInfo> {
 | 
			
		||||
        let _ssid = network_config
 | 
			
		||||
            .ssid
 | 
			
		||||
            .clone()
 | 
			
		||||
            .ok_or(anyhow!("No ssid configured"))?;
 | 
			
		||||
    pub(crate) async fn wifi(&mut self, network_config: &NetworkConfig) -> FatResult<IpInfo> {
 | 
			
		||||
        let _ssid = network_config.ssid.clone();
 | 
			
		||||
        match &_ssid {
 | 
			
		||||
            Some(ssid) => {
 | 
			
		||||
                if ssid.is_empty() {
 | 
			
		||||
                    bail!("Wifi ssid was empty")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            None => {
 | 
			
		||||
                bail!("Wifi ssid was empty")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        let _ssid = _ssid.unwrap();
 | 
			
		||||
        let _password = network_config.password.clone();
 | 
			
		||||
        let _max_wait = network_config.max_wait;
 | 
			
		||||
        bail!("todo")
 | 
			
		||||
@@ -452,59 +441,32 @@ impl Esp<'_> {
 | 
			
		||||
        // log(LogMessage::WifiInfo, 0, 0, "", &format!("{address:?}"));
 | 
			
		||||
        // anyhow::Ok(address)
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) async fn load_config(&mut self) -> anyhow::Result<PlantControllerConfig> {
 | 
			
		||||
    pub(crate) async fn load_config(&mut self) -> FatResult<PlantControllerConfig> {
 | 
			
		||||
        let cfg = PathBuf::try_from(CONFIG_FILE).unwrap();
 | 
			
		||||
        match self.fs.lock().await.read::<4096>(&cfg) {
 | 
			
		||||
            Ok(data) => {
 | 
			
		||||
                let config: PlantControllerConfig = serde_json::from_slice(&data)?;
 | 
			
		||||
                anyhow::Ok(config)
 | 
			
		||||
            }
 | 
			
		||||
            Err(err) => {
 | 
			
		||||
                bail!(format!("{err:?}"))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        let data = self.fs.lock().await.read::<4096>(&cfg)?;
 | 
			
		||||
        let config: PlantControllerConfig = serde_json::from_slice(&data)?;
 | 
			
		||||
        return Ok(config);
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) async fn save_config(&mut self, config: Vec<u8>) -> anyhow::Result<()> {
 | 
			
		||||
    pub(crate) async fn save_config(&mut self, config: Vec<u8>) -> FatResult<()> {
 | 
			
		||||
        let filesystem = self.fs.lock().await;
 | 
			
		||||
        let cfg = PathBuf::try_from(CONFIG_FILE).unwrap();
 | 
			
		||||
 | 
			
		||||
        match filesystem.write(&cfg, &*config) {
 | 
			
		||||
            Ok(_) => {}
 | 
			
		||||
            Err(err) => {
 | 
			
		||||
                bail!(format!("{err:?}"))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        anyhow::Ok(())
 | 
			
		||||
        let cfg = PathBuf::try_from(CONFIG_FILE)?;
 | 
			
		||||
        filesystem.write(&cfg, &*config)?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    async fn file_system_size(&mut self) -> anyhow::Result<FileSystemSizeInfo> {
 | 
			
		||||
        bail!("fail");
 | 
			
		||||
        // let storage = CString::new(Self::SPIFFS_PARTITION_NAME)?;
 | 
			
		||||
        // let mut total_size = 0;
 | 
			
		||||
        // let mut used_size = 0;
 | 
			
		||||
        // unsafe {
 | 
			
		||||
        //     esp_idf_sys::esp!(esp_spiffs_info(
 | 
			
		||||
        //         storage.as_ptr(),
 | 
			
		||||
        //         &mut total_size,
 | 
			
		||||
        //         &mut used_size
 | 
			
		||||
        //     ))?;
 | 
			
		||||
        // }
 | 
			
		||||
        // anyhow::Ok(FileSystemSizeInfo {
 | 
			
		||||
        //     total_size,
 | 
			
		||||
        //     used_size,
 | 
			
		||||
        //     free_size: total_size - used_size,
 | 
			
		||||
        // })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) async fn list_files(&self) -> anyhow::Result<FileList> {
 | 
			
		||||
    pub(crate) async fn list_files(&self) -> FatResult<FileList> {
 | 
			
		||||
        let path = PathBuf::new();
 | 
			
		||||
 | 
			
		||||
        let fs = self.fs.lock().await;
 | 
			
		||||
        let free_size = fs.available_space()?;
 | 
			
		||||
        let total_size = fs.total_space();
 | 
			
		||||
 | 
			
		||||
        let mut result = FileList {
 | 
			
		||||
            total: 0,
 | 
			
		||||
            used: 0,
 | 
			
		||||
            total: total_size,
 | 
			
		||||
            used: total_size - free_size,
 | 
			
		||||
            files: Vec::new(),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        match self.fs.lock().await.read_dir_and_then(&path, |dir| {
 | 
			
		||||
        fs.read_dir_and_then(&path, |dir| {
 | 
			
		||||
            for entry in dir {
 | 
			
		||||
                let e = entry?;
 | 
			
		||||
                if e.file_type() == FileType::File {
 | 
			
		||||
@@ -514,29 +476,11 @@ impl Esp<'_> {
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Result::Ok(())
 | 
			
		||||
        }) {
 | 
			
		||||
            Ok(_) => {}
 | 
			
		||||
            Err(err) => {
 | 
			
		||||
                bail!(format!("{err:?}"))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
            Ok(())
 | 
			
		||||
        })?;
 | 
			
		||||
        Ok(result)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // pub(crate) async fn get_file_handle(
 | 
			
		||||
    //     &self,
 | 
			
		||||
    //     filename: &str,
 | 
			
		||||
    //     write: bool,
 | 
			
		||||
    // ) -> anyhow::Result<File> {
 | 
			
		||||
    //     let filepath = Path::new(Self::BASE_PATH).join(Path::new(filename));
 | 
			
		||||
    //     anyhow::Ok(if write {
 | 
			
		||||
    //         File::create(filepath)?
 | 
			
		||||
    //     } else {
 | 
			
		||||
    //         File::open(filepath)?
 | 
			
		||||
    //     })
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    pub(crate) async fn init_rtc_deepsleep_memory(
 | 
			
		||||
        &self,
 | 
			
		||||
        init_rtc_store: bool,
 | 
			
		||||
@@ -558,22 +502,28 @@ impl Esp<'_> {
 | 
			
		||||
                if to_config_mode {
 | 
			
		||||
                    RESTART_TO_CONF = 1;
 | 
			
		||||
                }
 | 
			
		||||
                LOG_ACCESS.lock().await.log(
 | 
			
		||||
                    LogMessage::RestartToConfig,
 | 
			
		||||
                    RESTART_TO_CONF as u32,
 | 
			
		||||
                    0,
 | 
			
		||||
                    "",
 | 
			
		||||
                    "",
 | 
			
		||||
                ).await
 | 
			
		||||
                ;
 | 
			
		||||
                LOG_ACCESS.lock().await.log(
 | 
			
		||||
                    LogMessage::LowVoltage,
 | 
			
		||||
                    LOW_VOLTAGE_DETECTED as u32,
 | 
			
		||||
                    0,
 | 
			
		||||
                    "",
 | 
			
		||||
                    "",
 | 
			
		||||
                ).await
 | 
			
		||||
                ;
 | 
			
		||||
                LOG_ACCESS
 | 
			
		||||
                    .lock()
 | 
			
		||||
                    .await
 | 
			
		||||
                    .log(
 | 
			
		||||
                        LogMessage::RestartToConfig,
 | 
			
		||||
                        RESTART_TO_CONF as u32,
 | 
			
		||||
                        0,
 | 
			
		||||
                        "",
 | 
			
		||||
                        "",
 | 
			
		||||
                    )
 | 
			
		||||
                    .await;
 | 
			
		||||
                LOG_ACCESS
 | 
			
		||||
                    .lock()
 | 
			
		||||
                    .await
 | 
			
		||||
                    .log(
 | 
			
		||||
                        LogMessage::LowVoltage,
 | 
			
		||||
                        LOW_VOLTAGE_DETECTED as u32,
 | 
			
		||||
                        0,
 | 
			
		||||
                        "",
 | 
			
		||||
                        "",
 | 
			
		||||
                    )
 | 
			
		||||
                    .await;
 | 
			
		||||
                for i in 0..PLANT_COUNT {
 | 
			
		||||
                    log::info!(
 | 
			
		||||
                        "LAST_WATERING_TIMESTAMP[{}] = UTC {}",
 | 
			
		||||
@@ -592,7 +542,7 @@ impl Esp<'_> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) async fn mqtt(&mut self, network_config: &NetworkConfig) -> anyhow::Result<()> {
 | 
			
		||||
    pub(crate) async fn mqtt(&mut self, network_config: &NetworkConfig) -> FatResult<()> {
 | 
			
		||||
        let base_topic = network_config
 | 
			
		||||
            .base_topic
 | 
			
		||||
            .as_ref()
 | 
			
		||||
@@ -756,11 +706,7 @@ impl Esp<'_> {
 | 
			
		||||
        // }
 | 
			
		||||
        // bail!("Mqtt did not fire connection callback in time");
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) async fn mqtt_publish(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        _subtopic: &str,
 | 
			
		||||
        _message: &[u8],
 | 
			
		||||
    ) -> anyhow::Result<()> {
 | 
			
		||||
    pub(crate) async fn mqtt_publish(&mut self, _subtopic: &str, _message: &[u8]) -> FatResult<()> {
 | 
			
		||||
        bail!("todo");
 | 
			
		||||
        //
 | 
			
		||||
        // if self.mqtt_client.is_none() {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,16 +3,17 @@ use crate::hal::rtc::{BackupHeader, RTCModuleInteraction};
 | 
			
		||||
use alloc::vec::Vec;
 | 
			
		||||
//use crate::hal::water::TankSensor;
 | 
			
		||||
use crate::alloc::boxed::Box;
 | 
			
		||||
use crate::hal::water::TankSensor;
 | 
			
		||||
use crate::hal::{BoardInteraction, FreePeripherals, Sensor};
 | 
			
		||||
use crate::FatError::FatError;
 | 
			
		||||
use crate::{
 | 
			
		||||
    bail,
 | 
			
		||||
    config::PlantControllerConfig,
 | 
			
		||||
    hal::battery::{BatteryInteraction, NoBatteryMonitor},
 | 
			
		||||
};
 | 
			
		||||
use anyhow::{bail, Result};
 | 
			
		||||
use async_trait::async_trait;
 | 
			
		||||
use chrono::{DateTime, Utc};
 | 
			
		||||
use esp_hal::gpio::{Level, Output, OutputConfig};
 | 
			
		||||
use log::info;
 | 
			
		||||
use measurements::{Current, Voltage};
 | 
			
		||||
 | 
			
		||||
pub struct Initial<'a> {
 | 
			
		||||
@@ -23,27 +24,27 @@ pub struct Initial<'a> {
 | 
			
		||||
    pub rtc: Box<dyn RTCModuleInteraction + Send>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct NoRTC {}
 | 
			
		||||
pub(crate) struct NoRTC {}
 | 
			
		||||
 | 
			
		||||
#[async_trait]
 | 
			
		||||
impl RTCModuleInteraction for NoRTC {
 | 
			
		||||
    async fn get_backup_info(&mut self) -> Result<BackupHeader> {
 | 
			
		||||
    async fn get_backup_info(&mut self) -> Result<BackupHeader, FatError> {
 | 
			
		||||
        bail!("Please configure board revision")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn get_backup_config(&mut self) -> Result<Vec<u8>> {
 | 
			
		||||
    async fn get_backup_config(&mut self) -> Result<Vec<u8>, FatError> {
 | 
			
		||||
        bail!("Please configure board revision")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn backup_config(&mut self, _bytes: &[u8]) -> Result<()> {
 | 
			
		||||
    async fn backup_config(&mut self, _bytes: &[u8]) -> Result<(), FatError> {
 | 
			
		||||
        bail!("Please configure board revision")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn get_rtc_time(&mut self) -> Result<DateTime<Utc>> {
 | 
			
		||||
    async fn get_rtc_time(&mut self) -> Result<DateTime<Utc>, FatError> {
 | 
			
		||||
        bail!("Please configure board revision")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn set_rtc_time(&mut self, _time: &DateTime<Utc>) -> Result<()> {
 | 
			
		||||
    async fn set_rtc_time(&mut self, _time: &DateTime<Utc>) -> Result<(), FatError> {
 | 
			
		||||
        bail!("Please configure board revision")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -52,7 +53,7 @@ pub(crate) fn create_initial_board(
 | 
			
		||||
    free_pins: FreePeripherals<'static>,
 | 
			
		||||
    config: PlantControllerConfig,
 | 
			
		||||
    esp: Esp<'static>,
 | 
			
		||||
) -> Result<Box<dyn BoardInteraction<'static> + Send>> {
 | 
			
		||||
) -> Result<Box<dyn BoardInteraction<'static> + Send>, FatError> {
 | 
			
		||||
    log::info!("Start initial");
 | 
			
		||||
    let general_fault = Output::new(free_pins.gpio23, Level::Low, OutputConfig::default());
 | 
			
		||||
    let v = Initial {
 | 
			
		||||
@@ -67,9 +68,9 @@ pub(crate) fn create_initial_board(
 | 
			
		||||
 | 
			
		||||
#[async_trait]
 | 
			
		||||
impl<'a> BoardInteraction<'a> for Initial<'a> {
 | 
			
		||||
    // fn get_tank_sensor(&mut self) -> Option<&mut TankSensor<'a>> {
 | 
			
		||||
    //     None
 | 
			
		||||
    // }
 | 
			
		||||
    fn get_tank_sensor(&mut self) -> Result<&mut TankSensor<'a>, FatError> {
 | 
			
		||||
        bail!("Please configure board revision")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_esp(&mut self) -> &mut Esp<'a> {
 | 
			
		||||
        &mut self.esp
 | 
			
		||||
@@ -87,7 +88,7 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
 | 
			
		||||
        &mut self.rtc
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn set_charge_indicator(&mut self, _charging: bool) -> Result<()> {
 | 
			
		||||
    fn set_charge_indicator(&mut self, _charging: bool) -> Result<(), FatError> {
 | 
			
		||||
        bail!("Please configure board revision")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -97,23 +98,27 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
 | 
			
		||||
    fn is_day(&self) -> bool {
 | 
			
		||||
        false
 | 
			
		||||
    }
 | 
			
		||||
    fn light(&mut self, _enable: bool) -> Result<()> {
 | 
			
		||||
    fn light(&mut self, _enable: bool) -> Result<(), FatError> {
 | 
			
		||||
        bail!("Please configure board revision")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn pump(&mut self, _plant: usize, _enable: bool) -> Result<()> {
 | 
			
		||||
    async fn pump(&mut self, _plant: usize, _enable: bool) -> Result<(), FatError> {
 | 
			
		||||
        bail!("Please configure board revision")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn pump_current(&mut self, _plant: usize) -> Result<Current> {
 | 
			
		||||
    async fn pump_current(&mut self, _plant: usize) -> Result<Current, FatError> {
 | 
			
		||||
        bail!("Please configure board revision")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn fault(&mut self, _plant: usize, _enable: bool) -> Result<()> {
 | 
			
		||||
    async fn fault(&mut self, _plant: usize, _enable: bool) -> Result<(), FatError> {
 | 
			
		||||
        bail!("Please configure board revision")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn measure_moisture_hz(&mut self, _plant: usize, _sensor: Sensor) -> Result<f32> {
 | 
			
		||||
    async fn measure_moisture_hz(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        _plant: usize,
 | 
			
		||||
        _sensor: Sensor,
 | 
			
		||||
    ) -> Result<f32, FatError> {
 | 
			
		||||
        bail!("Please configure board revision")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -121,7 +126,7 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
 | 
			
		||||
        self.general_fault.set_level(enable.into());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn test(&mut self) -> Result<()> {
 | 
			
		||||
    async fn test(&mut self) -> Result<(), FatError> {
 | 
			
		||||
        bail!("Please configure board revision")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -129,11 +134,11 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
 | 
			
		||||
        self.config = config;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn get_mptt_voltage(&mut self) -> Result<Voltage> {
 | 
			
		||||
    async fn get_mptt_voltage(&mut self) -> Result<Voltage, FatError> {
 | 
			
		||||
        bail!("Please configure board revision")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn get_mptt_current(&mut self) -> Result<Current> {
 | 
			
		||||
    async fn get_mptt_current(&mut self) -> Result<Current, FatError> {
 | 
			
		||||
        bail!("Please configure board revision")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,10 +4,9 @@ use esp_storage::FlashStorage;
 | 
			
		||||
use littlefs2::consts::U512 as lfsCache;
 | 
			
		||||
use littlefs2::consts::U512 as lfsLookahead;
 | 
			
		||||
use littlefs2::driver::Storage as lfs2Storage;
 | 
			
		||||
use littlefs2::fs::Filesystem as lfs2Filesystem;
 | 
			
		||||
use littlefs2::io::Error as lfs2Error;
 | 
			
		||||
use littlefs2::io::Result as lfs2Result;
 | 
			
		||||
use log::{error, info};
 | 
			
		||||
use log::error;
 | 
			
		||||
 | 
			
		||||
pub struct LittleFs2Filesystem {
 | 
			
		||||
    pub(crate) storage: &'static mut FlashRegion<'static, FlashStorage>,
 | 
			
		||||
@@ -27,7 +26,7 @@ impl lfs2Storage for LittleFs2Filesystem {
 | 
			
		||||
        assert_eq!(off % read_size, 0);
 | 
			
		||||
        assert_eq!(buf.len() % read_size, 0);
 | 
			
		||||
        match self.storage.read(off as u32, buf) {
 | 
			
		||||
            anyhow::Result::Ok(..) => lfs2Result::Ok(buf.len()),
 | 
			
		||||
            Ok(..) => Ok(buf.len()),
 | 
			
		||||
            Err(err) => {
 | 
			
		||||
                error!("Littlefs2Filesystem read error: {:?}", err);
 | 
			
		||||
                Err(lfs2Error::IO)
 | 
			
		||||
@@ -36,16 +35,11 @@ impl lfs2Storage for LittleFs2Filesystem {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn write(&mut self, off: usize, data: &[u8]) -> lfs2Result<usize> {
 | 
			
		||||
        info!(
 | 
			
		||||
            "Littlefs2Filesystem write at offset {} with len {}",
 | 
			
		||||
            off,
 | 
			
		||||
            data.len()
 | 
			
		||||
        );
 | 
			
		||||
        let write_size: usize = Self::WRITE_SIZE;
 | 
			
		||||
        assert_eq!(off % write_size, 0);
 | 
			
		||||
        assert_eq!(data.len() % write_size, 0);
 | 
			
		||||
        match self.storage.write(off as u32, data) {
 | 
			
		||||
            anyhow::Result::Ok(..) => lfs2Result::Ok(data.len()),
 | 
			
		||||
            Ok(..) => Ok(data.len()),
 | 
			
		||||
            Err(err) => {
 | 
			
		||||
                error!("Littlefs2Filesystem write error: {:?}", err);
 | 
			
		||||
                Err(lfs2Error::IO)
 | 
			
		||||
@@ -54,10 +48,6 @@ impl lfs2Storage for LittleFs2Filesystem {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn erase(&mut self, off: usize, len: usize) -> lfs2Result<usize> {
 | 
			
		||||
        info!(
 | 
			
		||||
            "Littlefs2Filesystem erase at offset {} with len {}",
 | 
			
		||||
            off, len
 | 
			
		||||
        );
 | 
			
		||||
        let block_size: usize = Self::BLOCK_SIZE;
 | 
			
		||||
        debug_assert!(off % block_size == 0);
 | 
			
		||||
        debug_assert!(len % block_size == 0);
 | 
			
		||||
 
 | 
			
		||||
@@ -3,28 +3,55 @@ pub mod esp;
 | 
			
		||||
mod initial_hal;
 | 
			
		||||
mod little_fs2storage_adapter;
 | 
			
		||||
mod rtc;
 | 
			
		||||
mod v4_hal;
 | 
			
		||||
mod water;
 | 
			
		||||
//mod water;
 | 
			
		||||
 | 
			
		||||
use crate::alloc::string::ToString;
 | 
			
		||||
use crate::hal::rtc::RTCModuleInteraction;
 | 
			
		||||
use esp_hal::peripherals::Peripherals;
 | 
			
		||||
use esp_hal::peripherals::GPIO0;
 | 
			
		||||
use esp_hal::peripherals::GPIO1;
 | 
			
		||||
use esp_hal::peripherals::GPIO10;
 | 
			
		||||
use esp_hal::peripherals::GPIO11;
 | 
			
		||||
use esp_hal::peripherals::GPIO12;
 | 
			
		||||
use esp_hal::peripherals::GPIO13;
 | 
			
		||||
use esp_hal::peripherals::GPIO14;
 | 
			
		||||
use esp_hal::peripherals::GPIO15;
 | 
			
		||||
use esp_hal::peripherals::GPIO16;
 | 
			
		||||
use esp_hal::peripherals::GPIO17;
 | 
			
		||||
use esp_hal::peripherals::GPIO18;
 | 
			
		||||
use esp_hal::peripherals::GPIO2;
 | 
			
		||||
use esp_hal::peripherals::GPIO21;
 | 
			
		||||
use esp_hal::peripherals::GPIO22;
 | 
			
		||||
use esp_hal::peripherals::GPIO23;
 | 
			
		||||
use esp_hal::peripherals::GPIO24;
 | 
			
		||||
use esp_hal::peripherals::GPIO25;
 | 
			
		||||
use esp_hal::peripherals::GPIO26;
 | 
			
		||||
use esp_hal::peripherals::GPIO27;
 | 
			
		||||
use esp_hal::peripherals::GPIO28;
 | 
			
		||||
use esp_hal::peripherals::GPIO29;
 | 
			
		||||
use esp_hal::peripherals::GPIO3;
 | 
			
		||||
use esp_hal::peripherals::GPIO30;
 | 
			
		||||
use esp_hal::peripherals::GPIO4;
 | 
			
		||||
use esp_hal::peripherals::GPIO5;
 | 
			
		||||
use esp_hal::peripherals::GPIO6;
 | 
			
		||||
use esp_hal::peripherals::GPIO7;
 | 
			
		||||
use esp_hal::peripherals::GPIO8;
 | 
			
		||||
 | 
			
		||||
//use crate::hal::water::TankSensor;
 | 
			
		||||
use crate::{
 | 
			
		||||
    bail,
 | 
			
		||||
    config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig},
 | 
			
		||||
    hal::{
 | 
			
		||||
        battery::{BatteryInteraction, NoBatteryMonitor},
 | 
			
		||||
        esp::Esp,
 | 
			
		||||
    },
 | 
			
		||||
    log::{LogMessage},
 | 
			
		||||
    log::LogMessage,
 | 
			
		||||
};
 | 
			
		||||
use alloc::boxed::Box;
 | 
			
		||||
use alloc::format;
 | 
			
		||||
use alloc::sync::Arc;
 | 
			
		||||
use core::cell::OnceCell;
 | 
			
		||||
use anyhow::{bail, Ok, Result};
 | 
			
		||||
use async_trait::async_trait;
 | 
			
		||||
use chrono::{DateTime, FixedOffset, Utc};
 | 
			
		||||
use embassy_executor::Spawner;
 | 
			
		||||
@@ -39,6 +66,9 @@ use esp_hal::gpio::{Input, InputConfig, Pull};
 | 
			
		||||
use measurements::{Current, Voltage};
 | 
			
		||||
 | 
			
		||||
use crate::hal::little_fs2storage_adapter::LittleFs2Filesystem;
 | 
			
		||||
use crate::hal::water::TankSensor;
 | 
			
		||||
use crate::log::LOG_ACCESS;
 | 
			
		||||
use crate::FatError::FatError;
 | 
			
		||||
use embassy_sync::mutex::Mutex;
 | 
			
		||||
use embassy_sync::once_lock::OnceLock;
 | 
			
		||||
use esp_alloc as _;
 | 
			
		||||
@@ -53,7 +83,6 @@ use esp_wifi::{init, EspWifiController};
 | 
			
		||||
use littlefs2::fs::{Allocation, Filesystem as lfs2Filesystem};
 | 
			
		||||
use littlefs2::object_safe::DynStorage;
 | 
			
		||||
use log::{info, warn};
 | 
			
		||||
use crate::log::{LogArray, LOG_ACCESS};
 | 
			
		||||
 | 
			
		||||
pub static TIME_ACCESS: OnceLock<Rtc> = OnceLock::new();
 | 
			
		||||
 | 
			
		||||
@@ -78,26 +107,26 @@ pub struct HAL<'a> {
 | 
			
		||||
 | 
			
		||||
#[async_trait]
 | 
			
		||||
pub trait BoardInteraction<'a> {
 | 
			
		||||
    //fn get_tank_sensor(&mut self) -> Option<&mut TankSensor>;
 | 
			
		||||
    fn get_tank_sensor(&mut self) -> Result<&mut TankSensor<'a>, FatError>;
 | 
			
		||||
    fn get_esp(&mut self) -> &mut Esp<'a>;
 | 
			
		||||
    fn get_config(&mut self) -> &PlantControllerConfig;
 | 
			
		||||
    fn get_battery_monitor(&mut self) -> &mut Box<dyn BatteryInteraction + Send>;
 | 
			
		||||
    fn get_rtc_module(&mut self) -> &mut Box<dyn RTCModuleInteraction + Send>;
 | 
			
		||||
    fn set_charge_indicator(&mut self, charging: bool) -> Result<()>;
 | 
			
		||||
    fn set_charge_indicator(&mut self, charging: bool) -> Result<(), FatError>;
 | 
			
		||||
    async fn deep_sleep(&mut self, duration_in_ms: u64) -> !;
 | 
			
		||||
 | 
			
		||||
    fn is_day(&self) -> bool;
 | 
			
		||||
    //should be multsampled
 | 
			
		||||
    fn light(&mut self, enable: bool) -> Result<()>;
 | 
			
		||||
    async fn pump(&mut self, plant: usize, enable: bool) -> Result<()>;
 | 
			
		||||
    async fn pump_current(&mut self, plant: usize) -> Result<Current>;
 | 
			
		||||
    async fn fault(&mut self, plant: usize, enable: bool) -> Result<()>;
 | 
			
		||||
    async fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result<f32>;
 | 
			
		||||
    fn light(&mut self, enable: bool) -> Result<(), FatError>;
 | 
			
		||||
    async fn pump(&mut self, plant: usize, enable: bool) -> Result<(), FatError>;
 | 
			
		||||
    async fn pump_current(&mut self, plant: usize) -> Result<Current, FatError>;
 | 
			
		||||
    async fn fault(&mut self, plant: usize, enable: bool) -> Result<(), FatError>;
 | 
			
		||||
    async fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result<f32, FatError>;
 | 
			
		||||
    async fn general_fault(&mut self, enable: bool);
 | 
			
		||||
    async fn test(&mut self) -> Result<()>;
 | 
			
		||||
    async fn test(&mut self) -> Result<(), FatError>;
 | 
			
		||||
    fn set_config(&mut self, config: PlantControllerConfig);
 | 
			
		||||
    async fn get_mptt_voltage(&mut self) -> anyhow::Result<Voltage>;
 | 
			
		||||
    async fn get_mptt_current(&mut self) -> anyhow::Result<Current>;
 | 
			
		||||
    async fn get_mptt_voltage(&mut self) -> Result<Voltage, FatError>;
 | 
			
		||||
    async fn get_mptt_current(&mut self) -> Result<Current, FatError>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl dyn BoardInteraction<'_> {
 | 
			
		||||
@@ -119,36 +148,36 @@ impl dyn BoardInteraction<'_> {
 | 
			
		||||
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
pub struct FreePeripherals<'a> {
 | 
			
		||||
    // pub gpio0: Gpio0,
 | 
			
		||||
    // pub gpio1: Gpio1,
 | 
			
		||||
    // pub gpio2: Gpio2,
 | 
			
		||||
    // pub gpio3: Gpio3,
 | 
			
		||||
    // pub gpio4: Gpio4,
 | 
			
		||||
    // pub gpio5: Gpio5,
 | 
			
		||||
    pub gpio0: GPIO0<'a>,
 | 
			
		||||
    pub gpio1: GPIO1<'a>,
 | 
			
		||||
    pub gpio2: GPIO2<'a>,
 | 
			
		||||
    pub gpio3: GPIO3<'a>,
 | 
			
		||||
    pub gpio4: GPIO4<'a>,
 | 
			
		||||
    pub gpio5: GPIO5<'a>,
 | 
			
		||||
    pub gpio6: GPIO6<'a>,
 | 
			
		||||
    // pub gpio7: Gpio7,
 | 
			
		||||
    // pub gpio8: Gpio8,
 | 
			
		||||
    pub gpio7: GPIO7<'a>,
 | 
			
		||||
    pub gpio8: GPIO8<'a>,
 | 
			
		||||
    // //config button here
 | 
			
		||||
    // pub gpio10: Gpio10,
 | 
			
		||||
    // pub gpio11: Gpio11,
 | 
			
		||||
    // pub gpio12: Gpio12,
 | 
			
		||||
    // pub gpio13: Gpio13,
 | 
			
		||||
    // pub gpio14: Gpio14,
 | 
			
		||||
    // pub gpio15: Gpio15,
 | 
			
		||||
    // pub gpio16: Gpio16,
 | 
			
		||||
    // pub gpio17: Gpio17,
 | 
			
		||||
    // pub gpio18: Gpio18,
 | 
			
		||||
    pub gpio10: GPIO10<'a>,
 | 
			
		||||
    pub gpio11: GPIO11<'a>,
 | 
			
		||||
    pub gpio12: GPIO12<'a>,
 | 
			
		||||
    pub gpio13: GPIO13<'a>,
 | 
			
		||||
    pub gpio14: GPIO14<'a>,
 | 
			
		||||
    pub gpio15: GPIO15<'a>,
 | 
			
		||||
    pub gpio16: GPIO16<'a>,
 | 
			
		||||
    pub gpio17: GPIO17<'a>,
 | 
			
		||||
    pub gpio18: GPIO18<'a>,
 | 
			
		||||
    // //i2c here
 | 
			
		||||
    // pub gpio21: Gpio21,
 | 
			
		||||
    // pub gpio22: Gpio22,
 | 
			
		||||
    pub gpio21: GPIO21<'a>,
 | 
			
		||||
    pub gpio22: GPIO22<'a>,
 | 
			
		||||
    pub gpio23: GPIO23<'a>,
 | 
			
		||||
    // pub gpio24: Gpio24,
 | 
			
		||||
    // pub gpio25: Gpio25,
 | 
			
		||||
    // pub gpio26: Gpio26,
 | 
			
		||||
    // pub gpio27: Gpio27,
 | 
			
		||||
    // pub gpio28: Gpio28,
 | 
			
		||||
    // pub gpio29: Gpio29,
 | 
			
		||||
    // pub gpio30: Gpio30,
 | 
			
		||||
    pub gpio24: GPIO24<'a>,
 | 
			
		||||
    pub gpio25: GPIO25<'a>,
 | 
			
		||||
    pub gpio26: GPIO26<'a>,
 | 
			
		||||
    pub gpio27: GPIO27<'a>,
 | 
			
		||||
    pub gpio28: GPIO28<'a>,
 | 
			
		||||
    pub gpio29: GPIO29<'a>,
 | 
			
		||||
    pub gpio30: GPIO30<'a>,
 | 
			
		||||
    // pub pcnt0: PCNT0,
 | 
			
		||||
    // pub pcnt1: PCNT1,
 | 
			
		||||
    // pub adc1: ADC1,
 | 
			
		||||
@@ -183,7 +212,9 @@ impl PlantHal {
 | 
			
		||||
    //     Mutex::new(I2cDriver::new(i2c, sda, scl, &config).unwrap())
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    pub async fn create(spawner: Spawner) -> Result<Mutex<CriticalSectionRawMutex, HAL<'static>>> {
 | 
			
		||||
    pub async fn create(
 | 
			
		||||
        spawner: Spawner,
 | 
			
		||||
    ) -> Result<Mutex<CriticalSectionRawMutex, HAL<'static>>, FatError> {
 | 
			
		||||
        let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
 | 
			
		||||
        let peripherals: Peripherals = esp_hal::init(config);
 | 
			
		||||
 | 
			
		||||
@@ -191,7 +222,7 @@ impl PlantHal {
 | 
			
		||||
        esp_alloc::heap_allocator!(#[link_section = ".dram2_uninit"] size: 64000);
 | 
			
		||||
 | 
			
		||||
        let rtc: Rtc = Rtc::new(peripherals.LPWR);
 | 
			
		||||
        match(TIME_ACCESS.init(rtc)){
 | 
			
		||||
        match (TIME_ACCESS.init(rtc)) {
 | 
			
		||||
            Result::Ok(_) => {}
 | 
			
		||||
            Err(_) => {}
 | 
			
		||||
        }
 | 
			
		||||
@@ -223,36 +254,35 @@ impl PlantHal {
 | 
			
		||||
            //     adc1: peripherals.adc1,
 | 
			
		||||
            //     pcnt0: peripherals.pcnt0,
 | 
			
		||||
            //     pcnt1: peripherals.pcnt1,
 | 
			
		||||
            //     gpio0: peripherals.pins.gpio0,
 | 
			
		||||
            //     gpio1: peripherals.pins.gpio1,
 | 
			
		||||
            //     gpio2: peripherals.pins.gpio2,
 | 
			
		||||
            //     gpio3: peripherals.pins.gpio3,
 | 
			
		||||
            //     gpio4: peripherals.pins.gpio4,
 | 
			
		||||
            //     gpio5: peripherals.pins.gpio5,
 | 
			
		||||
            gpio0: peripherals.GPIO0,
 | 
			
		||||
            gpio1: peripherals.GPIO1,
 | 
			
		||||
            gpio2: peripherals.GPIO2,
 | 
			
		||||
            gpio3: peripherals.GPIO3,
 | 
			
		||||
            gpio4: peripherals.GPIO4,
 | 
			
		||||
            gpio5: peripherals.GPIO5,
 | 
			
		||||
            gpio6: peripherals.GPIO6,
 | 
			
		||||
            //     gpio7: peripherals.pins.gpio7,
 | 
			
		||||
            //     gpio8: peripherals.pins.gpio8,
 | 
			
		||||
            //     gpio10: peripherals.pins.gpio10,
 | 
			
		||||
            //     gpio11: peripherals.pins.gpio11,
 | 
			
		||||
            //     gpio12: peripherals.pins.gpio12,
 | 
			
		||||
            //     gpio13: peripherals.pins.gpio13,
 | 
			
		||||
            //     gpio14: peripherals.pins.gpio14,
 | 
			
		||||
            //     gpio15: peripherals.pins.gpio15,
 | 
			
		||||
            //     gpio16: peripherals.pins.gpio16,
 | 
			
		||||
            //     gpio17: peripherals.pins.gpio17,
 | 
			
		||||
            //     gpio18: peripherals.pins.gpio18,
 | 
			
		||||
            //     gpio21: peripherals.pins.gpio21,
 | 
			
		||||
            //     gpio22: peripherals.pins.gpio22,
 | 
			
		||||
            gpio7: peripherals.GPIO7,
 | 
			
		||||
            gpio8: peripherals.GPIO8,
 | 
			
		||||
            gpio10: peripherals.GPIO10,
 | 
			
		||||
            gpio11: peripherals.GPIO11,
 | 
			
		||||
            gpio12: peripherals.GPIO12,
 | 
			
		||||
            gpio13: peripherals.GPIO13,
 | 
			
		||||
            gpio14: peripherals.GPIO14,
 | 
			
		||||
            gpio15: peripherals.GPIO15,
 | 
			
		||||
            gpio16: peripherals.GPIO16,
 | 
			
		||||
            gpio17: peripherals.GPIO17,
 | 
			
		||||
            gpio18: peripherals.GPIO18,
 | 
			
		||||
            gpio21: peripherals.GPIO21,
 | 
			
		||||
            gpio22: peripherals.GPIO22,
 | 
			
		||||
            gpio23: peripherals.GPIO23,
 | 
			
		||||
            //     gpio24: peripherals.pins.gpio24,
 | 
			
		||||
            //     gpio25: peripherals.pins.gpio25,
 | 
			
		||||
            //     gpio26: peripherals.pins.gpio26,
 | 
			
		||||
            //     gpio27: peripherals.pins.gpio27,
 | 
			
		||||
            //     gpio28: peripherals.pins.gpio28,
 | 
			
		||||
            //     gpio29: peripherals.pins.gpio29,
 | 
			
		||||
            //     gpio30: peripherals.pins.gpio30,
 | 
			
		||||
            gpio24: peripherals.GPIO24,
 | 
			
		||||
            gpio25: peripherals.GPIO25,
 | 
			
		||||
            gpio26: peripherals.GPIO26,
 | 
			
		||||
            gpio27: peripherals.GPIO27,
 | 
			
		||||
            gpio28: peripherals.GPIO28,
 | 
			
		||||
            gpio29: peripherals.GPIO29,
 | 
			
		||||
            gpio30: peripherals.GPIO30,
 | 
			
		||||
        };
 | 
			
		||||
        //
 | 
			
		||||
 | 
			
		||||
        let tablebuffer = mk_static!(
 | 
			
		||||
            [u8; esp_bootloader_esp_idf::partitions::PARTITION_TABLE_MAX_LEN],
 | 
			
		||||
@@ -351,77 +381,45 @@ impl PlantHal {
 | 
			
		||||
        let mut init_rtc_store: bool = false;
 | 
			
		||||
        let mut to_config_mode: bool = false;
 | 
			
		||||
        let reasons = match reset_reason() {
 | 
			
		||||
            None => {
 | 
			
		||||
                "unknown"
 | 
			
		||||
            }
 | 
			
		||||
            Some(reason) => {
 | 
			
		||||
                match reason {
 | 
			
		||||
                    SocResetReason::ChipPowerOn => {
 | 
			
		||||
                        "power on"
 | 
			
		||||
                    }
 | 
			
		||||
                    SocResetReason::CoreSw => {
 | 
			
		||||
                        "software reset"
 | 
			
		||||
                    }
 | 
			
		||||
                    SocResetReason::CoreDeepSleep => {
 | 
			
		||||
                        "deep sleep"
 | 
			
		||||
                    }
 | 
			
		||||
                    SocResetReason::CoreSDIO => {
 | 
			
		||||
                        "sdio reset"
 | 
			
		||||
                    }
 | 
			
		||||
                    SocResetReason::CoreMwdt0 => {
 | 
			
		||||
                        "Watchdog Main"
 | 
			
		||||
                    }
 | 
			
		||||
                    SocResetReason::CoreMwdt1 => {
 | 
			
		||||
                        "Watchdog 1"
 | 
			
		||||
                    }
 | 
			
		||||
                    SocResetReason::CoreRtcWdt => {
 | 
			
		||||
                        "Watchdog RTC"
 | 
			
		||||
                    }
 | 
			
		||||
                    SocResetReason::Cpu0Mwdt0 => {
 | 
			
		||||
                        "Watchdog MCpu0"
 | 
			
		||||
                    }
 | 
			
		||||
                    SocResetReason::Cpu0Sw => {
 | 
			
		||||
                        "software reset cpu0"
 | 
			
		||||
                    }
 | 
			
		||||
                    SocResetReason::Cpu0RtcWdt => {
 | 
			
		||||
                        init_rtc_store = true;
 | 
			
		||||
                        "Watchdog RTC cpu0"
 | 
			
		||||
                    }
 | 
			
		||||
                    SocResetReason::SysBrownOut => {
 | 
			
		||||
                        "sys brown out"
 | 
			
		||||
                    }
 | 
			
		||||
                    SocResetReason::SysRtcWdt => {
 | 
			
		||||
                        "Watchdog Sys rtc"
 | 
			
		||||
                    }
 | 
			
		||||
                    SocResetReason::Cpu0Mwdt1 => {
 | 
			
		||||
                        "cpu0 mwdt1"
 | 
			
		||||
                    }
 | 
			
		||||
                    SocResetReason::SysSuperWdt => {
 | 
			
		||||
                        "Watchdog Super"
 | 
			
		||||
                    }
 | 
			
		||||
                    SocResetReason::CoreEfuseCrc => {
 | 
			
		||||
                        "core efuse crc"
 | 
			
		||||
                    }
 | 
			
		||||
                    SocResetReason::CoreUsbUart => {
 | 
			
		||||
                        to_config_mode = true;
 | 
			
		||||
                        "core usb uart"
 | 
			
		||||
                    }
 | 
			
		||||
                    SocResetReason::CoreUsbJtag => {
 | 
			
		||||
                        "core usb jtag"
 | 
			
		||||
                    }
 | 
			
		||||
                    SocResetReason::Cpu0JtagCpu => {
 | 
			
		||||
                        "cpu0 jtag cpu"
 | 
			
		||||
                    }
 | 
			
		||||
            None => "unknown",
 | 
			
		||||
            Some(reason) => match reason {
 | 
			
		||||
                SocResetReason::ChipPowerOn => "power on",
 | 
			
		||||
                SocResetReason::CoreSw => "software reset",
 | 
			
		||||
                SocResetReason::CoreDeepSleep => "deep sleep",
 | 
			
		||||
                SocResetReason::CoreSDIO => "sdio reset",
 | 
			
		||||
                SocResetReason::CoreMwdt0 => "Watchdog Main",
 | 
			
		||||
                SocResetReason::CoreMwdt1 => "Watchdog 1",
 | 
			
		||||
                SocResetReason::CoreRtcWdt => "Watchdog RTC",
 | 
			
		||||
                SocResetReason::Cpu0Mwdt0 => "Watchdog MCpu0",
 | 
			
		||||
                SocResetReason::Cpu0Sw => "software reset cpu0",
 | 
			
		||||
                SocResetReason::Cpu0RtcWdt => {
 | 
			
		||||
                    init_rtc_store = true;
 | 
			
		||||
                    "Watchdog RTC cpu0"
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
                SocResetReason::SysBrownOut => "sys brown out",
 | 
			
		||||
                SocResetReason::SysRtcWdt => "Watchdog Sys rtc",
 | 
			
		||||
                SocResetReason::Cpu0Mwdt1 => "cpu0 mwdt1",
 | 
			
		||||
                SocResetReason::SysSuperWdt => "Watchdog Super",
 | 
			
		||||
                SocResetReason::CoreEfuseCrc => "core efuse crc",
 | 
			
		||||
                SocResetReason::CoreUsbUart => {
 | 
			
		||||
                    to_config_mode = true;
 | 
			
		||||
                    "core usb uart"
 | 
			
		||||
                }
 | 
			
		||||
                SocResetReason::CoreUsbJtag => "core usb jtag",
 | 
			
		||||
                SocResetReason::Cpu0JtagCpu => "cpu0 jtag cpu",
 | 
			
		||||
            },
 | 
			
		||||
        };
 | 
			
		||||
        LOG_ACCESS.lock().await.log(
 | 
			
		||||
            LogMessage::ResetReason,
 | 
			
		||||
            init_rtc_store as u32,
 | 
			
		||||
            to_config_mode as u32,
 | 
			
		||||
            "",
 | 
			
		||||
            &format!("{reasons:?}"),
 | 
			
		||||
        ).await;
 | 
			
		||||
        LOG_ACCESS
 | 
			
		||||
            .lock()
 | 
			
		||||
            .await
 | 
			
		||||
            .log(
 | 
			
		||||
                LogMessage::ResetReason,
 | 
			
		||||
                init_rtc_store as u32,
 | 
			
		||||
                to_config_mode as u32,
 | 
			
		||||
                "",
 | 
			
		||||
                &format!("{reasons:?}"),
 | 
			
		||||
            )
 | 
			
		||||
            .await;
 | 
			
		||||
 | 
			
		||||
        esp.init_rtc_deepsleep_memory(init_rtc_store, to_config_mode)
 | 
			
		||||
            .await;
 | 
			
		||||
@@ -449,7 +447,7 @@ impl PlantHal {
 | 
			
		||||
        // }
 | 
			
		||||
 | 
			
		||||
        // let storage = Storage::new(eeprom, Delay::new(1000));
 | 
			
		||||
        //let rtc_module: Box<dyn RTCModuleInteraction + Send> =
 | 
			
		||||
        let rtc_module: Box<dyn RTCModuleInteraction + Send> = Box::new(initial_hal::NoRTC {});
 | 
			
		||||
        //    Box::new(DS3231Module { rtc, storage }) as Box<dyn RTCModuleInteraction + Send>;
 | 
			
		||||
 | 
			
		||||
        let hal = match config {
 | 
			
		||||
@@ -487,32 +485,35 @@ impl PlantHal {
 | 
			
		||||
                        }
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                let board_hal: Box<dyn BoardInteraction + Send> = //match config.hardware.board {
 | 
			
		||||
                    //BoardVersion::INITIAL => {
 | 
			
		||||
                let board_hal: Box<dyn BoardInteraction + Send> = match config.hardware.board {
 | 
			
		||||
                    BoardVersion::INITIAL => {
 | 
			
		||||
                        initial_hal::create_initial_board(free_pins, config, esp)?
 | 
			
		||||
                ;
 | 
			
		||||
                        //}
 | 
			
		||||
                    }
 | 
			
		||||
                    // BoardVersion::V3 => {
 | 
			
		||||
                    //     v3_hal::create_v3(free_pins, esp, config, battery_interaction, rtc_module)?
 | 
			
		||||
                    // }
 | 
			
		||||
                    //BoardVersion::V4 => {
 | 
			
		||||
                    //    v4_hal::create_v4(free_pins, esp, config, battery_interaction, rtc_module)?
 | 
			
		||||
                    //}
 | 
			
		||||
                    //_ => {
 | 
			
		||||
                    //    todo!()
 | 
			
		||||
                    //}
 | 
			
		||||
                //};
 | 
			
		||||
                    BoardVersion::V4 => {
 | 
			
		||||
                        v4_hal::create_v4(free_pins, esp, config, battery_interaction, rtc_module)?
 | 
			
		||||
                    }
 | 
			
		||||
                    _ => {
 | 
			
		||||
                        todo!()
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                HAL { board_hal }
 | 
			
		||||
            }
 | 
			
		||||
            Err(err) => {
 | 
			
		||||
                LOG_ACCESS.lock().await.log(
 | 
			
		||||
                    LogMessage::ConfigModeMissingConfig,
 | 
			
		||||
                    0,
 | 
			
		||||
                    0,
 | 
			
		||||
                    "",
 | 
			
		||||
                    &err.to_string(),
 | 
			
		||||
                ).await;
 | 
			
		||||
                LOG_ACCESS
 | 
			
		||||
                    .lock()
 | 
			
		||||
                    .await
 | 
			
		||||
                    .log(
 | 
			
		||||
                        LogMessage::ConfigModeMissingConfig,
 | 
			
		||||
                        0,
 | 
			
		||||
                        0,
 | 
			
		||||
                        "",
 | 
			
		||||
                        &err.to_string(),
 | 
			
		||||
                    )
 | 
			
		||||
                    .await;
 | 
			
		||||
                HAL {
 | 
			
		||||
                    board_hal: initial_hal::create_initial_board(
 | 
			
		||||
                        free_pins,
 | 
			
		||||
@@ -527,11 +528,13 @@ impl PlantHal {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
pub async fn esp_time() -> DateTime<Utc> {
 | 
			
		||||
    DateTime::from_timestamp_micros(TIME_ACCESS.get().await.current_time_us() as i64).unwrap()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn esp_set_time(time: DateTime<FixedOffset>) {
 | 
			
		||||
    TIME_ACCESS.get().await.set_current_time_us(time.timestamp_micros() as u64);
 | 
			
		||||
    TIME_ACCESS
 | 
			
		||||
        .get()
 | 
			
		||||
        .await
 | 
			
		||||
        .set_current_time_us(time.timestamp_micros() as u64);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
use crate::hal::Box;
 | 
			
		||||
use crate::FatError::FatResult;
 | 
			
		||||
use alloc::vec::Vec;
 | 
			
		||||
use async_trait::async_trait;
 | 
			
		||||
use chrono::{DateTime, Utc};
 | 
			
		||||
@@ -24,11 +25,11 @@ use chrono::{DateTime, Utc};
 | 
			
		||||
//
 | 
			
		||||
#[async_trait]
 | 
			
		||||
pub trait RTCModuleInteraction {
 | 
			
		||||
     async fn get_backup_info(&mut self) -> anyhow::Result<BackupHeader>;
 | 
			
		||||
     async fn get_backup_config(&mut self) -> anyhow::Result<Vec<u8>>;
 | 
			
		||||
     async fn backup_config(&mut self, bytes: &[u8]) -> anyhow::Result<()>;
 | 
			
		||||
     async fn get_rtc_time(&mut self) -> anyhow::Result<DateTime<Utc>>;
 | 
			
		||||
     async fn set_rtc_time(&mut self, time: &DateTime<Utc>) -> anyhow::Result<()>;
 | 
			
		||||
    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_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;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										426
									
								
								rust/src/hal/v4_hal.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										426
									
								
								rust/src/hal/v4_hal.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,426 @@
 | 
			
		||||
use crate::config::PlantControllerConfig;
 | 
			
		||||
use crate::hal::battery::BatteryInteraction;
 | 
			
		||||
use crate::hal::esp::Esp;
 | 
			
		||||
use crate::hal::rtc::RTCModuleInteraction;
 | 
			
		||||
use crate::hal::water::TankSensor;
 | 
			
		||||
use crate::hal::{BoardInteraction, FreePeripherals, Sensor};
 | 
			
		||||
use alloc::boxed::Box;
 | 
			
		||||
use async_trait::async_trait;
 | 
			
		||||
//use embedded_hal_bus::i2c::MutexDevice;
 | 
			
		||||
use crate::bail;
 | 
			
		||||
use crate::FatError::FatError;
 | 
			
		||||
use esp_hal::gpio::{Flex, Level, Output, OutputConfig};
 | 
			
		||||
use measurements::{Current, Voltage};
 | 
			
		||||
// pub enum Charger<'a> {
 | 
			
		||||
//     SolarMpptV1 {
 | 
			
		||||
//         mppt_ina: SyncIna219<MutexDevice<'a, I2cDriver<'a>>, UnCalibrated>,
 | 
			
		||||
//         solar_is_day: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>,
 | 
			
		||||
//         charge_indicator: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
 | 
			
		||||
//     },
 | 
			
		||||
//     ErrorInit {},
 | 
			
		||||
// }
 | 
			
		||||
//
 | 
			
		||||
// impl Charger<'_> {
 | 
			
		||||
//     pub(crate) fn power_save(&mut self) {
 | 
			
		||||
//         match self {
 | 
			
		||||
//             Charger::SolarMpptV1 { mppt_ina, .. } => {
 | 
			
		||||
//                 let _ = mppt_ina
 | 
			
		||||
//                     .set_configuration(Configuration {
 | 
			
		||||
//                         reset: Default::default(),
 | 
			
		||||
//                         bus_voltage_range: Default::default(),
 | 
			
		||||
//                         shunt_voltage_range: Default::default(),
 | 
			
		||||
//                         bus_resolution: Default::default(),
 | 
			
		||||
//                         shunt_resolution: Default::default(),
 | 
			
		||||
//                         operating_mode: OperatingMode::PowerDown,
 | 
			
		||||
//                     })
 | 
			
		||||
//                     .map_err(|e| {
 | 
			
		||||
//                         log::info!(
 | 
			
		||||
//                     "Error setting ina mppt configuration during deep sleep preparation{:?}",
 | 
			
		||||
//                     e
 | 
			
		||||
//                 );
 | 
			
		||||
//                     });
 | 
			
		||||
//             }
 | 
			
		||||
//             _ => {}
 | 
			
		||||
//         }
 | 
			
		||||
//     }
 | 
			
		||||
//     fn set_charge_indicator(&mut self, charging: bool) -> anyhow::Result<()> {
 | 
			
		||||
//         match self {
 | 
			
		||||
//             Self::SolarMpptV1 {
 | 
			
		||||
//                 charge_indicator, ..
 | 
			
		||||
//             } => {
 | 
			
		||||
//                 charge_indicator.set_state(charging.into())?;
 | 
			
		||||
//             }
 | 
			
		||||
//             _ => {}
 | 
			
		||||
//         }
 | 
			
		||||
//         Ok(())
 | 
			
		||||
//     }
 | 
			
		||||
//
 | 
			
		||||
//     fn is_day(&self) -> bool {
 | 
			
		||||
//         match self {
 | 
			
		||||
//             Charger::SolarMpptV1 { solar_is_day, .. } => solar_is_day.get_level().into(),
 | 
			
		||||
//             _ => true,
 | 
			
		||||
//         }
 | 
			
		||||
//     }
 | 
			
		||||
//
 | 
			
		||||
//     fn get_mptt_voltage(&mut self) -> anyhow::Result<Voltage> {
 | 
			
		||||
//         let voltage = match self {
 | 
			
		||||
//             Charger::SolarMpptV1 { mppt_ina, .. } => mppt_ina
 | 
			
		||||
//                 .bus_voltage()
 | 
			
		||||
//                 .map(|v| Voltage::from_millivolts(v.voltage_mv() as f64))?,
 | 
			
		||||
//             _ => {
 | 
			
		||||
//                 bail!("hardware error during init")
 | 
			
		||||
//             }
 | 
			
		||||
//         };
 | 
			
		||||
//         Ok(voltage)
 | 
			
		||||
//     }
 | 
			
		||||
//
 | 
			
		||||
//     fn get_mptt_current(&mut self) -> anyhow::Result<Current> {
 | 
			
		||||
//         let current = match self {
 | 
			
		||||
//             Charger::SolarMpptV1 { mppt_ina, .. } => mppt_ina.shunt_voltage().map(|v| {
 | 
			
		||||
//                 let shunt_voltage = Voltage::from_microvolts(v.shunt_voltage_uv().abs() as f64);
 | 
			
		||||
//                 let shut_value = Resistance::from_ohms(0.05_f64);
 | 
			
		||||
//                 let current = shunt_voltage.as_volts() / shut_value.as_ohms();
 | 
			
		||||
//                 Current::from_amperes(current)
 | 
			
		||||
//             })?,
 | 
			
		||||
//             _ => {
 | 
			
		||||
//                 bail!("hardware error during init")
 | 
			
		||||
//             }
 | 
			
		||||
//         };
 | 
			
		||||
//         Ok(current)
 | 
			
		||||
//     }
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
pub struct V4<'a> {
 | 
			
		||||
    esp: Esp<'a>,
 | 
			
		||||
    tank_sensor: TankSensor<'a>,
 | 
			
		||||
    //charger: Charger<'a>,
 | 
			
		||||
    rtc_module: Box<dyn RTCModuleInteraction + Send>,
 | 
			
		||||
    battery_monitor: Box<dyn BatteryInteraction + Send>,
 | 
			
		||||
    config: PlantControllerConfig,
 | 
			
		||||
 | 
			
		||||
    awake: Output<'a>,
 | 
			
		||||
    light: Output<'a>,
 | 
			
		||||
    general_fault: Output<'a>,
 | 
			
		||||
    //pump_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>,
 | 
			
		||||
    //pump_ina: Option<SyncIna219<MutexDevice<'a, I2cDriver<'a>>, UnCalibrated>>,
 | 
			
		||||
    //sensor: SensorImpl<'a>,
 | 
			
		||||
    extra1: Output<'a>,
 | 
			
		||||
    extra2: Output<'a>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct InputOutput<'a> {
 | 
			
		||||
    pin: Flex<'a>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn create_v4(
 | 
			
		||||
    peripherals: FreePeripherals<'static>,
 | 
			
		||||
    esp: Esp<'static>,
 | 
			
		||||
    config: PlantControllerConfig,
 | 
			
		||||
    battery_monitor: Box<dyn BatteryInteraction + Send>,
 | 
			
		||||
    rtc_module: Box<dyn RTCModuleInteraction + Send>,
 | 
			
		||||
) -> Result<Box<dyn BoardInteraction<'static> + Send + 'static>, FatError> {
 | 
			
		||||
    log::info!("Start v4");
 | 
			
		||||
    let mut awake = Output::new(peripherals.gpio21, Level::High, OutputConfig::default());
 | 
			
		||||
    awake.set_high();
 | 
			
		||||
 | 
			
		||||
    let mut general_fault = Output::new(peripherals.gpio23, Level::Low, OutputConfig::default());
 | 
			
		||||
    general_fault.set_low();
 | 
			
		||||
 | 
			
		||||
    let mut extra1 = Output::new(peripherals.gpio6, Level::Low, OutputConfig::default());
 | 
			
		||||
    let mut extra2 = Output::new(peripherals.gpio15, Level::Low, OutputConfig::default());
 | 
			
		||||
 | 
			
		||||
    let one_wire_pin = Flex::new(peripherals.gpio18);
 | 
			
		||||
    let tank_power_pin = peripherals.gpio11;
 | 
			
		||||
    let flow_sensor_pin = peripherals.gpio4;
 | 
			
		||||
 | 
			
		||||
    let tank_sensor = TankSensor::create(
 | 
			
		||||
        one_wire_pin,
 | 
			
		||||
        //peripherals.adc1,
 | 
			
		||||
        //peripherals.gpio5,
 | 
			
		||||
        //tank_power_pin,
 | 
			
		||||
        //flow_sensor_pin,
 | 
			
		||||
        //peripherals.pcnt1,
 | 
			
		||||
    )?;
 | 
			
		||||
    //
 | 
			
		||||
    // let mut sensor_expander = Pca9535Immediate::new(MutexDevice::new(&I2C_DRIVER), 34);
 | 
			
		||||
    // let sensor = match sensor_expander.pin_into_output(GPIOBank::Bank0, 0) {
 | 
			
		||||
    //     Ok(_) => {
 | 
			
		||||
    //         log::info!("SensorExpander answered");
 | 
			
		||||
    //         //pulse counter version
 | 
			
		||||
    //         let mut signal_counter = PcntDriver::new(
 | 
			
		||||
    //             peripherals.pcnt0,
 | 
			
		||||
    //             Some(peripherals.gpio22),
 | 
			
		||||
    //             Option::<AnyInputPin>::None,
 | 
			
		||||
    //             Option::<AnyInputPin>::None,
 | 
			
		||||
    //             Option::<AnyInputPin>::None,
 | 
			
		||||
    //         )?;
 | 
			
		||||
    //
 | 
			
		||||
    //         signal_counter.channel_config(
 | 
			
		||||
    //             PcntChannel::Channel0,
 | 
			
		||||
    //             PinIndex::Pin0,
 | 
			
		||||
    //             PinIndex::Pin1,
 | 
			
		||||
    //             &PcntChannelConfig {
 | 
			
		||||
    //                 lctrl_mode: PcntControlMode::Keep,
 | 
			
		||||
    //                 hctrl_mode: PcntControlMode::Keep,
 | 
			
		||||
    //                 pos_mode: PcntCountMode::Increment,
 | 
			
		||||
    //                 neg_mode: PcntCountMode::Hold,
 | 
			
		||||
    //                 counter_h_lim: i16::MAX,
 | 
			
		||||
    //                 counter_l_lim: 0,
 | 
			
		||||
    //             },
 | 
			
		||||
    //         )?;
 | 
			
		||||
    //
 | 
			
		||||
    //         for pin in 0..8 {
 | 
			
		||||
    //             let _ = sensor_expander.pin_into_output(GPIOBank::Bank0, pin);
 | 
			
		||||
    //             let _ = sensor_expander.pin_into_output(GPIOBank::Bank1, pin);
 | 
			
		||||
    //             let _ = sensor_expander.pin_set_low(GPIOBank::Bank0, pin);
 | 
			
		||||
    //             let _ = sensor_expander.pin_set_low(GPIOBank::Bank1, pin);
 | 
			
		||||
    //         }
 | 
			
		||||
    //
 | 
			
		||||
    //         SensorImpl::PulseCounter {
 | 
			
		||||
    //             signal_counter,
 | 
			
		||||
    //             sensor_expander,
 | 
			
		||||
    //         }
 | 
			
		||||
    //     }
 | 
			
		||||
    //     Err(_) => {
 | 
			
		||||
    //         log::info!("Can bus mode ");
 | 
			
		||||
    //         let timing = can::config::Timing::B25K;
 | 
			
		||||
    //         let config = can::config::Config::new().timing(timing);
 | 
			
		||||
    //         let can = can::CanDriver::new(
 | 
			
		||||
    //             peripherals.can,
 | 
			
		||||
    //             peripherals.gpio0,
 | 
			
		||||
    //             peripherals.gpio2,
 | 
			
		||||
    //             &config,
 | 
			
		||||
    //         )
 | 
			
		||||
    //         .unwrap();
 | 
			
		||||
    //
 | 
			
		||||
    //         let frame = StandardId::new(0x042).unwrap();
 | 
			
		||||
    //         let tx_frame = Frame::new(frame, &[0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
 | 
			
		||||
    //         can.transmit(&tx_frame, 1000).unwrap();
 | 
			
		||||
    //
 | 
			
		||||
    //         if let Ok(rx_frame) = can.receive(1000) {
 | 
			
		||||
    //             log::info!("rx {:}:", rx_frame);
 | 
			
		||||
    //         }
 | 
			
		||||
    //         //can bus version
 | 
			
		||||
    //         SensorImpl::CanBus { can }
 | 
			
		||||
    //     }
 | 
			
		||||
    // };
 | 
			
		||||
 | 
			
		||||
    let mut solar_is_day = Output::new(peripherals.gpio7, Level::Low, Default::default());
 | 
			
		||||
    let mut light = Output::new(peripherals.gpio10, Level::Low, Default::default());
 | 
			
		||||
    let mut charge_indicator = Output::new(peripherals.gpio3, Level::Low, Default::default());
 | 
			
		||||
 | 
			
		||||
    // let mut pump_expander = Pca9535Immediate::new(MutexDevice::new(&I2C_DRIVER), 32);
 | 
			
		||||
    // for pin in 0..8 {
 | 
			
		||||
    //     let _ = pump_expander.pin_into_output(GPIOBank::Bank0, pin);
 | 
			
		||||
    //     let _ = pump_expander.pin_into_output(GPIOBank::Bank1, pin);
 | 
			
		||||
    //     let _ = pump_expander.pin_set_low(GPIOBank::Bank0, pin);
 | 
			
		||||
    //     let _ = pump_expander.pin_set_low(GPIOBank::Bank1, pin);
 | 
			
		||||
    // }
 | 
			
		||||
    //
 | 
			
		||||
    // let mppt_ina = SyncIna219::new(
 | 
			
		||||
    //     MutexDevice::new(&I2C_DRIVER),
 | 
			
		||||
    //     Address::from_pins(Pin::Vcc, Pin::Gnd),
 | 
			
		||||
    // );
 | 
			
		||||
    //
 | 
			
		||||
    // let charger = match mppt_ina {
 | 
			
		||||
    //     Ok(mut mppt_ina) => {
 | 
			
		||||
    //         mppt_ina.set_configuration(Configuration {
 | 
			
		||||
    //             reset: Default::default(),
 | 
			
		||||
    //             bus_voltage_range: Default::default(),
 | 
			
		||||
    //             shunt_voltage_range: Default::default(),
 | 
			
		||||
    //             bus_resolution: Default::default(),
 | 
			
		||||
    //             shunt_resolution: ina219::configuration::Resolution::Avg128,
 | 
			
		||||
    //             operating_mode: Default::default(),
 | 
			
		||||
    //         })?;
 | 
			
		||||
    //
 | 
			
		||||
    //         Charger::SolarMpptV1 {
 | 
			
		||||
    //             mppt_ina,
 | 
			
		||||
    //             solar_is_day,
 | 
			
		||||
    //             charge_indicator,
 | 
			
		||||
    //         }
 | 
			
		||||
    //     }
 | 
			
		||||
    //     Err(_) => Charger::ErrorInit {},
 | 
			
		||||
    // };
 | 
			
		||||
    //
 | 
			
		||||
    // let pump_ina = match SyncIna219::new(
 | 
			
		||||
    //     MutexDevice::new(&I2C_DRIVER),
 | 
			
		||||
    //     Address::from_pins(Pin::Gnd, Pin::Sda),
 | 
			
		||||
    // ) {
 | 
			
		||||
    //     Ok(pump_ina) => Some(pump_ina),
 | 
			
		||||
    //     Err(err) => {
 | 
			
		||||
    //         log::info!("Error creating pump ina: {:?}", err);
 | 
			
		||||
    //         None
 | 
			
		||||
    //     }
 | 
			
		||||
    // };
 | 
			
		||||
 | 
			
		||||
    let v = V4 {
 | 
			
		||||
        rtc_module,
 | 
			
		||||
        esp,
 | 
			
		||||
        awake,
 | 
			
		||||
        tank_sensor,
 | 
			
		||||
        light,
 | 
			
		||||
        general_fault,
 | 
			
		||||
        //pump_ina,
 | 
			
		||||
        //pump_expander,
 | 
			
		||||
        config,
 | 
			
		||||
        battery_monitor,
 | 
			
		||||
        //charger,
 | 
			
		||||
        extra1,
 | 
			
		||||
        extra2,
 | 
			
		||||
        //sensor,
 | 
			
		||||
    };
 | 
			
		||||
    Ok(Box::new(v))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[async_trait]
 | 
			
		||||
impl<'a> BoardInteraction<'a> for V4<'a> {
 | 
			
		||||
    fn get_tank_sensor(&mut self) -> Result<&mut TankSensor<'a>, FatError> {
 | 
			
		||||
        Ok(&mut self.tank_sensor)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_esp(&mut self) -> &mut Esp<'a> {
 | 
			
		||||
        &mut self.esp
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_config(&mut self) -> &PlantControllerConfig {
 | 
			
		||||
        &self.config
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_battery_monitor(&mut self) -> &mut Box<dyn BatteryInteraction + Send> {
 | 
			
		||||
        &mut self.battery_monitor
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_rtc_module(&mut self) -> &mut Box<dyn RTCModuleInteraction + Send> {
 | 
			
		||||
        &mut self.rtc_module
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn set_charge_indicator(&mut self, charging: bool) -> Result<(), FatError> {
 | 
			
		||||
        bail!("not implemented");
 | 
			
		||||
        // self.charger.set_charge_indicator(charging)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn deep_sleep(&mut self, duration_in_ms: u64) -> ! {
 | 
			
		||||
        self.awake.set_low();
 | 
			
		||||
        //self.charger.power_save();
 | 
			
		||||
        self.esp.deep_sleep(duration_in_ms).await;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn is_day(&self) -> bool {
 | 
			
		||||
        false
 | 
			
		||||
        //self.charger.is_day()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn light(&mut self, enable: bool) -> Result<(), FatError> {
 | 
			
		||||
        bail!("not implemented");
 | 
			
		||||
        // unsafe { gpio_hold_dis(self.light.pin()) };
 | 
			
		||||
        // self.light.set_state(enable.into())?;
 | 
			
		||||
        // unsafe { gpio_hold_en(self.light.pin()) };
 | 
			
		||||
        // anyhow::Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn pump(&mut self, plant: usize, enable: bool) -> Result<(), FatError> {
 | 
			
		||||
        bail!("not implemented");
 | 
			
		||||
        // if enable {
 | 
			
		||||
        //     self.pump_expander
 | 
			
		||||
        //         .pin_set_high(GPIOBank::Bank0, plant.try_into()?)?;
 | 
			
		||||
        // } else {
 | 
			
		||||
        //     self.pump_expander
 | 
			
		||||
        //         .pin_set_low(GPIOBank::Bank0, plant.try_into()?)?;
 | 
			
		||||
        // }
 | 
			
		||||
        // anyhow::Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn pump_current(&mut self, _plant: usize) -> Result<Current, FatError> {
 | 
			
		||||
        bail!("not implemented");
 | 
			
		||||
        // //sensore is shared for all pumps, ignore plant id
 | 
			
		||||
        // match self.pump_ina.as_mut() {
 | 
			
		||||
        //     None => {
 | 
			
		||||
        //         bail!("pump current sensor not available");
 | 
			
		||||
        //     }
 | 
			
		||||
        //     Some(pump_ina) => {
 | 
			
		||||
        //         let v = pump_ina.shunt_voltage().map(|v| {
 | 
			
		||||
        //             let shunt_voltage = Voltage::from_microvolts(v.shunt_voltage_uv().abs() as f64);
 | 
			
		||||
        //             let shut_value = Resistance::from_ohms(0.05_f64);
 | 
			
		||||
        //             let current = shunt_voltage.as_volts() / shut_value.as_ohms();
 | 
			
		||||
        //             Current::from_amperes(current)
 | 
			
		||||
        //         })?;
 | 
			
		||||
        //         Ok(v)
 | 
			
		||||
        //     }
 | 
			
		||||
        // }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn fault(&mut self, plant: usize, enable: bool) -> Result<(), FatError> {
 | 
			
		||||
        bail!("not implemented");
 | 
			
		||||
        // if enable {
 | 
			
		||||
        //     self.pump_expander
 | 
			
		||||
        //         .pin_set_high(GPIOBank::Bank1, plant.try_into()?)?
 | 
			
		||||
        // } else {
 | 
			
		||||
        //     self.pump_expander
 | 
			
		||||
        //         .pin_set_low(GPIOBank::Bank1, plant.try_into()?)?
 | 
			
		||||
        // }
 | 
			
		||||
        // anyhow::Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result<f32, FatError> {
 | 
			
		||||
        bail!("not implemented");
 | 
			
		||||
        //self.sensor.measure_moisture_hz(plant, sensor)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn general_fault(&mut self, enable: bool) {
 | 
			
		||||
        //FIXME unsafe { gpio_hold_dis(self.general_fault.pin()) };
 | 
			
		||||
        self.general_fault.set_level(enable.into());
 | 
			
		||||
        //FIXME unsafe { gpio_hold_en(self.general_fault.pin()) };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn test(&mut self) -> Result<(), FatError> {
 | 
			
		||||
        // self.general_fault(true);
 | 
			
		||||
        // self.esp.delay.delay_ms(100);
 | 
			
		||||
        // self.general_fault(false);
 | 
			
		||||
        // self.esp.delay.delay_ms(500);
 | 
			
		||||
        // self.light(true)?;
 | 
			
		||||
        // self.esp.delay.delay_ms(500);
 | 
			
		||||
        // self.light(false)?;
 | 
			
		||||
        // self.esp.delay.delay_ms(500);
 | 
			
		||||
        // for i in 0..PLANT_COUNT {
 | 
			
		||||
        //     self.fault(i, true)?;
 | 
			
		||||
        //     self.esp.delay.delay_ms(500);
 | 
			
		||||
        //     self.fault(i, false)?;
 | 
			
		||||
        //     self.esp.delay.delay_ms(500);
 | 
			
		||||
        // }
 | 
			
		||||
        // for i in 0..PLANT_COUNT {
 | 
			
		||||
        //     self.pump(i, true)?;
 | 
			
		||||
        //     self.esp.delay.delay_ms(100);
 | 
			
		||||
        //     self.pump(i, false)?;
 | 
			
		||||
        //     self.esp.delay.delay_ms(100);
 | 
			
		||||
        // }
 | 
			
		||||
        // for plant in 0..PLANT_COUNT {
 | 
			
		||||
        //     let a = self.measure_moisture_hz(plant, Sensor::A);
 | 
			
		||||
        //     let b = self.measure_moisture_hz(plant, Sensor::B);
 | 
			
		||||
        //     let aa = match a {
 | 
			
		||||
        //         OkStd(a) => a as u32,
 | 
			
		||||
        //         Err(_) => u32::MAX,
 | 
			
		||||
        //     };
 | 
			
		||||
        //     let bb = match b {
 | 
			
		||||
        //         OkStd(b) => b as u32,
 | 
			
		||||
        //         Err(_) => u32::MAX,
 | 
			
		||||
        //     };
 | 
			
		||||
        //     log(LogMessage::TestSensor, aa, bb, &plant.to_string(), "");
 | 
			
		||||
        // }
 | 
			
		||||
        // self.esp.delay.delay_ms(10);
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn set_config(&mut self, config: PlantControllerConfig) {
 | 
			
		||||
        self.config = config;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn get_mptt_voltage(&mut self) -> Result<Voltage, FatError> {
 | 
			
		||||
        bail!("not implemented");
 | 
			
		||||
        //self.charger.get_mptt_voltage()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn get_mptt_current(&mut self) -> Result<Current, FatError> {
 | 
			
		||||
        bail!("not implemented");
 | 
			
		||||
        //self.charger.get_mptt_current()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										172
									
								
								rust/src/hal/water.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								rust/src/hal/water.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,172 @@
 | 
			
		||||
use crate::bail;
 | 
			
		||||
use crate::FatError::FatError;
 | 
			
		||||
use embassy_time::Timer;
 | 
			
		||||
use esp_hal::delay::Delay;
 | 
			
		||||
use esp_hal::gpio::{Flex, OutputConfig, Pull};
 | 
			
		||||
use esp_println::println;
 | 
			
		||||
use onewire::{ds18b20, Device, DeviceSearch, OneWire, DS18B20};
 | 
			
		||||
 | 
			
		||||
pub struct TankSensor<'a> {
 | 
			
		||||
    one_wire_bus: OneWire<Flex<'a>>,
 | 
			
		||||
    // tank_channel: AdcChannelDriver<'a, Gpio5, AdcDriver<'a, ADC1>>,
 | 
			
		||||
    // tank_power: PinDriver<'a, AnyIOPin, InputOutput>,
 | 
			
		||||
    // flow_counter: PcntDriver<'a>,
 | 
			
		||||
    // delay: Delay,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> TankSensor<'a> {
 | 
			
		||||
    pub(crate) fn create(
 | 
			
		||||
        mut one_wire_pin: Flex,
 | 
			
		||||
        // adc1: ADC1,
 | 
			
		||||
        // gpio5: Gpio5,
 | 
			
		||||
        // tank_power_pin: AnyIOPin,
 | 
			
		||||
        // flow_sensor_pin: AnyIOPin,
 | 
			
		||||
        // pcnt1: PCNT1,
 | 
			
		||||
    ) -> Result<TankSensor, FatError> {
 | 
			
		||||
        one_wire_pin.apply_output_config(&OutputConfig::default().with_pull(Pull::None));
 | 
			
		||||
 | 
			
		||||
        // let adc_config = AdcChannelConfig {
 | 
			
		||||
        //     attenuation: attenuation::DB_11,
 | 
			
		||||
        //     resolution: Resolution::Resolution12Bit,
 | 
			
		||||
        //     calibration: esp_idf_hal::adc::oneshot::config::Calibration::Curve,
 | 
			
		||||
        // };
 | 
			
		||||
        // let tank_driver = AdcDriver::new(adc1).expect("Failed to configure ADC");
 | 
			
		||||
        // let tank_channel = AdcChannelDriver::new(tank_driver, gpio5, &adc_config)
 | 
			
		||||
        //     .expect("Failed to configure ADC channel");
 | 
			
		||||
        //
 | 
			
		||||
        // let mut tank_power =
 | 
			
		||||
        //     PinDriver::input_output(tank_power_pin).expect("Failed to configure pin");
 | 
			
		||||
        // tank_power
 | 
			
		||||
        //     .set_pull(Pull::Floating)
 | 
			
		||||
        //     .expect("Failed to set pull");
 | 
			
		||||
        //
 | 
			
		||||
 | 
			
		||||
        let one_wire_bus = OneWire::new(one_wire_pin, false);
 | 
			
		||||
 | 
			
		||||
        //
 | 
			
		||||
        // let mut flow_counter = PcntDriver::new(
 | 
			
		||||
        //     pcnt1,
 | 
			
		||||
        //     Some(flow_sensor_pin),
 | 
			
		||||
        //     Option::<AnyInputPin>::None,
 | 
			
		||||
        //     Option::<AnyInputPin>::None,
 | 
			
		||||
        //     Option::<AnyInputPin>::None,
 | 
			
		||||
        // )?;
 | 
			
		||||
        //
 | 
			
		||||
        // flow_counter.channel_config(
 | 
			
		||||
        //     PcntChannel::Channel1,
 | 
			
		||||
        //     PinIndex::Pin0,
 | 
			
		||||
        //     PinIndex::Pin1,
 | 
			
		||||
        //     &PcntChannelConfig {
 | 
			
		||||
        //         lctrl_mode: PcntControlMode::Keep,
 | 
			
		||||
        //         hctrl_mode: PcntControlMode::Keep,
 | 
			
		||||
        //         pos_mode: PcntCountMode::Increment,
 | 
			
		||||
        //         neg_mode: PcntCountMode::Hold,
 | 
			
		||||
        //         counter_h_lim: i16::MAX,
 | 
			
		||||
        //         counter_l_lim: 0,
 | 
			
		||||
        //     },
 | 
			
		||||
        // )?;
 | 
			
		||||
        //
 | 
			
		||||
        Ok(TankSensor {
 | 
			
		||||
            one_wire_bus,
 | 
			
		||||
            //     tank_channel,
 | 
			
		||||
            //     tank_power,
 | 
			
		||||
            //     flow_counter,
 | 
			
		||||
            //     delay: Default::default(),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn reset_flow_meter(&mut self) {
 | 
			
		||||
        // self.flow_counter.counter_pause().unwrap();
 | 
			
		||||
        // self.flow_counter.counter_clear().unwrap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn start_flow_meter(&mut self) {
 | 
			
		||||
        //self.flow_counter.counter_resume().unwrap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn get_flow_meter_value(&mut self) -> i16 {
 | 
			
		||||
        //self.flow_counter.get_counter_value().unwrap()
 | 
			
		||||
        5_i16
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn stop_flow_meter(&mut self) -> i16 {
 | 
			
		||||
        //self.flow_counter.counter_pause().unwrap();
 | 
			
		||||
        self.get_flow_meter_value()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn water_temperature_c(&mut self) -> Result<f32, FatError> {
 | 
			
		||||
        //multisample should be moved to water_temperature_c
 | 
			
		||||
        let mut attempt = 1;
 | 
			
		||||
        let mut delay = Delay::new();
 | 
			
		||||
        self.one_wire_bus.reset(&mut delay)?;
 | 
			
		||||
        let mut search = DeviceSearch::new();
 | 
			
		||||
        let mut water_temp_sensor: Option<Device> = None;
 | 
			
		||||
        while let Some(device) = self.one_wire_bus.search_next(&mut search, &mut delay)? {
 | 
			
		||||
            if device.address[0] == ds18b20::FAMILY_CODE {
 | 
			
		||||
                water_temp_sensor = Some(device);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        match water_temp_sensor {
 | 
			
		||||
            Some(device) => {
 | 
			
		||||
                println!("Found one wire device: {:?}", device);
 | 
			
		||||
                let mut water_temp_sensor = DS18B20::new(device)?;
 | 
			
		||||
 | 
			
		||||
                let water_temp: Result<f32, FatError> = loop {
 | 
			
		||||
                    let temp = self
 | 
			
		||||
                        .single_temperature_c(&mut water_temp_sensor, &mut delay)
 | 
			
		||||
                        .await;
 | 
			
		||||
                    match &temp {
 | 
			
		||||
                        Ok(res) => {
 | 
			
		||||
                            println!("Water temp is {}", res);
 | 
			
		||||
                            break temp;
 | 
			
		||||
                        }
 | 
			
		||||
                        Err(err) => {
 | 
			
		||||
                            println!("Could not get water temp {} attempt {}", err, attempt)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if attempt == 5 {
 | 
			
		||||
                        break temp;
 | 
			
		||||
                    }
 | 
			
		||||
                    attempt += 1;
 | 
			
		||||
                };
 | 
			
		||||
                water_temp
 | 
			
		||||
            }
 | 
			
		||||
            None => {
 | 
			
		||||
                bail!("Not found any one wire Ds18b20");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn single_temperature_c(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        sensor: &mut DS18B20,
 | 
			
		||||
        delay: &mut Delay,
 | 
			
		||||
    ) -> Result<f32, FatError> {
 | 
			
		||||
        let resolution = sensor.measure_temperature(&mut self.one_wire_bus, delay)?;
 | 
			
		||||
        Timer::after_millis(resolution.time_ms() as u64).await;
 | 
			
		||||
        let temperature = sensor.read_temperature(&mut self.one_wire_bus, delay)? as f32;
 | 
			
		||||
        if temperature == 85_f32 {
 | 
			
		||||
            bail!("Ds18b20 dummy temperature returned");
 | 
			
		||||
        }
 | 
			
		||||
        Ok(temperature / 10_f32)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn tank_sensor_voltage(&mut self) -> Result<f32, FatError> {
 | 
			
		||||
        // self.tank_power.set_high()?;
 | 
			
		||||
        // //let stabilize
 | 
			
		||||
        // self.delay.delay_ms(100);
 | 
			
		||||
        //
 | 
			
		||||
        // let mut store = [0_u16; TANK_MULTI_SAMPLE];
 | 
			
		||||
        // for multisample in 0..TANK_MULTI_SAMPLE {
 | 
			
		||||
        //     let value = self.tank_channel.read()?;
 | 
			
		||||
        //     store[multisample] = value;
 | 
			
		||||
        // }
 | 
			
		||||
        // self.tank_power.set_low()?;
 | 
			
		||||
        //
 | 
			
		||||
        // store.sort();
 | 
			
		||||
        // let median_mv = store[6] as f32 / 1000_f32;
 | 
			
		||||
        let median_mv = 10_f32;
 | 
			
		||||
        Ok(median_mv)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,39 +1,48 @@
 | 
			
		||||
use crate::hal::TIME_ACCESS;
 | 
			
		||||
use crate::vec;
 | 
			
		||||
use alloc::string::ToString;
 | 
			
		||||
use alloc::vec::Vec;
 | 
			
		||||
use bytemuck::{AnyBitPattern, Contiguous, Pod, Zeroable};
 | 
			
		||||
use bytemuck::{AnyBitPattern, Pod, Zeroable};
 | 
			
		||||
use deranged::RangedU8;
 | 
			
		||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
 | 
			
		||||
use embassy_sync::mutex::Mutex;
 | 
			
		||||
use embassy_time::Instant;
 | 
			
		||||
use esp_hal::Persistable;
 | 
			
		||||
use log::info;
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
use strum_macros::IntoStaticStr;
 | 
			
		||||
use unit_enum::UnitEnum;
 | 
			
		||||
use crate::hal::TIME_ACCESS;
 | 
			
		||||
 | 
			
		||||
const LOG_ARRAY_SIZE: u8 = 220;
 | 
			
		||||
const MAX_LOG_ARRAY_INDEX: u8 = LOG_ARRAY_SIZE - 1;
 | 
			
		||||
#[esp_hal::ram(rtc_fast, persistent)]
 | 
			
		||||
static mut LOG_ARRAY: LogArray = LogArray {
 | 
			
		||||
    buffer: [LogEntryInner { timestamp: 0, message_id: 0, a: 0, b: 0, txt_short: [0;TXT_SHORT_LENGTH], txt_long: [0;TXT_LONG_LENGTH] }; 256],
 | 
			
		||||
    buffer: [LogEntryInner {
 | 
			
		||||
        timestamp: 0,
 | 
			
		||||
        message_id: 0,
 | 
			
		||||
        a: 0,
 | 
			
		||||
        b: 0,
 | 
			
		||||
        txt_short: [0; TXT_SHORT_LENGTH],
 | 
			
		||||
        txt_long: [0; TXT_LONG_LENGTH],
 | 
			
		||||
    }; LOG_ARRAY_SIZE as usize],
 | 
			
		||||
    head: 0,
 | 
			
		||||
};
 | 
			
		||||
pub static LOG_ACCESS: Mutex<CriticalSectionRawMutex, LogArray> = Mutex::new(unsafe { LOG_ARRAY });
 | 
			
		||||
pub static LOG_ACCESS: Mutex<CriticalSectionRawMutex, &'static mut LogArray> =
 | 
			
		||||
    unsafe { Mutex::new(&mut *&raw mut LOG_ARRAY) };
 | 
			
		||||
 | 
			
		||||
const TXT_SHORT_LENGTH: usize = 8;
 | 
			
		||||
const TXT_LONG_LENGTH: usize = 32;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Copy, AnyBitPattern)]
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
pub struct LogArray{
 | 
			
		||||
    buffer: [LogEntryInner; (u8::MAX_VALUE  as usize) +1],
 | 
			
		||||
    head: u8
 | 
			
		||||
pub struct LogArray {
 | 
			
		||||
    buffer: [LogEntryInner; LOG_ARRAY_SIZE as usize],
 | 
			
		||||
    head: u8,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe impl Persistable for LogArray {}
 | 
			
		||||
unsafe impl Zeroable for LogEntryInner {}
 | 
			
		||||
 | 
			
		||||
unsafe impl Pod for LogEntryInner{}
 | 
			
		||||
unsafe impl Pod for LogEntryInner {}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Copy)]
 | 
			
		||||
struct LogEntryInner {
 | 
			
		||||
@@ -57,7 +66,7 @@ pub struct LogEntry {
 | 
			
		||||
 | 
			
		||||
impl From<LogEntryInner> for LogEntry {
 | 
			
		||||
    fn from(value: LogEntryInner) -> Self {
 | 
			
		||||
        LogEntry{
 | 
			
		||||
        LogEntry {
 | 
			
		||||
            timestamp: value.timestamp,
 | 
			
		||||
            message_id: value.message_id,
 | 
			
		||||
            a: value.a,
 | 
			
		||||
@@ -70,10 +79,13 @@ impl From<LogEntryInner> for LogEntry {
 | 
			
		||||
 | 
			
		||||
impl LogArray {
 | 
			
		||||
    pub fn get(&mut self) -> Vec<LogEntry> {
 | 
			
		||||
        let head: RangedU8<0, MAX_LOG_ARRAY_INDEX> =
 | 
			
		||||
            RangedU8::new(self.head).unwrap_or(RangedU8::new(0).unwrap());
 | 
			
		||||
 | 
			
		||||
        let mut rv: Vec<LogEntry> = Vec::new();
 | 
			
		||||
        let mut index = self.head.wrapping_sub(1);
 | 
			
		||||
        let mut index = head.wrapping_sub(1);
 | 
			
		||||
        for _ in 0..self.buffer.len() {
 | 
			
		||||
            let entry = self.buffer[index as usize];
 | 
			
		||||
            let entry = self.buffer[index.get() as usize];
 | 
			
		||||
            if (entry.message_id as usize) != LogMessage::Empty.ordinal() {
 | 
			
		||||
                rv.push(entry.into());
 | 
			
		||||
            }
 | 
			
		||||
@@ -90,13 +102,16 @@ impl LogArray {
 | 
			
		||||
        txt_short: &str,
 | 
			
		||||
        txt_long: &str,
 | 
			
		||||
    ) {
 | 
			
		||||
        let mut head: RangedU8<0, MAX_LOG_ARRAY_INDEX> =
 | 
			
		||||
            RangedU8::new(self.head).unwrap_or(RangedU8::new(0).unwrap());
 | 
			
		||||
 | 
			
		||||
        let mut txt_short_stack: heapless::String<TXT_SHORT_LENGTH> = heapless::String::new();
 | 
			
		||||
        let mut txt_long_stack: heapless::String<TXT_LONG_LENGTH> = heapless::String::new();
 | 
			
		||||
 | 
			
		||||
        limit_length(txt_short, &mut txt_short_stack);
 | 
			
		||||
        limit_length(txt_long, &mut txt_long_stack);
 | 
			
		||||
 | 
			
		||||
        let time = TIME_ACCESS.get().await.current_time_us()/1000;
 | 
			
		||||
        let time = TIME_ACCESS.get().await.current_time_us() / 1000;
 | 
			
		||||
 | 
			
		||||
        let ordinal = message_key.ordinal() as u16;
 | 
			
		||||
        let template: &str = message_key.into();
 | 
			
		||||
@@ -108,14 +123,19 @@ impl LogArray {
 | 
			
		||||
 | 
			
		||||
        info!("{}", template_string);
 | 
			
		||||
 | 
			
		||||
        let to_modify = &mut self.buffer[self.head as usize];
 | 
			
		||||
        let to_modify = &mut self.buffer[head.get() as usize];
 | 
			
		||||
        to_modify.timestamp = time;
 | 
			
		||||
        to_modify.message_id = ordinal;
 | 
			
		||||
        to_modify.a = number_a;
 | 
			
		||||
        to_modify.b = number_b;
 | 
			
		||||
        to_modify.txt_short.clone_from_slice(&txt_short_stack.as_bytes());
 | 
			
		||||
        to_modify.txt_long.clone_from_slice(&txt_long_stack.as_bytes());
 | 
			
		||||
        self.head = self.head.wrapping_add(1);
 | 
			
		||||
        to_modify
 | 
			
		||||
            .txt_short
 | 
			
		||||
            .clone_from_slice(&txt_short_stack.as_bytes());
 | 
			
		||||
        to_modify
 | 
			
		||||
            .txt_long
 | 
			
		||||
            .clone_from_slice(&txt_long_stack.as_bytes());
 | 
			
		||||
        head = head.wrapping_add(1);
 | 
			
		||||
        self.head = head.get();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -140,13 +160,9 @@ fn limit_length<const LIMIT: usize>(input: &str, target: &mut heapless::String<L
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[derive(IntoStaticStr, Serialize, PartialEq, Eq, PartialOrd, Ord, Clone, UnitEnum)]
 | 
			
		||||
pub enum LogMessage {
 | 
			
		||||
    #[strum(
 | 
			
		||||
        serialize = ""
 | 
			
		||||
    )]
 | 
			
		||||
    #[strum(serialize = "")]
 | 
			
		||||
    Empty,
 | 
			
		||||
    #[strum(
 | 
			
		||||
        serialize = "Reset due to ${txt_long} requires rtc clear ${number_a} and force config mode ${number_b}"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										192
									
								
								rust/src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										192
									
								
								rust/src/main.rs
									
									
									
									
									
								
							@@ -12,8 +12,11 @@ esp_bootloader_esp_idf::esp_app_desc!();
 | 
			
		||||
use esp_backtrace as _;
 | 
			
		||||
 | 
			
		||||
use crate::config::PlantConfig;
 | 
			
		||||
use crate::hal::esp_time;
 | 
			
		||||
use crate::log::LOG_ACCESS;
 | 
			
		||||
use crate::tank::WATER_FROZEN_THRESH;
 | 
			
		||||
use crate::webserver::httpd;
 | 
			
		||||
use crate::FatError::FatResult;
 | 
			
		||||
use crate::{
 | 
			
		||||
    config::BoardVersion::INITIAL,
 | 
			
		||||
    hal::{PlantHal, HAL, PLANT_COUNT},
 | 
			
		||||
@@ -32,15 +35,14 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
 | 
			
		||||
use embassy_sync::mutex::{Mutex, MutexGuard};
 | 
			
		||||
use embassy_sync::once_lock::OnceLock;
 | 
			
		||||
use embassy_time::Timer;
 | 
			
		||||
use esp_bootloader_esp_idf::ota::OtaImageState;
 | 
			
		||||
use esp_hal::rom::ets_delay_us;
 | 
			
		||||
use esp_hal::system::software_reset;
 | 
			
		||||
use esp_println::{logger, println};
 | 
			
		||||
use hal::battery::BatteryState;
 | 
			
		||||
use log::{ LogMessage};
 | 
			
		||||
use log::LogMessage;
 | 
			
		||||
use plant_state::PlantState;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::hal::esp_time;
 | 
			
		||||
use crate::log::LOG_ACCESS;
 | 
			
		||||
 | 
			
		||||
#[no_mangle]
 | 
			
		||||
extern "C" fn custom_halt() -> ! {
 | 
			
		||||
@@ -56,6 +58,7 @@ extern "C" fn custom_halt() -> ! {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//use tank::*;
 | 
			
		||||
mod FatError;
 | 
			
		||||
mod config;
 | 
			
		||||
mod hal;
 | 
			
		||||
mod log;
 | 
			
		||||
@@ -146,7 +149,7 @@ enum NetworkMode {
 | 
			
		||||
    OFFLINE,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn safe_main(spawner: Spawner) -> anyhow::Result<()> {
 | 
			
		||||
async fn safe_main(spawner: Spawner) -> FatResult<()> {
 | 
			
		||||
    info!("Startup Rust");
 | 
			
		||||
 | 
			
		||||
    let mut to_config = false;
 | 
			
		||||
@@ -183,28 +186,34 @@ async fn safe_main(spawner: Spawner) -> anyhow::Result<()> {
 | 
			
		||||
    let _ota_state_string = "unknown";
 | 
			
		||||
 | 
			
		||||
    board.board_hal.general_fault(false).await;
 | 
			
		||||
    let time = esp_time().await;
 | 
			
		||||
    let cur = board
 | 
			
		||||
        .board_hal
 | 
			
		||||
        .get_rtc_module()
 | 
			
		||||
        .get_rtc_time()
 | 
			
		||||
        .await
 | 
			
		||||
        .or_else(|err| {
 | 
			
		||||
    let cur = match board.board_hal.get_rtc_module().get_rtc_time().await {
 | 
			
		||||
        Ok(value) => value,
 | 
			
		||||
        Err(err) => {
 | 
			
		||||
            info!("rtc module error: {:?}", err);
 | 
			
		||||
            board.board_hal.general_fault(true);
 | 
			
		||||
            anyhow::Ok(time)
 | 
			
		||||
        })?;
 | 
			
		||||
            board.board_hal.general_fault(true).await;
 | 
			
		||||
            esp_time().await
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //check if we know the time current > 2020 (plausibility checks, this code is newer than 2020)
 | 
			
		||||
    if cur.year() < 2020 {
 | 
			
		||||
        to_config = true;
 | 
			
		||||
        LOG_ACCESS.lock().await.log(LogMessage::YearInplausibleForceConfig, 0, 0, "", "").await;
 | 
			
		||||
        LOG_ACCESS
 | 
			
		||||
            .lock()
 | 
			
		||||
            .await
 | 
			
		||||
            .log(LogMessage::YearInplausibleForceConfig, 0, 0, "", "")
 | 
			
		||||
            .await;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    info!("cur is {}", cur);
 | 
			
		||||
    update_charge_indicator(&mut board).await;
 | 
			
		||||
    println!("faul led3");
 | 
			
		||||
    if board.board_hal.get_esp().get_restart_to_conf() {
 | 
			
		||||
        LOG_ACCESS.lock().await.log(LogMessage::ConfigModeSoftwareOverride, 0, 0, "", "").await;
 | 
			
		||||
        LOG_ACCESS
 | 
			
		||||
            .lock()
 | 
			
		||||
            .await
 | 
			
		||||
            .log(LogMessage::ConfigModeSoftwareOverride, 0, 0, "", "")
 | 
			
		||||
            .await;
 | 
			
		||||
        for _i in 0..2 {
 | 
			
		||||
            board.board_hal.general_fault(true).await;
 | 
			
		||||
            Timer::after_millis(100).await;
 | 
			
		||||
@@ -216,7 +225,11 @@ async fn safe_main(spawner: Spawner) -> anyhow::Result<()> {
 | 
			
		||||
        board.board_hal.get_esp().set_restart_to_conf(false);
 | 
			
		||||
    } else if board.board_hal.get_esp().mode_override_pressed() {
 | 
			
		||||
        board.board_hal.general_fault(true).await;
 | 
			
		||||
        LOG_ACCESS.lock().await.log(LogMessage::ConfigModeButtonOverride, 0, 0, "", "").await;
 | 
			
		||||
        LOG_ACCESS
 | 
			
		||||
            .lock()
 | 
			
		||||
            .await
 | 
			
		||||
            .log(LogMessage::ConfigModeButtonOverride, 0, 0, "", "")
 | 
			
		||||
            .await;
 | 
			
		||||
        for _i in 0..5 {
 | 
			
		||||
            board.board_hal.general_fault(true).await;
 | 
			
		||||
            Timer::after_millis(100).await;
 | 
			
		||||
@@ -297,22 +310,25 @@ async fn safe_main(spawner: Spawner) -> anyhow::Result<()> {
 | 
			
		||||
        let _ = publish_mppt_state(&mut board).await;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LOG_ACCESS.lock().await.log(
 | 
			
		||||
        LogMessage::StartupInfo,
 | 
			
		||||
        matches!(network_mode, NetworkMode::WIFI { .. }) as u32,
 | 
			
		||||
        matches!(
 | 
			
		||||
            network_mode,
 | 
			
		||||
            NetworkMode::WIFI {
 | 
			
		||||
                sntp: SntpMode::SYNC { .. },
 | 
			
		||||
                ..
 | 
			
		||||
            }
 | 
			
		||||
        ) as u32,
 | 
			
		||||
        matches!(network_mode, NetworkMode::WIFI { mqtt: true, .. })
 | 
			
		||||
            .to_string()
 | 
			
		||||
            .as_str(),
 | 
			
		||||
        "",
 | 
			
		||||
    ).await
 | 
			
		||||
    ;
 | 
			
		||||
    LOG_ACCESS
 | 
			
		||||
        .lock()
 | 
			
		||||
        .await
 | 
			
		||||
        .log(
 | 
			
		||||
            LogMessage::StartupInfo,
 | 
			
		||||
            matches!(network_mode, NetworkMode::WIFI { .. }) as u32,
 | 
			
		||||
            matches!(
 | 
			
		||||
                network_mode,
 | 
			
		||||
                NetworkMode::WIFI {
 | 
			
		||||
                    sntp: SntpMode::SYNC { .. },
 | 
			
		||||
                    ..
 | 
			
		||||
                }
 | 
			
		||||
            ) as u32,
 | 
			
		||||
            matches!(network_mode, NetworkMode::WIFI { mqtt: true, .. })
 | 
			
		||||
                .to_string()
 | 
			
		||||
                .as_str(),
 | 
			
		||||
            "",
 | 
			
		||||
        )
 | 
			
		||||
        .await;
 | 
			
		||||
 | 
			
		||||
    if to_config {
 | 
			
		||||
        //check if client or ap mode and init Wi-Fi
 | 
			
		||||
@@ -323,7 +339,11 @@ async fn safe_main(spawner: Spawner) -> anyhow::Result<()> {
 | 
			
		||||
        let board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
        wait_infinity(board, WaitType::ConfigButton, reboot_now.clone()).await;
 | 
			
		||||
    } else {
 | 
			
		||||
        LOG_ACCESS.lock().await.log(LogMessage::NormalRun, 0, 0, "", "").await;
 | 
			
		||||
        LOG_ACCESS
 | 
			
		||||
            .lock()
 | 
			
		||||
            .await
 | 
			
		||||
            .log(LogMessage::NormalRun, 0, 0, "", "")
 | 
			
		||||
            .await;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let _dry_run = false;
 | 
			
		||||
@@ -364,13 +384,10 @@ async fn safe_main(spawner: Spawner) -> anyhow::Result<()> {
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    let mut _water_frozen = false;
 | 
			
		||||
    //TODO
 | 
			
		||||
    let water_temp = anyhow::Ok(12_f32);
 | 
			
		||||
    // board
 | 
			
		||||
    //     .board_hal
 | 
			
		||||
    //     .get_tank_sensor()
 | 
			
		||||
    //     .context("no sensor")
 | 
			
		||||
    //     .and_then(async |f| f.water_temperature_c().await);
 | 
			
		||||
    let water_temp = board
 | 
			
		||||
        .board_hal
 | 
			
		||||
        .get_tank_sensor()
 | 
			
		||||
        .and_then(async |sensor| sensor.water_temperature_c().await);
 | 
			
		||||
 | 
			
		||||
    if let Ok(res) = water_temp {
 | 
			
		||||
        if res < WATER_FROZEN_THRESH {
 | 
			
		||||
@@ -615,7 +632,7 @@ pub async fn do_secure_pump(
 | 
			
		||||
    plant_id: usize,
 | 
			
		||||
    plant_config: &PlantConfig,
 | 
			
		||||
    dry_run: bool,
 | 
			
		||||
) -> anyhow::Result<PumpResult> {
 | 
			
		||||
) -> FatResult<PumpResult> {
 | 
			
		||||
    let mut current_collector = vec![0_u16; plant_config.pump_time_s.into()];
 | 
			
		||||
    let mut flow_collector = vec![0_i16; plant_config.pump_time_s.into()];
 | 
			
		||||
    let mut error = false;
 | 
			
		||||
@@ -662,14 +679,17 @@ pub async fn do_secure_pump(
 | 
			
		||||
                    let high_current = current_ma > plant_config.max_pump_current_ma;
 | 
			
		||||
                    if high_current {
 | 
			
		||||
                        if first_error {
 | 
			
		||||
                            LOG_ACCESS.lock().await.log(
 | 
			
		||||
                                LogMessage::PumpOverCurrent,
 | 
			
		||||
                                plant_id as u32 + 1,
 | 
			
		||||
                                current_ma as u32,
 | 
			
		||||
                                plant_config.max_pump_current_ma.to_string().as_str(),
 | 
			
		||||
                                step.to_string().as_str(),
 | 
			
		||||
                            ).await
 | 
			
		||||
                            ;
 | 
			
		||||
                            LOG_ACCESS
 | 
			
		||||
                                .lock()
 | 
			
		||||
                                .await
 | 
			
		||||
                                .log(
 | 
			
		||||
                                    LogMessage::PumpOverCurrent,
 | 
			
		||||
                                    plant_id as u32 + 1,
 | 
			
		||||
                                    current_ma as u32,
 | 
			
		||||
                                    plant_config.max_pump_current_ma.to_string().as_str(),
 | 
			
		||||
                                    step.to_string().as_str(),
 | 
			
		||||
                                )
 | 
			
		||||
                                .await;
 | 
			
		||||
                            board.board_hal.general_fault(true).await;
 | 
			
		||||
                            board.board_hal.fault(plant_id, true).await?;
 | 
			
		||||
                            if !plant_config.ignore_current_error {
 | 
			
		||||
@@ -682,14 +702,17 @@ pub async fn do_secure_pump(
 | 
			
		||||
                    let low_current = current_ma < plant_config.min_pump_current_ma;
 | 
			
		||||
                    if low_current {
 | 
			
		||||
                        if first_error {
 | 
			
		||||
                            LOG_ACCESS.lock().await.log(
 | 
			
		||||
                                LogMessage::PumpOpenLoopCurrent,
 | 
			
		||||
                                plant_id as u32 + 1,
 | 
			
		||||
                                current_ma as u32,
 | 
			
		||||
                                plant_config.min_pump_current_ma.to_string().as_str(),
 | 
			
		||||
                                step.to_string().as_str(),
 | 
			
		||||
                            ).await
 | 
			
		||||
                            ;
 | 
			
		||||
                            LOG_ACCESS
 | 
			
		||||
                                .lock()
 | 
			
		||||
                                .await
 | 
			
		||||
                                .log(
 | 
			
		||||
                                    LogMessage::PumpOpenLoopCurrent,
 | 
			
		||||
                                    plant_id as u32 + 1,
 | 
			
		||||
                                    current_ma as u32,
 | 
			
		||||
                                    plant_config.min_pump_current_ma.to_string().as_str(),
 | 
			
		||||
                                    step.to_string().as_str(),
 | 
			
		||||
                                )
 | 
			
		||||
                                .await;
 | 
			
		||||
                            board.board_hal.general_fault(true).await;
 | 
			
		||||
                            board.board_hal.fault(plant_id, true).await?;
 | 
			
		||||
                            if !plant_config.ignore_current_error {
 | 
			
		||||
@@ -703,14 +726,17 @@ pub async fn do_secure_pump(
 | 
			
		||||
                Err(err) => {
 | 
			
		||||
                    if !plant_config.ignore_current_error {
 | 
			
		||||
                        info!("Error getting pump current: {}", err);
 | 
			
		||||
                        LOG_ACCESS.lock().await.log(
 | 
			
		||||
                            LogMessage::PumpMissingSensorCurrent,
 | 
			
		||||
                            plant_id as u32,
 | 
			
		||||
                            0,
 | 
			
		||||
                            "",
 | 
			
		||||
                            "",
 | 
			
		||||
                        ).await
 | 
			
		||||
                        ;
 | 
			
		||||
                        LOG_ACCESS
 | 
			
		||||
                            .lock()
 | 
			
		||||
                            .await
 | 
			
		||||
                            .log(
 | 
			
		||||
                                LogMessage::PumpMissingSensorCurrent,
 | 
			
		||||
                                plant_id as u32,
 | 
			
		||||
                                0,
 | 
			
		||||
                                "",
 | 
			
		||||
                                "",
 | 
			
		||||
                            )
 | 
			
		||||
                            .await;
 | 
			
		||||
                        error = true;
 | 
			
		||||
                        break;
 | 
			
		||||
                    } else {
 | 
			
		||||
@@ -957,7 +983,7 @@ async fn pump_info(
 | 
			
		||||
 | 
			
		||||
async fn publish_mppt_state(
 | 
			
		||||
    board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>,
 | 
			
		||||
) -> anyhow::Result<()> {
 | 
			
		||||
) -> FatResult<()> {
 | 
			
		||||
    let current = board.board_hal.get_mptt_current().await?;
 | 
			
		||||
    let voltage = board.board_hal.get_mptt_voltage().await?;
 | 
			
		||||
    let solar_state = Solar {
 | 
			
		||||
@@ -983,13 +1009,18 @@ async fn publish_battery_state(
 | 
			
		||||
        .get_battery_monitor()
 | 
			
		||||
        .get_battery_state()
 | 
			
		||||
        .await;
 | 
			
		||||
    if let Ok(serialized_battery_state_bytes) =
 | 
			
		||||
        serde_json::to_string(&state).map(|s| s.into_bytes())
 | 
			
		||||
    let value = match state {
 | 
			
		||||
        Ok(state) => {
 | 
			
		||||
            let json = serde_json::to_string(&state).unwrap().to_owned();
 | 
			
		||||
            json.as_bytes().to_owned()
 | 
			
		||||
        }
 | 
			
		||||
        Err(_) => "error".as_bytes().to_owned(),
 | 
			
		||||
    };
 | 
			
		||||
    {
 | 
			
		||||
        board
 | 
			
		||||
        let _ = board
 | 
			
		||||
            .board_hal
 | 
			
		||||
            .get_esp()
 | 
			
		||||
            .mqtt_publish("/battery", &serialized_battery_state_bytes)
 | 
			
		||||
            .mqtt_publish("/battery", &*value)
 | 
			
		||||
            .await;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1062,13 +1093,14 @@ async fn wait_infinity(
 | 
			
		||||
                .lock()
 | 
			
		||||
                .await
 | 
			
		||||
                .board_hal
 | 
			
		||||
                .deep_sleep(0).await;
 | 
			
		||||
                .deep_sleep(0)
 | 
			
		||||
                .await;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[esp_hal_embassy::main]
 | 
			
		||||
async fn main(spawner: Spawner) {
 | 
			
		||||
async fn main(spawner: Spawner) -> ! {
 | 
			
		||||
    // intialize embassy
 | 
			
		||||
    logger::init_logger_from_env();
 | 
			
		||||
    //force init here!
 | 
			
		||||
@@ -1082,21 +1114,13 @@ async fn main(spawner: Spawner) {
 | 
			
		||||
    println!("Hal init done, starting logic");
 | 
			
		||||
 | 
			
		||||
    match safe_main(spawner).await {
 | 
			
		||||
        // this should not get triggered, safe_main should not return but go into deep sleep with sensible
 | 
			
		||||
        // timeout, this is just a fallback
 | 
			
		||||
        // this should not get triggered, safe_main should not return but go into deep sleep or reboot
 | 
			
		||||
        Ok(_) => {
 | 
			
		||||
            warn!("Main app finished, but should never do, restarting");
 | 
			
		||||
            let board = &mut BOARD_ACCESS.get().await.lock().await.board_hal;
 | 
			
		||||
 | 
			
		||||
            board.get_esp().set_restart_to_conf(false);
 | 
			
		||||
            board.deep_sleep(1);
 | 
			
		||||
            panic!("Main app finished, but should never do, restarting");
 | 
			
		||||
        }
 | 
			
		||||
        // if safe_main exists with an error, rollback to a known good ota version
 | 
			
		||||
        Err(err) => {
 | 
			
		||||
            error!("Failed main {}", err);
 | 
			
		||||
            //TODO
 | 
			
		||||
            //let _rollback_successful = rollback_and_reboot();
 | 
			
		||||
            //panic!("Failed to rollback :(");
 | 
			
		||||
            panic!("Failed main {}", err);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,9 @@
 | 
			
		||||
use crate::alloc::string::{String, ToString};
 | 
			
		||||
use crate::config::TankConfig;
 | 
			
		||||
use crate::hal::HAL;
 | 
			
		||||
use crate::FatError::FatResult;
 | 
			
		||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
 | 
			
		||||
use embassy_sync::mutex::MutexGuard;
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
 | 
			
		||||
const OPEN_TANK_VOLTAGE: f32 = 3.0;
 | 
			
		||||
@@ -113,7 +117,7 @@ impl TankState {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn as_mqtt_info(&self, config: &TankConfig, water_temp: &anyhow::Result<f32>) -> TankInfo {
 | 
			
		||||
    pub fn as_mqtt_info(&self, config: &TankConfig, water_temp: &FatResult<f32>) -> TankInfo {
 | 
			
		||||
        let mut tank_err: Option<TankError> = None;
 | 
			
		||||
        let left_ml = match self.left_ml(config) {
 | 
			
		||||
            Err(err) => {
 | 
			
		||||
@@ -150,40 +154,41 @@ impl TankState {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// pub fn determine_tank_state(board: &mut std::sync::MutexGuard<'_, HAL<'_>>) -> TankState {
 | 
			
		||||
//     if board.board_hal.get_config().tank.tank_sensor_enabled {
 | 
			
		||||
//         match board
 | 
			
		||||
//             .board_hal
 | 
			
		||||
//             .get_tank_sensor()
 | 
			
		||||
//             .context("no sensor")
 | 
			
		||||
//             .and_then(|f| f.tank_sensor_voltage())
 | 
			
		||||
//         {
 | 
			
		||||
//             Ok(raw_sensor_value_mv) => TankState::Present(raw_sensor_value_mv),
 | 
			
		||||
//             Err(err) => TankState::Error(TankError::BoardError(err.to_string())),
 | 
			
		||||
//         }
 | 
			
		||||
//     } else {
 | 
			
		||||
//         TankState::Disabled
 | 
			
		||||
//     }
 | 
			
		||||
// }
 | 
			
		||||
pub async fn determine_tank_state(
 | 
			
		||||
    board: &mut MutexGuard<'static, CriticalSectionRawMutex, HAL<'static>>,
 | 
			
		||||
) -> TankState {
 | 
			
		||||
    if board.board_hal.get_config().tank.tank_sensor_enabled {
 | 
			
		||||
        match board
 | 
			
		||||
            .board_hal
 | 
			
		||||
            .get_tank_sensor()
 | 
			
		||||
            .and_then(|f| core::prelude::v1::Ok(f.tank_sensor_voltage()))
 | 
			
		||||
        {
 | 
			
		||||
            Ok(raw_sensor_value_mv) => TankState::Present(raw_sensor_value_mv.await.unwrap()),
 | 
			
		||||
            Err(err) => TankState::Error(TankError::BoardError(err.to_string())),
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        TankState::Disabled
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize)]
 | 
			
		||||
/// Information structure send to mqtt for monitoring purposes
 | 
			
		||||
pub struct TankInfo {
 | 
			
		||||
    /// there is enough water in the tank
 | 
			
		||||
    enough_water: bool,
 | 
			
		||||
    pub(crate) enough_water: bool,
 | 
			
		||||
    /// warning that water needs to be refilled soon
 | 
			
		||||
    warn_level: bool,
 | 
			
		||||
    pub(crate) warn_level: bool,
 | 
			
		||||
    /// estimation how many ml are still in the tank
 | 
			
		||||
    left_ml: Option<f32>,
 | 
			
		||||
    pub(crate) left_ml: Option<f32>,
 | 
			
		||||
    /// if there is an issue with the water level sensor
 | 
			
		||||
    sensor_error: Option<TankError>,
 | 
			
		||||
    pub(crate) sensor_error: Option<TankError>,
 | 
			
		||||
    /// raw water sensor value
 | 
			
		||||
    raw: Option<f32>,
 | 
			
		||||
    pub(crate) raw: Option<f32>,
 | 
			
		||||
    /// percent value
 | 
			
		||||
    percent: Option<f32>,
 | 
			
		||||
    pub(crate) percent: Option<f32>,
 | 
			
		||||
    /// water in the tank might be frozen
 | 
			
		||||
    water_frozen: bool,
 | 
			
		||||
    pub(crate) water_frozen: bool,
 | 
			
		||||
    /// water temperature
 | 
			
		||||
    water_temp: Option<f32>,
 | 
			
		||||
    temp_sensor_error: Option<String>,
 | 
			
		||||
    pub(crate) water_temp: Option<f32>,
 | 
			
		||||
    pub(crate) temp_sensor_error: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,23 +1,25 @@
 | 
			
		||||
//offer ota and config mode
 | 
			
		||||
 | 
			
		||||
use crate::config::PlantControllerConfig;
 | 
			
		||||
use crate::{get_version, log::LogMessage, BOARD_ACCESS};
 | 
			
		||||
use crate::hal::{esp_set_time, esp_time};
 | 
			
		||||
use crate::log::LOG_ACCESS;
 | 
			
		||||
use crate::tank::{determine_tank_state, TankInfo};
 | 
			
		||||
use crate::FatError::{FatError, FatResult};
 | 
			
		||||
use crate::{bail, get_version, log::LogMessage, BOARD_ACCESS};
 | 
			
		||||
use alloc::borrow::ToOwned;
 | 
			
		||||
use alloc::format;
 | 
			
		||||
use alloc::string::{String, ToString};
 | 
			
		||||
use alloc::sync::Arc;
 | 
			
		||||
use alloc::vec::Vec;
 | 
			
		||||
use anyhow::{bail};
 | 
			
		||||
use chrono::DateTime;
 | 
			
		||||
use core::fmt::{Debug, Display};
 | 
			
		||||
use core::net::{IpAddr, Ipv4Addr, SocketAddr};
 | 
			
		||||
use core::result::Result::Ok;
 | 
			
		||||
use core::str::from_utf8;
 | 
			
		||||
use core::sync::atomic::{AtomicBool, Ordering};
 | 
			
		||||
use chrono::DateTime;
 | 
			
		||||
use edge_http::io::server::{Connection, Handler, Server};
 | 
			
		||||
use edge_http::io::Error;
 | 
			
		||||
use edge_http::Method;
 | 
			
		||||
use edge_nal::{TcpBind};
 | 
			
		||||
use edge_nal::TcpBind;
 | 
			
		||||
use edge_nal_embassy::{Tcp, TcpBuffers};
 | 
			
		||||
use embassy_net::Stack;
 | 
			
		||||
use embassy_time::Instant;
 | 
			
		||||
@@ -25,8 +27,6 @@ use embedded_io_async::{Read, Write};
 | 
			
		||||
use esp_println::println;
 | 
			
		||||
use log::info;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::hal::{esp_set_time, esp_time};
 | 
			
		||||
use crate::log::{LOG_ACCESS};
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Debug)]
 | 
			
		||||
struct SSIDList {
 | 
			
		||||
@@ -191,23 +191,6 @@ pub struct NightLampCommand {
 | 
			
		||||
//     anyhow::Ok(Some(serde_json::to_string(&pump_result)?))
 | 
			
		||||
// }
 | 
			
		||||
//
 | 
			
		||||
// fn tank_info(
 | 
			
		||||
//     _request: &mut Request<&mut EspHttpConnection>,
 | 
			
		||||
// ) -> Result<Option<std::string::String>, anyhow::Error> {
 | 
			
		||||
//     let mut board = BOARD_ACCESS.lock().unwrap();
 | 
			
		||||
//     let tank_info = determine_tank_state(&mut board);
 | 
			
		||||
//     //should be multsampled
 | 
			
		||||
//
 | 
			
		||||
//     let water_temp = board
 | 
			
		||||
//         .board_hal
 | 
			
		||||
//         .get_tank_sensor()
 | 
			
		||||
//         .context("no sensor")
 | 
			
		||||
//         .and_then(|f| f.water_temperature_c());
 | 
			
		||||
//     Ok(Some(serde_json::to_string(&tank_info.as_mqtt_info(
 | 
			
		||||
//         &board.board_hal.get_config().tank,
 | 
			
		||||
//         &water_temp,
 | 
			
		||||
//     ))?))
 | 
			
		||||
// }
 | 
			
		||||
//
 | 
			
		||||
// fn night_lamp_test(
 | 
			
		||||
//     request: &mut Request<&mut EspHttpConnection>,
 | 
			
		||||
@@ -285,12 +268,12 @@ struct HttpHandler {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Handler for HttpHandler {
 | 
			
		||||
    type Error<E: core::fmt::Debug> = Error<E>;
 | 
			
		||||
    type Error<E: Debug> = FatError;
 | 
			
		||||
    async fn handle<'a, T, const N: usize>(
 | 
			
		||||
        &self,
 | 
			
		||||
        _task_id: impl Display + Copy,
 | 
			
		||||
        conn: &mut Connection<'a, T, N>,
 | 
			
		||||
    ) -> anyhow::Result<(), Self::Error<T::Error>>
 | 
			
		||||
    ) -> Result<(), FatError>
 | 
			
		||||
    where
 | 
			
		||||
        T: Read + Write,
 | 
			
		||||
    {
 | 
			
		||||
@@ -303,46 +286,55 @@ impl Handler for HttpHandler {
 | 
			
		||||
        let prefix = "/file?filename=";
 | 
			
		||||
        let status = if path.starts_with(prefix) {
 | 
			
		||||
            let filename = &path[prefix.len()..];
 | 
			
		||||
            let mut board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
            info!("file request for {} with method {}", filename, method);
 | 
			
		||||
            match method {
 | 
			
		||||
                Method::Delete => {
 | 
			
		||||
                    let mut board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
                    board
 | 
			
		||||
                        .board_hal
 | 
			
		||||
                        .get_esp()
 | 
			
		||||
                        .delete_file(filename.to_owned())
 | 
			
		||||
                        .await
 | 
			
		||||
                        .unwrap();
 | 
			
		||||
                        .await?;
 | 
			
		||||
                    Some(200)
 | 
			
		||||
                }
 | 
			
		||||
                Method::Get => {
 | 
			
		||||
                    let disp = format!("attachment; filename=\"{filename}\"");
 | 
			
		||||
                    let size = {
 | 
			
		||||
                        let mut board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
                        board
 | 
			
		||||
                            .board_hal
 | 
			
		||||
                            .get_esp()
 | 
			
		||||
                            .get_size(filename.to_owned())
 | 
			
		||||
                            .await?
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    conn.initiate_response(
 | 
			
		||||
                        200,
 | 
			
		||||
                        Some("OK"),
 | 
			
		||||
                        &[
 | 
			
		||||
                            ("Content-Type", "application/octet-stream"),
 | 
			
		||||
                            ("Content-Disposition", disp.as_str()),
 | 
			
		||||
                            ("Content-Length", &format!("{}", size)),
 | 
			
		||||
                        ],
 | 
			
		||||
                    )
 | 
			
		||||
                    .await?;
 | 
			
		||||
 | 
			
		||||
                    let mut chunk = 0;
 | 
			
		||||
                    loop {
 | 
			
		||||
                        let mut board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
                        let read_chunk = board
 | 
			
		||||
                            .board_hal
 | 
			
		||||
                            .get_esp()
 | 
			
		||||
                            .get_file(filename.to_owned(), chunk)
 | 
			
		||||
                            .await
 | 
			
		||||
                            .unwrap();
 | 
			
		||||
                            .await?;
 | 
			
		||||
                        let length = read_chunk.1;
 | 
			
		||||
                        info!("read {} bytes for file request for {}", length, filename);
 | 
			
		||||
                        if length == 0 {
 | 
			
		||||
                            info!("file request for {} finished", filename);
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                        let data = &read_chunk.0[0..length];
 | 
			
		||||
                        conn.write_all(data).await?;
 | 
			
		||||
                        if length < 128 {
 | 
			
		||||
                        if length < read_chunk.0.len() {
 | 
			
		||||
                            info!("file request for {} finished", filename);
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
@@ -351,12 +343,16 @@ impl Handler for HttpHandler {
 | 
			
		||||
                    Some(200)
 | 
			
		||||
                }
 | 
			
		||||
                Method::Post => {
 | 
			
		||||
                    //ensure file is deleted, otherwise we would need to truncate the file which will not work with streaming
 | 
			
		||||
                    let _ = board
 | 
			
		||||
                        .board_hal
 | 
			
		||||
                        .get_esp()
 | 
			
		||||
                        .delete_file(filename.to_owned())
 | 
			
		||||
                        .await;
 | 
			
		||||
                    {
 | 
			
		||||
                        let mut board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
                        //ensure file is deleted, otherwise we would need to truncate the file which will not work with streaming
 | 
			
		||||
                        let _ = board
 | 
			
		||||
                            .board_hal
 | 
			
		||||
                            .get_esp()
 | 
			
		||||
                            .delete_file(filename.to_owned())
 | 
			
		||||
                            .await;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    let mut offset = 0_usize;
 | 
			
		||||
                    loop {
 | 
			
		||||
                        let mut buf = [0_u8; 1024];
 | 
			
		||||
@@ -365,16 +361,12 @@ impl Handler for HttpHandler {
 | 
			
		||||
                            info!("file request for {} finished", filename);
 | 
			
		||||
                            break;
 | 
			
		||||
                        } else {
 | 
			
		||||
                            info!(
 | 
			
		||||
                                "writing {} bytes for file request for {}",
 | 
			
		||||
                                to_write, filename
 | 
			
		||||
                            );
 | 
			
		||||
                            let mut board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
                            board
 | 
			
		||||
                                .board_hal
 | 
			
		||||
                                .get_esp()
 | 
			
		||||
                                .write_file(filename.to_owned(), offset as u32, &buf[0..to_write])
 | 
			
		||||
                                .await
 | 
			
		||||
                                .unwrap();
 | 
			
		||||
                                .await?;
 | 
			
		||||
                        }
 | 
			
		||||
                        offset = offset + to_write
 | 
			
		||||
                    }
 | 
			
		||||
@@ -397,25 +389,32 @@ impl Handler for HttpHandler {
 | 
			
		||||
                        Some(200)
 | 
			
		||||
                    }
 | 
			
		||||
                    "/" => {
 | 
			
		||||
                        conn.initiate_response(200, Some("OK"), &[("Content-Type", "text/html")])
 | 
			
		||||
                            .await?;
 | 
			
		||||
                        conn.write_all(include_bytes!("index.html")).await?;
 | 
			
		||||
                        conn.initiate_response(
 | 
			
		||||
                            200,
 | 
			
		||||
                            Some("OK"),
 | 
			
		||||
                            &[("Content-Type", "text/html"), ("Content-Encoding", "gzip")],
 | 
			
		||||
                        )
 | 
			
		||||
                        .await?;
 | 
			
		||||
                        conn.write_all(include_bytes!("index.html.gz")).await?;
 | 
			
		||||
                        Some(200)
 | 
			
		||||
                    }
 | 
			
		||||
                    "/bundle.js" => {
 | 
			
		||||
                        conn.initiate_response(
 | 
			
		||||
                            200,
 | 
			
		||||
                            Some("OK"),
 | 
			
		||||
                            &[("Content-Type", "text/javascript")],
 | 
			
		||||
                            &[
 | 
			
		||||
                                ("Content-Type", "text/javascript"),
 | 
			
		||||
                                ("Content-Encoding", "gzip"),
 | 
			
		||||
                            ],
 | 
			
		||||
                        )
 | 
			
		||||
                        .await?;
 | 
			
		||||
                        conn.write_all(include_bytes!("bundle.js")).await?;
 | 
			
		||||
                        conn.write_all(include_bytes!("bundle.js.gz")).await?;
 | 
			
		||||
                        Some(200)
 | 
			
		||||
                    }
 | 
			
		||||
                    "/log" => {
 | 
			
		||||
                        let buf = get_log(conn).await;
 | 
			
		||||
                        Some(200)
 | 
			
		||||
                    },
 | 
			
		||||
                    }
 | 
			
		||||
                    &_ => {
 | 
			
		||||
                        let json = match path {
 | 
			
		||||
                            "/version" => Some(get_version_web(conn).await),
 | 
			
		||||
@@ -554,23 +553,40 @@ impl Handler for HttpHandler {
 | 
			
		||||
//     })
 | 
			
		||||
//     .unwrap();
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
    determine_tank_state(&mut board);
 | 
			
		||||
    //should be multsampled
 | 
			
		||||
    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(
 | 
			
		||||
        &board.board_hal.get_config().tank,
 | 
			
		||||
        &water_temp,
 | 
			
		||||
    ))?))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn write_time<T, const N: usize>(
 | 
			
		||||
    request: &mut Connection<'_, T, N>,
 | 
			
		||||
) -> Result<Option<String>, anyhow::Error>
 | 
			
		||||
) -> FatResult<Option<String>>
 | 
			
		||||
where
 | 
			
		||||
    T: Read + Write
 | 
			
		||||
    T: Read + Write,
 | 
			
		||||
{
 | 
			
		||||
    let actual_data = read_up_to_bytes_from_request(request, None).await?;
 | 
			
		||||
    let time: SetTime = serde_json::from_slice(&actual_data)?;
 | 
			
		||||
    let parsed = DateTime::parse_from_rfc3339(time.time).unwrap();
 | 
			
		||||
    esp_set_time(parsed).await;
 | 
			
		||||
    anyhow::Ok(None)
 | 
			
		||||
    Ok(None)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async fn set_config<T, const N: usize>(
 | 
			
		||||
    request: &mut Connection<'_, T, N>,
 | 
			
		||||
) -> Result<Option<String>, anyhow::Error>
 | 
			
		||||
) -> FatResult<Option<String>>
 | 
			
		||||
where
 | 
			
		||||
    T: Read + Write,
 | 
			
		||||
{
 | 
			
		||||
@@ -582,13 +598,13 @@ where
 | 
			
		||||
    board.board_hal.get_esp().save_config(all).await?;
 | 
			
		||||
    log::info!("Wrote config config {:?} with size {}", config, length);
 | 
			
		||||
    board.board_hal.set_config(config);
 | 
			
		||||
    anyhow::Ok(Some("saved".to_string()))
 | 
			
		||||
    Ok(Some("saved".to_string()))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn read_up_to_bytes_from_request<T, const N: usize>(
 | 
			
		||||
    request: &mut Connection<'_, T, N>,
 | 
			
		||||
    limit: Option<usize>,
 | 
			
		||||
) -> Result<Vec<u8>, anyhow::Error>
 | 
			
		||||
) -> FatResult<Vec<u8>>
 | 
			
		||||
where
 | 
			
		||||
    T: Read + Write,
 | 
			
		||||
{
 | 
			
		||||
@@ -597,10 +613,7 @@ where
 | 
			
		||||
    let mut total_read = 0;
 | 
			
		||||
    loop {
 | 
			
		||||
        let mut buf = [0_u8; 64];
 | 
			
		||||
        let read = match request.read(&mut buf).await {
 | 
			
		||||
            Ok(read) => read,
 | 
			
		||||
            Err(e) => bail!("Error reading request {:?}", e),
 | 
			
		||||
        };
 | 
			
		||||
        let read = request.read(&mut buf).await?;
 | 
			
		||||
        if read == 0 {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
@@ -618,7 +631,7 @@ where
 | 
			
		||||
 | 
			
		||||
async fn wifi_scan<T, const N: usize>(
 | 
			
		||||
    _request: &mut Connection<'_, T, N>,
 | 
			
		||||
) -> Result<Option<String>, anyhow::Error> {
 | 
			
		||||
) -> FatResult<Option<String>> {
 | 
			
		||||
    let mut board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
    info!("start wifi scan");
 | 
			
		||||
    //let scan_result = board.board_hal.get_esp().wifi_scan().await?
 | 
			
		||||
@@ -629,93 +642,87 @@ async fn wifi_scan<T, const N: usize>(
 | 
			
		||||
    //.for_each(|s| ssids.push(s.ssid.to_string()));
 | 
			
		||||
    let ssid_json = serde_json::to_string(&SSIDList { ssids })?;
 | 
			
		||||
    info!("Sending ssid list {}", &ssid_json);
 | 
			
		||||
    anyhow::Ok(Some(ssid_json))
 | 
			
		||||
    Ok(Some(ssid_json))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn get_log<T, const N: usize>(
 | 
			
		||||
    conn: &mut Connection<'_, T, N>,
 | 
			
		||||
) -> anyhow::Result<()>
 | 
			
		||||
async fn get_log<T, const N: usize>(conn: &mut Connection<'_, T, N>) -> FatResult<()>
 | 
			
		||||
where
 | 
			
		||||
    T: Read + Write,{
 | 
			
		||||
    T: Read + Write,
 | 
			
		||||
{
 | 
			
		||||
    let log = LOG_ACCESS.lock().await.get();
 | 
			
		||||
    conn.initiate_response(
 | 
			
		||||
        200,
 | 
			
		||||
        Some("OK"),
 | 
			
		||||
        &[("Content-Type", "text/javascript")],
 | 
			
		||||
    )
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
    conn.write_all("[".as_bytes()).await.unwrap();
 | 
			
		||||
    conn.initiate_response(200, Some("OK"), &[("Content-Type", "text/javascript")])
 | 
			
		||||
        .await?;
 | 
			
		||||
    conn.write_all("[".as_bytes()).await?;
 | 
			
		||||
    let mut append = false;
 | 
			
		||||
    for  entry in log {
 | 
			
		||||
    for entry in log {
 | 
			
		||||
        if append {
 | 
			
		||||
            conn.write_all(",".as_bytes()).await.unwrap();
 | 
			
		||||
            conn.write_all(",".as_bytes()).await?;
 | 
			
		||||
        }
 | 
			
		||||
        append = true;
 | 
			
		||||
        let json = serde_json::to_string(&entry)?;
 | 
			
		||||
        conn.write_all(json.as_bytes()).await.unwrap();
 | 
			
		||||
        conn.write_all(json.as_bytes()).await?;
 | 
			
		||||
    }
 | 
			
		||||
    conn.write_all("]".as_bytes()).await.unwrap();
 | 
			
		||||
    conn.write_all("]".as_bytes()).await?;
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn get_log_localization_config<T, const N: usize>(
 | 
			
		||||
    _request: &mut Connection<'_, T, N>,
 | 
			
		||||
) -> Result<Option<String>, anyhow::Error> {
 | 
			
		||||
    anyhow::Ok(Some(serde_json::to_string(
 | 
			
		||||
) -> FatResult<Option<String>> {
 | 
			
		||||
    Ok(Some(serde_json::to_string(
 | 
			
		||||
        &LogMessage::to_log_localisation_config(),
 | 
			
		||||
    )?))
 | 
			
		||||
}
 | 
			
		||||
async fn list_files<T, const N: usize>(
 | 
			
		||||
    _request: &mut Connection<'_, T, N>,
 | 
			
		||||
) -> Result<Option<String>, anyhow::Error> {
 | 
			
		||||
) -> FatResult<Option<String>> {
 | 
			
		||||
    let mut board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
    let result = board.board_hal.get_esp().list_files().await?;
 | 
			
		||||
    let file_list_json = serde_json::to_string(&result)?;
 | 
			
		||||
    anyhow::Ok(Some(file_list_json))
 | 
			
		||||
    Ok(Some(file_list_json))
 | 
			
		||||
}
 | 
			
		||||
async fn get_config<T, const N: usize>(
 | 
			
		||||
    _request: &mut Connection<'_, T, N>,
 | 
			
		||||
) -> Result<Option<String>, anyhow::Error> {
 | 
			
		||||
) -> FatResult<Option<String>> {
 | 
			
		||||
    let mut board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
    let json = serde_json::to_string(&board.board_hal.get_config())?;
 | 
			
		||||
    anyhow::Ok(Some(json))
 | 
			
		||||
    Ok(Some(json))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn get_solar_state<T, const N: usize>(
 | 
			
		||||
    _request: &mut Connection<'_, T, N>,
 | 
			
		||||
) -> Result<Option<String>, anyhow::Error> {
 | 
			
		||||
) -> FatResult<Option<String>> {
 | 
			
		||||
    let mut board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
    let state = SolarState {
 | 
			
		||||
        mppt_voltage: board.board_hal.get_mptt_voltage().await?.as_millivolts() as f32,
 | 
			
		||||
        mppt_current: board.board_hal.get_mptt_current().await?.as_milliamperes() as f32,
 | 
			
		||||
        is_day: board.board_hal.is_day(),
 | 
			
		||||
    };
 | 
			
		||||
    anyhow::Ok(Some(serde_json::to_string(&state)?))
 | 
			
		||||
    Ok(Some(serde_json::to_string(&state)?))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn get_battery_state<T, const N: usize>(
 | 
			
		||||
    _request: &mut Connection<'_, T, N>,
 | 
			
		||||
) -> Result<Option<String>, anyhow::Error> {
 | 
			
		||||
) -> FatResult<Option<String>> {
 | 
			
		||||
    let mut board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
    let battery_state = board
 | 
			
		||||
        .board_hal
 | 
			
		||||
        .get_battery_monitor()
 | 
			
		||||
        .get_battery_state()
 | 
			
		||||
        .await?;
 | 
			
		||||
    anyhow::Ok(Some(serde_json::to_string(&battery_state)?))
 | 
			
		||||
    Ok(Some(serde_json::to_string(&battery_state)?))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn get_version_web<T, const N: usize>(
 | 
			
		||||
    _request: &mut Connection<'_, T, N>,
 | 
			
		||||
) -> Result<Option<String>, anyhow::Error> {
 | 
			
		||||
) -> FatResult<Option<String>> {
 | 
			
		||||
    let mut board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
    anyhow::Ok(Some(serde_json::to_string(&get_version(&mut board).await)?))
 | 
			
		||||
    Ok(Some(serde_json::to_string(&get_version(&mut board).await)?))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn get_time<T, const N: usize>(
 | 
			
		||||
    _request: &mut Connection<'_, T, N>,
 | 
			
		||||
) -> Result<Option<String>, anyhow::Error> {
 | 
			
		||||
) -> FatResult<Option<String>> {
 | 
			
		||||
    let mut board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
    //TODO do not fail if rtc module is missing
 | 
			
		||||
    let native = esp_time().await.to_rfc3339();
 | 
			
		||||
@@ -733,7 +740,7 @@ async fn get_time<T, const N: usize>(
 | 
			
		||||
    };
 | 
			
		||||
    let json = serde_json::to_string(&data)?;
 | 
			
		||||
 | 
			
		||||
    anyhow::Ok(Some(json))
 | 
			
		||||
    Ok(Some(json))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[embassy_executor::task]
 | 
			
		||||
@@ -943,8 +950,8 @@ pub async fn httpd(reboot_now: Arc<AtomicBool>, stack: Stack<'static>) {
 | 
			
		||||
 | 
			
		||||
async fn handle_json<'a, T, const N: usize>(
 | 
			
		||||
    conn: &mut Connection<'a, T, N>,
 | 
			
		||||
    chain: anyhow::Result<Option<String>>,
 | 
			
		||||
) -> anyhow::Result<u32, Error<T::Error>>
 | 
			
		||||
    chain: FatResult<Option<String>>,
 | 
			
		||||
) -> FatResult<u32>
 | 
			
		||||
where
 | 
			
		||||
    T: Read + Write,
 | 
			
		||||
    <T as embedded_io_async::ErrorType>::Error: Debug,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user