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