new ota logic
This commit is contained in:
1
Software/MainBoard/rust/.idea/vcs.xml
generated
1
Software/MainBoard/rust/.idea/vcs.xml
generated
@@ -2,5 +2,6 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="$PROJECT_DIR$/../../.." vcs="Git" />
|
<mapping directory="$PROJECT_DIR$/../../.." vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/../../../website/themes/blowfish" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -51,6 +51,8 @@ esp-storage = { version = "0.8.1", features = ["esp32c6"] }
|
|||||||
esp-radio = { version = "0.17.0", features = ["esp32c6", "log-04", "smoltcp", "wifi", "unstable"] }
|
esp-radio = { version = "0.17.0", features = ["esp32c6", "log-04", "smoltcp", "wifi", "unstable"] }
|
||||||
esp-alloc = { version = "0.9.0", features = ["esp32c6", "internal-heap-stats"] }
|
esp-alloc = { version = "0.9.0", features = ["esp32c6", "internal-heap-stats"] }
|
||||||
|
|
||||||
|
esp-hal-ota = { version = "0.4.6", features = ["esp32c6"] }
|
||||||
|
|
||||||
# Async runtime (Embassy core)
|
# Async runtime (Embassy core)
|
||||||
embassy-executor = { version = "0.9.1", features = ["log", "nightly"] }
|
embassy-executor = { version = "0.9.1", features = ["log", "nightly"] }
|
||||||
embassy-time = { version = "0.5.0", features = ["log"], default-features = false }
|
embassy-time = { version = "0.5.0", features = ["log"], default-features = false }
|
||||||
@@ -95,7 +97,7 @@ embedded-can = "0.4.1"
|
|||||||
nb = "1.1.0"
|
nb = "1.1.0"
|
||||||
|
|
||||||
# Concrete hardware drivers and sensors/IO expanders
|
# Concrete hardware drivers and sensors/IO expanders
|
||||||
lib-bms-protocol = {git = "https://gitea.wlandt.de/judge/ch32-bms.git" , default-features = false }
|
lib-bms-protocol = { git = "https://gitea.wlandt.de/judge/ch32-bms.git", default-features = false }
|
||||||
onewire = "0.4.0"
|
onewire = "0.4.0"
|
||||||
ds323x = "0.7.0"
|
ds323x = "0.7.0"
|
||||||
eeprom24x = "0.7.2"
|
eeprom24x = "0.7.2"
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use embedded_storage::nor_flash::NorFlashErrorKind;
|
|||||||
use esp_hal::i2c::master::ConfigError;
|
use esp_hal::i2c::master::ConfigError;
|
||||||
use esp_hal::pcnt::unit::{InvalidHighLimit, InvalidLowLimit};
|
use esp_hal::pcnt::unit::{InvalidHighLimit, InvalidLowLimit};
|
||||||
use esp_hal::twai::EspTwaiError;
|
use esp_hal::twai::EspTwaiError;
|
||||||
|
use esp_hal_ota::OtaError;
|
||||||
use esp_radio::wifi::WifiError;
|
use esp_radio::wifi::WifiError;
|
||||||
use ina219::errors::{BusVoltageReadError, ShuntVoltageReadError};
|
use ina219::errors::{BusVoltageReadError, ShuntVoltageReadError};
|
||||||
use lib_bms_protocol::BmsProtocolError;
|
use lib_bms_protocol::BmsProtocolError;
|
||||||
@@ -73,6 +74,9 @@ pub enum FatError {
|
|||||||
SNTPError {
|
SNTPError {
|
||||||
error: sntpc::Error,
|
error: sntpc::Error,
|
||||||
},
|
},
|
||||||
|
OtaError {
|
||||||
|
error: OtaError,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type FatResult<T> = Result<T, FatError>;
|
pub type FatResult<T> = Result<T, FatError>;
|
||||||
@@ -106,6 +110,7 @@ impl fmt::Display for FatError {
|
|||||||
}
|
}
|
||||||
FatError::SNTPError { error } => write!(f, "SNTPError {error:?}"),
|
FatError::SNTPError { error } => write!(f, "SNTPError {error:?}"),
|
||||||
FatError::BMSError { error } => write!(f, "BMSError, {error}"),
|
FatError::BMSError { error } => write!(f, "BMSError, {error}"),
|
||||||
|
FatError::OtaError { error } => write!(f, "OtaError {error:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -323,13 +328,18 @@ impl From<sntpc::Error> for FatError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<BmsProtocolError> for FatError {
|
||||||
impl From<BmsProtocolError> for FatError{
|
|
||||||
fn from(value: BmsProtocolError) -> Self {
|
fn from(value: BmsProtocolError) -> Self {
|
||||||
match value {
|
match value {
|
||||||
BmsProtocolError::I2cCommunicationError => {
|
BmsProtocolError::I2cCommunicationError => FatError::String {
|
||||||
FatError::String{error: "I2C communication error".to_string()}
|
error: "I2C communication error".to_string(),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<OtaError> for FatError {
|
||||||
|
fn from(value: OtaError) -> Self {
|
||||||
|
FatError::OtaError { error: value }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use crate::hal::little_fs2storage_adapter::LittleFs2Filesystem;
|
|||||||
use crate::hal::shared_flash::MutexFlashStorage;
|
use crate::hal::shared_flash::MutexFlashStorage;
|
||||||
use alloc::string::ToString;
|
use alloc::string::ToString;
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use alloc::{format, string::String, vec, vec::Vec};
|
use alloc::{format, string::String, vec::Vec};
|
||||||
use core::net::{IpAddr, Ipv4Addr, SocketAddr};
|
use core::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||||
use core::str::FromStr;
|
use core::str::FromStr;
|
||||||
use core::sync::atomic::Ordering;
|
use core::sync::atomic::Ordering;
|
||||||
@@ -21,11 +21,6 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
|||||||
use embassy_sync::mutex::{Mutex, MutexGuard};
|
use embassy_sync::mutex::{Mutex, MutexGuard};
|
||||||
use embassy_sync::once_lock::OnceLock;
|
use embassy_sync::once_lock::OnceLock;
|
||||||
use embassy_time::{Duration, Timer, WithTimeout};
|
use embassy_time::{Duration, Timer, WithTimeout};
|
||||||
use embedded_storage::nor_flash::{check_erase, NorFlash, ReadNorFlash};
|
|
||||||
use esp_bootloader_esp_idf::ota::OtaImageState::Valid;
|
|
||||||
use esp_bootloader_esp_idf::ota::{Ota, OtaImageState};
|
|
||||||
use esp_bootloader_esp_idf::partitions::{AppPartitionSubType, FlashRegion};
|
|
||||||
use esp_hal::Blocking;
|
|
||||||
use esp_hal::gpio::{Input, RtcPinWithResistors};
|
use esp_hal::gpio::{Input, RtcPinWithResistors};
|
||||||
use esp_hal::rng::Rng;
|
use esp_hal::rng::Rng;
|
||||||
use esp_hal::rtc_cntl::{
|
use esp_hal::rtc_cntl::{
|
||||||
@@ -34,6 +29,8 @@ use esp_hal::rtc_cntl::{
|
|||||||
};
|
};
|
||||||
use esp_hal::system::software_reset;
|
use esp_hal::system::software_reset;
|
||||||
use esp_hal::uart::Uart;
|
use esp_hal::uart::Uart;
|
||||||
|
use esp_hal::Blocking;
|
||||||
|
use esp_hal_ota::Ota;
|
||||||
use esp_println::println;
|
use esp_println::println;
|
||||||
use esp_radio::wifi::{
|
use esp_radio::wifi::{
|
||||||
AccessPointConfig, AccessPointInfo, AuthMethod, ClientConfig, ModeConfig, ScanConfig,
|
AccessPointConfig, AccessPointInfo, AuthMethod, ClientConfig, ModeConfig, ScanConfig,
|
||||||
@@ -129,12 +126,7 @@ pub struct Esp<'a> {
|
|||||||
// RTC-capable GPIO used as external wake source (store the raw peripheral)
|
// RTC-capable GPIO used as external wake source (store the raw peripheral)
|
||||||
pub wake_gpio1: esp_hal::peripherals::GPIO1<'static>,
|
pub wake_gpio1: esp_hal::peripherals::GPIO1<'static>,
|
||||||
pub uart0: Uart<'a, Blocking>,
|
pub uart0: Uart<'a, Blocking>,
|
||||||
|
pub ota: &'static mut Ota<&'static mut MutexFlashStorage>,
|
||||||
pub ota: Ota<'static, MutexFlashStorage>,
|
|
||||||
pub ota_target: &'static mut FlashRegion<'static, MutexFlashStorage>,
|
|
||||||
pub current: AppPartitionSubType,
|
|
||||||
pub slot0_state: OtaImageState,
|
|
||||||
pub slot1_state: OtaImageState,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: On this target we never move Esp across OS threads; the firmware runs single-core
|
// SAFETY: On this target we never move Esp across OS threads; the firmware runs single-core
|
||||||
@@ -155,7 +147,6 @@ macro_rules! mk_static {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Esp<'_> {
|
impl Esp<'_> {
|
||||||
|
|
||||||
pub(crate) async fn read_serial_line(&mut self) -> FatResult<Option<alloc::string::String>> {
|
pub(crate) async fn read_serial_line(&mut self) -> FatResult<Option<alloc::string::String>> {
|
||||||
let mut buf = [0u8; 1];
|
let mut buf = [0u8; 1];
|
||||||
let mut line = String::new();
|
let mut line = String::new();
|
||||||
@@ -171,7 +162,7 @@ impl Esp<'_> {
|
|||||||
}
|
}
|
||||||
line.push(c);
|
line.push(c);
|
||||||
}
|
}
|
||||||
Err(error ) => {
|
Err(error) => {
|
||||||
if line.is_empty() {
|
if line.is_empty() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
} else {
|
} else {
|
||||||
@@ -250,50 +241,6 @@ impl Esp<'_> {
|
|||||||
Ok((buf, read))
|
Ok((buf, read))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn write_ota(&mut self, offset: u32, buf: &[u8]) -> Result<(), FatError> {
|
|
||||||
let _ = check_erase(self.ota_target, offset, offset + 4096);
|
|
||||||
self.ota_target.erase(offset, offset + 4096)?;
|
|
||||||
|
|
||||||
let mut temp = vec![0; buf.len()];
|
|
||||||
let read_back = temp.as_mut_slice();
|
|
||||||
//change to nor flash, align writes!
|
|
||||||
self.ota_target.write(offset, buf)?;
|
|
||||||
self.ota_target.read(offset, read_back)?;
|
|
||||||
if buf != read_back {
|
|
||||||
info!("Expected {buf:?} but got {read_back:?}");
|
|
||||||
bail!(
|
|
||||||
"Flash error, read back does not match write buffer at offset {:x}",
|
|
||||||
offset
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn finalize_ota(&mut self) -> Result<(), FatError> {
|
|
||||||
let current = self.ota.current_app_partition()?;
|
|
||||||
if self.ota.current_ota_state()? != Valid {
|
|
||||||
info!("Validating current slot {current:?} as it was able to ota");
|
|
||||||
self.ota.set_current_ota_state(Valid)?;
|
|
||||||
}
|
|
||||||
let next = match current {
|
|
||||||
AppPartitionSubType::Ota0 => AppPartitionSubType::Ota1,
|
|
||||||
AppPartitionSubType::Ota1 => AppPartitionSubType::Ota0,
|
|
||||||
_ => {
|
|
||||||
bail!("Invalid current slot {current:?} for ota");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.ota.set_current_app_partition(next)?;
|
|
||||||
info!("switched slot");
|
|
||||||
self.ota.set_current_ota_state(OtaImageState::New)?;
|
|
||||||
info!("switched state for new partition");
|
|
||||||
let state_new = self.ota.current_ota_state()?;
|
|
||||||
info!("state on new partition now {state_new:?}");
|
|
||||||
//determine nextslot crc
|
|
||||||
|
|
||||||
self.set_restart_to_conf(true);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn mode_override_pressed(&mut self) -> bool {
|
pub(crate) fn mode_override_pressed(&mut self) -> bool {
|
||||||
self.boot_button.is_low()
|
self.boot_button.is_low()
|
||||||
}
|
}
|
||||||
@@ -594,14 +541,7 @@ impl Esp<'_> {
|
|||||||
duration_in_ms: u64,
|
duration_in_ms: u64,
|
||||||
mut rtc: MutexGuard<CriticalSectionRawMutex, Rtc>,
|
mut rtc: MutexGuard<CriticalSectionRawMutex, Rtc>,
|
||||||
) -> ! {
|
) -> ! {
|
||||||
// Mark the current OTA image as valid if we reached here while in pending verify.
|
//TODO HERE Mark the current OTA image as valid if we reached here while in pending verify.
|
||||||
if let Ok(cur) = self.ota.current_ota_state() {
|
|
||||||
if cur == OtaImageState::PendingVerify {
|
|
||||||
self.ota
|
|
||||||
.set_current_ota_state(Valid)
|
|
||||||
.expect("Could not set image to valid");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if duration_in_ms == 0 {
|
if duration_in_ms == 0 {
|
||||||
software_reset();
|
software_reset();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
use esp_hal::uart::Config as UartConfig;
|
||||||
use lib_bms_protocol::BmsReadable;
|
use lib_bms_protocol::BmsReadable;
|
||||||
use esp_hal::uart::{Config as UartConfig};
|
|
||||||
pub(crate) mod battery;
|
pub(crate) mod battery;
|
||||||
// mod can_api; // replaced by external canapi crate
|
// mod can_api; // replaced by external canapi crate
|
||||||
pub mod esp;
|
pub mod esp;
|
||||||
@@ -9,7 +9,6 @@ mod shared_flash;
|
|||||||
mod v4_hal;
|
mod v4_hal;
|
||||||
mod water;
|
mod water;
|
||||||
|
|
||||||
use lib_bms_protocol::ProtocolVersion;
|
|
||||||
use crate::alloc::string::ToString;
|
use crate::alloc::string::ToString;
|
||||||
use crate::hal::rtc::{DS3231Module, RTCModuleInteraction};
|
use crate::hal::rtc::{DS3231Module, RTCModuleInteraction};
|
||||||
use esp_hal::peripherals::Peripherals;
|
use esp_hal::peripherals::Peripherals;
|
||||||
@@ -36,6 +35,7 @@ use esp_hal::peripherals::GPIO6;
|
|||||||
use esp_hal::peripherals::GPIO7;
|
use esp_hal::peripherals::GPIO7;
|
||||||
use esp_hal::peripherals::GPIO8;
|
use esp_hal::peripherals::GPIO8;
|
||||||
use esp_hal::peripherals::TWAI0;
|
use esp_hal::peripherals::TWAI0;
|
||||||
|
use lib_bms_protocol::ProtocolVersion;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bail,
|
bail,
|
||||||
@@ -65,24 +65,20 @@ use eeprom24x::{Eeprom24x, SlaveAddr, Storage};
|
|||||||
use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice;
|
use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice;
|
||||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
use embassy_sync::blocking_mutex::CriticalSectionMutex;
|
use embassy_sync::blocking_mutex::CriticalSectionMutex;
|
||||||
use esp_bootloader_esp_idf::partitions::{
|
use esp_bootloader_esp_idf::partitions::{DataPartitionSubType, FlashRegion, PartitionEntry};
|
||||||
AppPartitionSubType, DataPartitionSubType, FlashRegion, PartitionEntry,
|
|
||||||
};
|
|
||||||
use esp_hal::clock::CpuClock;
|
use esp_hal::clock::CpuClock;
|
||||||
use esp_hal::gpio::{Input, InputConfig, Pull};
|
use esp_hal::gpio::{Input, InputConfig, Pull};
|
||||||
use measurements::{Current, Voltage};
|
use measurements::{Current, Voltage};
|
||||||
|
|
||||||
use crate::fat_error::{ContextExt, FatError, FatResult};
|
use crate::fat_error::{FatError, FatResult};
|
||||||
use crate::hal::battery::{WCHI2CSlave};
|
use crate::hal::battery::WCHI2CSlave;
|
||||||
use crate::hal::little_fs2storage_adapter::LittleFs2Filesystem;
|
use crate::hal::little_fs2storage_adapter::LittleFs2Filesystem;
|
||||||
use crate::hal::water::TankSensor;
|
use crate::hal::water::TankSensor;
|
||||||
use crate::log::LOG_ACCESS;
|
use crate::log::LOG_ACCESS;
|
||||||
use embassy_sync::mutex::Mutex;
|
use embassy_sync::mutex::Mutex;
|
||||||
use embassy_sync::once_lock::OnceLock;
|
use embassy_sync::once_lock::OnceLock;
|
||||||
use embedded_storage::ReadStorage;
|
|
||||||
use esp_alloc as _;
|
use esp_alloc as _;
|
||||||
use esp_backtrace as _;
|
use esp_backtrace as _;
|
||||||
use esp_bootloader_esp_idf::ota::{Ota, OtaImageState};
|
|
||||||
use esp_hal::delay::Delay;
|
use esp_hal::delay::Delay;
|
||||||
use esp_hal::i2c::master::{BusTimeout, Config, I2c};
|
use esp_hal::i2c::master::{BusTimeout, Config, I2c};
|
||||||
use esp_hal::interrupt::software::SoftwareInterruptControl;
|
use esp_hal::interrupt::software::SoftwareInterruptControl;
|
||||||
@@ -93,8 +89,9 @@ 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::time::Rate;
|
||||||
use esp_hal::timer::timg::TimerGroup;
|
use esp_hal::timer::timg::TimerGroup;
|
||||||
use esp_hal::Blocking;
|
|
||||||
use esp_hal::uart::Uart;
|
use esp_hal::uart::Uart;
|
||||||
|
use esp_hal::Blocking;
|
||||||
|
use esp_hal_ota::Ota;
|
||||||
use esp_radio::{init, Controller};
|
use esp_radio::{init, Controller};
|
||||||
use esp_storage::FlashStorage;
|
use esp_storage::FlashStorage;
|
||||||
use littlefs2::fs::{Allocation, Filesystem as lfs2Filesystem};
|
use littlefs2::fs::{Allocation, Filesystem as lfs2Filesystem};
|
||||||
@@ -138,12 +135,6 @@ pub struct HAL<'a> {
|
|||||||
pub board_hal: Box<dyn BoardInteraction<'a> + Send>,
|
pub board_hal: Box<dyn BoardInteraction<'a> + Send>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DetectionRequest {
|
|
||||||
pub sensorsa: [Sensor; PLANT_COUNT],
|
|
||||||
pub sensorsb: [Sensor; PLANT_COUNT],
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
pub trait BoardInteraction<'a> {
|
pub trait BoardInteraction<'a> {
|
||||||
fn get_tank_sensor(&mut self) -> Result<&mut TankSensor<'a>, FatError>;
|
fn get_tank_sensor(&mut self) -> Result<&mut TankSensor<'a>, FatError>;
|
||||||
@@ -169,7 +160,7 @@ pub trait BoardInteraction<'a> {
|
|||||||
async fn can_power(&mut self, state: bool) -> FatResult<()>;
|
async fn can_power(&mut self, state: bool) -> FatResult<()>;
|
||||||
|
|
||||||
// Return JSON string with autodetected sensors per plant. Default: not supported.
|
// Return JSON string with autodetected sensors per plant. Default: not supported.
|
||||||
async fn detect_sensors(&mut self, request: Detection) -> FatResult<Detection> {
|
async fn detect_sensors(&mut self, _request: Detection) -> FatResult<Detection> {
|
||||||
bail!("Autodetection is only available on v4 HAL with CAN bus");
|
bail!("Autodetection is only available on v4 HAL with CAN bus");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,51 +315,15 @@ impl PlantHal {
|
|||||||
let pt =
|
let pt =
|
||||||
esp_bootloader_esp_idf::partitions::read_partition_table(flash_storage, tablebuffer)?;
|
esp_bootloader_esp_idf::partitions::read_partition_table(flash_storage, tablebuffer)?;
|
||||||
|
|
||||||
let ota_data = mk_static!(
|
let ota = mk_static!(
|
||||||
PartitionEntry,
|
Ota<&mut MutexFlashStorage>,
|
||||||
pt.find_partition(esp_bootloader_esp_idf::partitions::PartitionType::Data(
|
Ota::new(flash_storage_2).unwrap()
|
||||||
DataPartitionSubType::Ota,
|
|
||||||
))?
|
|
||||||
.expect("No OTA data partition found")
|
|
||||||
);
|
|
||||||
|
|
||||||
let ota_data = mk_static!(
|
|
||||||
FlashRegion<MutexFlashStorage>,
|
|
||||||
ota_data.as_embedded_storage(flash_storage_2)
|
|
||||||
);
|
|
||||||
|
|
||||||
let state_0 = ota_state(AppPartitionSubType::Ota0, ota_data);
|
|
||||||
let state_1 = ota_state(AppPartitionSubType::Ota1, ota_data);
|
|
||||||
let mut ota = Ota::new(ota_data, 2)?;
|
|
||||||
let running = get_current_slot_and_fix_ota_data(&mut ota, state_0, state_1)?;
|
|
||||||
let target = next_partition(running)?;
|
|
||||||
|
|
||||||
info!("Currently running OTA slot: {running:?}");
|
|
||||||
info!("Slot0 state: {state_0:?}");
|
|
||||||
info!("Slot1 state: {state_1:?}");
|
|
||||||
|
|
||||||
//get current_state and next_state here!
|
|
||||||
let ota_target = match target {
|
|
||||||
AppPartitionSubType::Ota0 => pt
|
|
||||||
.find_partition(esp_bootloader_esp_idf::partitions::PartitionType::App(
|
|
||||||
AppPartitionSubType::Ota0,
|
|
||||||
))?
|
|
||||||
.context("Partition table invalid no ota0")?,
|
|
||||||
AppPartitionSubType::Ota1 => pt
|
|
||||||
.find_partition(esp_bootloader_esp_idf::partitions::PartitionType::App(
|
|
||||||
AppPartitionSubType::Ota1,
|
|
||||||
))?
|
|
||||||
.context("Partition table invalid no ota1")?,
|
|
||||||
_ => {
|
|
||||||
bail!("Invalid target partition");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let ota_target = mk_static!(PartitionEntry, ota_target);
|
|
||||||
let ota_target = mk_static!(
|
|
||||||
FlashRegion<MutexFlashStorage>,
|
|
||||||
ota_target.as_embedded_storage(flash_storage)
|
|
||||||
);
|
);
|
||||||
|
info!("Ota initialized");
|
||||||
|
let selected = ota.get_currently_booted_partition();
|
||||||
|
let ota_state = ota.get_ota_image_state();
|
||||||
|
info!("Currently running partition slot: {selected:?}");
|
||||||
|
info!("Slot state: {ota_state:?}");
|
||||||
|
|
||||||
let data_partition = pt
|
let data_partition = pt
|
||||||
.find_partition(esp_bootloader_esp_idf::partitions::PartitionType::Data(
|
.find_partition(esp_bootloader_esp_idf::partitions::PartitionType::Data(
|
||||||
@@ -401,8 +356,8 @@ impl PlantHal {
|
|||||||
lfs2Filesystem::mount(alloc, lfs2filesystem).expect("Could not mount lfs2 filesystem"),
|
lfs2Filesystem::mount(alloc, lfs2filesystem).expect("Could not mount lfs2 filesystem"),
|
||||||
));
|
));
|
||||||
|
|
||||||
let uart0 = Uart::new(peripherals.UART0, UartConfig::default())
|
let uart0 =
|
||||||
.map_err(|_| FatError::String {
|
Uart::new(peripherals.UART0, UartConfig::default()).map_err(|_| FatError::String {
|
||||||
error: "Uart creation failed".to_string(),
|
error: "Uart creation failed".to_string(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@@ -417,11 +372,7 @@ impl PlantHal {
|
|||||||
boot_button,
|
boot_button,
|
||||||
wake_gpio1,
|
wake_gpio1,
|
||||||
ota,
|
ota,
|
||||||
ota_target,
|
uart0,
|
||||||
current: running,
|
|
||||||
slot0_state: state_0,
|
|
||||||
slot1_state: state_1,
|
|
||||||
uart0
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//init,reset rtc memory depending on cause
|
//init,reset rtc memory depending on cause
|
||||||
@@ -571,9 +522,14 @@ impl PlantHal {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
HAL {
|
HAL {
|
||||||
board_hal: v4_hal::create_v4(free_pins, esp, PlantControllerConfig::default(),
|
board_hal: v4_hal::create_v4(
|
||||||
Box::new(NoBatteryMonitor {}), rtc_module)
|
free_pins,
|
||||||
.await?
|
esp,
|
||||||
|
PlantControllerConfig::default(),
|
||||||
|
Box::new(NoBatteryMonitor {}),
|
||||||
|
rtc_module,
|
||||||
|
)
|
||||||
|
.await?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -582,87 +538,6 @@ impl PlantHal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ota_state(
|
|
||||||
slot: AppPartitionSubType,
|
|
||||||
ota_data: &mut FlashRegion<MutexFlashStorage>,
|
|
||||||
) -> OtaImageState {
|
|
||||||
// Read and log OTA states for both slots before constructing Ota
|
|
||||||
// Each OTA select entry is 32 bytes: [seq:4][label:20][state:4][crc:4]
|
|
||||||
// Offsets within the OTA data partition: slot0 @ 0x0000, slot1 @ 0x1000
|
|
||||||
let mut slot_buf = [0u8; 32];
|
|
||||||
if slot == AppPartitionSubType::Ota0 {
|
|
||||||
let _ = ReadStorage::read(ota_data, 0x0000, &mut slot_buf);
|
|
||||||
} else {
|
|
||||||
let _ = ReadStorage::read(ota_data, 0x1000, &mut slot_buf);
|
|
||||||
}
|
|
||||||
let raw_state = u32::from_le_bytes(slot_buf[24..28].try_into().unwrap_or([0xff; 4]));
|
|
||||||
|
|
||||||
OtaImageState::try_from(raw_state).unwrap_or(OtaImageState::Undefined)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_current_slot_and_fix_ota_data(
|
|
||||||
ota: &mut Ota<MutexFlashStorage>,
|
|
||||||
state0: OtaImageState,
|
|
||||||
state1: OtaImageState,
|
|
||||||
) -> Result<AppPartitionSubType, FatError> {
|
|
||||||
let state = ota.current_ota_state().unwrap_or_default();
|
|
||||||
let swap = match state {
|
|
||||||
OtaImageState::Invalid => true,
|
|
||||||
OtaImageState::Aborted => true,
|
|
||||||
OtaImageState::Undefined => {
|
|
||||||
info!("Undefined image in current slot, bootloader wrong?");
|
|
||||||
false
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
let current = ota.current_app_partition()?;
|
|
||||||
if swap {
|
|
||||||
let other = match current {
|
|
||||||
AppPartitionSubType::Ota0 => state1,
|
|
||||||
AppPartitionSubType::Ota1 => state0,
|
|
||||||
_ => OtaImageState::Invalid,
|
|
||||||
};
|
|
||||||
|
|
||||||
match other {
|
|
||||||
OtaImageState::Invalid => {
|
|
||||||
bail!(
|
|
||||||
"cannot recover slot, as both slots in invalid state {:?} {:?} {:?}",
|
|
||||||
current,
|
|
||||||
state0,
|
|
||||||
state1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
OtaImageState::Aborted => {
|
|
||||||
bail!(
|
|
||||||
"cannot recover slot, as both slots in invalid state {:?} {:?} {:?}",
|
|
||||||
current,
|
|
||||||
state0,
|
|
||||||
state1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
info!("Current slot has state {state:?} other state has {other:?} swapping");
|
|
||||||
let next = next_partition(current)?;
|
|
||||||
ota.set_current_app_partition(next)?;
|
|
||||||
|
|
||||||
//we actually booted other slot, than partition table assumes
|
|
||||||
return Ok(ota.current_app_partition()?);
|
|
||||||
};
|
|
||||||
Ok(current)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_partition(current: AppPartitionSubType) -> FatResult<AppPartitionSubType> {
|
|
||||||
let next = match current {
|
|
||||||
AppPartitionSubType::Ota0 => AppPartitionSubType::Ota1,
|
|
||||||
AppPartitionSubType::Ota1 => AppPartitionSubType::Ota0,
|
|
||||||
_ => {
|
|
||||||
bail!("Current slot is not ota0 or ota1");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(next)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn esp_time() -> DateTime<Utc> {
|
pub async fn esp_time() -> DateTime<Utc> {
|
||||||
let guard = TIME_ACCESS.get().await.lock().await;
|
let guard = TIME_ACCESS.get().await.lock().await;
|
||||||
DateTime::from_timestamp_micros(guard.current_time_us() as i64).unwrap()
|
DateTime::from_timestamp_micros(guard.current_time_us() as i64).unwrap()
|
||||||
@@ -699,4 +574,3 @@ pub struct DetectionSensorResult {
|
|||||||
sensor_a: bool,
|
sensor_a: bool,
|
||||||
sensor_b: bool,
|
sensor_b: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use core::cell::RefCell;
|
|||||||
use core::ops::{Deref, DerefMut};
|
use core::ops::{Deref, DerefMut};
|
||||||
use embassy_sync::blocking_mutex::CriticalSectionMutex;
|
use embassy_sync::blocking_mutex::CriticalSectionMutex;
|
||||||
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
|
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
|
||||||
use embedded_storage::ReadStorage;
|
use embedded_storage::{ReadStorage, Storage};
|
||||||
use esp_storage::{FlashStorage, FlashStorageError};
|
use esp_storage::{FlashStorage, FlashStorageError};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -11,6 +11,19 @@ pub struct MutexFlashStorage {
|
|||||||
pub(crate) inner: Arc<CriticalSectionMutex<RefCell<FlashStorage<'static>>>>,
|
pub(crate) inner: Arc<CriticalSectionMutex<RefCell<FlashStorage<'static>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Storage for MutexFlashStorage {
|
||||||
|
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||||
|
self.inner
|
||||||
|
.lock(|f| NorFlash::write(f.borrow_mut().deref_mut(), offset, bytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Storage for &mut MutexFlashStorage {
|
||||||
|
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||||
|
Storage::write(&mut (**self), offset, bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ReadStorage for MutexFlashStorage {
|
impl ReadStorage for MutexFlashStorage {
|
||||||
type Error = FlashStorageError;
|
type Error = FlashStorageError;
|
||||||
|
|
||||||
@@ -25,9 +38,29 @@ impl ReadStorage for MutexFlashStorage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl embedded_storage::Storage for MutexFlashStorage {
|
impl ReadStorage for &mut MutexFlashStorage {
|
||||||
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
|
type Error = FlashStorageError;
|
||||||
NorFlash::write(self, offset, bytes)
|
|
||||||
|
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), FlashStorageError> {
|
||||||
|
ReadStorage::read(&mut (**self), offset, bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn capacity(&self) -> usize {
|
||||||
|
ReadStorage::capacity(&(**self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReadStorage for &MutexFlashStorage {
|
||||||
|
type Error = FlashStorageError;
|
||||||
|
|
||||||
|
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), FlashStorageError> {
|
||||||
|
self.inner
|
||||||
|
.lock(|f| ReadStorage::read(f.borrow_mut().deref_mut(), offset, bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn capacity(&self) -> usize {
|
||||||
|
self.inner
|
||||||
|
.lock(|f| ReadStorage::capacity(f.borrow().deref()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,6 +68,10 @@ impl ErrorType for MutexFlashStorage {
|
|||||||
type Error = FlashStorageError;
|
type Error = FlashStorageError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ErrorType for &MutexFlashStorage {
|
||||||
|
type Error = FlashStorageError;
|
||||||
|
}
|
||||||
|
|
||||||
impl ReadNorFlash for MutexFlashStorage {
|
impl ReadNorFlash for MutexFlashStorage {
|
||||||
const READ_SIZE: usize = 1;
|
const READ_SIZE: usize = 1;
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use crate::hal::{
|
|||||||
};
|
};
|
||||||
use crate::log::{LogMessage, LOG_ACCESS};
|
use crate::log::{LogMessage, LOG_ACCESS};
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use alloc::string::{ToString};
|
use alloc::string::ToString;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use canapi::id::{classify, plant_id, MessageKind, IDENTIFY_CMD_OFFSET};
|
use canapi::id::{classify, plant_id, MessageKind, IDENTIFY_CMD_OFFSET};
|
||||||
use canapi::SensorSlot;
|
use canapi::SensorSlot;
|
||||||
@@ -23,7 +23,6 @@ use esp_hal::gpio::{Flex, Input, InputConfig, Level, Output, OutputConfig, Pull}
|
|||||||
use esp_hal::i2c::master::I2c;
|
use esp_hal::i2c::master::I2c;
|
||||||
use esp_hal::twai::{EspTwaiError, EspTwaiFrame, StandardId, Twai, TwaiConfiguration, TwaiMode};
|
use esp_hal::twai::{EspTwaiError, EspTwaiFrame, StandardId, Twai, TwaiConfiguration, TwaiMode};
|
||||||
use esp_hal::{twai, Async, Blocking};
|
use esp_hal::{twai, Async, Blocking};
|
||||||
use esp_println::println;
|
|
||||||
use ina219::address::{Address, Pin};
|
use ina219::address::{Address, Pin};
|
||||||
use ina219::calibration::UnCalibrated;
|
use ina219::calibration::UnCalibrated;
|
||||||
use ina219::configuration::{Configuration, OperatingMode, Resolution};
|
use ina219::configuration::{Configuration, OperatingMode, Resolution};
|
||||||
@@ -133,10 +132,9 @@ pub struct V4<'a> {
|
|||||||
|
|
||||||
extra1: Output<'a>,
|
extra1: Output<'a>,
|
||||||
extra2: Output<'a>,
|
extra2: Output<'a>,
|
||||||
twai_config: Option<TwaiConfiguration<'static, Blocking>>
|
twai_config: Option<TwaiConfiguration<'static, Blocking>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub(crate) async fn create_v4(
|
pub(crate) async fn create_v4(
|
||||||
peripherals: FreePeripherals<'static>,
|
peripherals: FreePeripherals<'static>,
|
||||||
esp: Esp<'static>,
|
esp: Esp<'static>,
|
||||||
@@ -261,12 +259,11 @@ pub(crate) async fn create_v4(
|
|||||||
extra1,
|
extra1,
|
||||||
extra2,
|
extra2,
|
||||||
can_power,
|
can_power,
|
||||||
twai_config
|
twai_config,
|
||||||
};
|
};
|
||||||
Ok(Box::new(v))
|
Ok(Box::new(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
impl<'a> BoardInteraction<'a> for V4<'a> {
|
impl<'a> BoardInteraction<'a> for V4<'a> {
|
||||||
fn get_tank_sensor(&mut self) -> Result<&mut TankSensor<'a>, FatError> {
|
fn get_tank_sensor(&mut self) -> Result<&mut TankSensor<'a>, FatError> {
|
||||||
@@ -393,11 +390,18 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
if !detect {
|
if !detect {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let target =
|
let target = StandardId::new(plant_id(
|
||||||
StandardId::new(plant_id(IDENTIFY_CMD_OFFSET, sensor.into(), (plant +1) as u16))
|
IDENTIFY_CMD_OFFSET,
|
||||||
|
sensor.into(),
|
||||||
|
(plant + 1) as u16,
|
||||||
|
))
|
||||||
.context(">> Could not create address for sensor! (plant: {}) <<")?;
|
.context(">> Could not create address for sensor! (plant: {}) <<")?;
|
||||||
let can_buffer = [0_u8; 0];
|
let can_buffer = [0_u8; 0];
|
||||||
info!("Sending test message to plant {} sensor {sensor:?} with id {}", plant +1, target.as_raw());
|
info!(
|
||||||
|
"Sending test message to plant {} sensor {sensor:?} with id {}",
|
||||||
|
plant + 1,
|
||||||
|
target.as_raw()
|
||||||
|
);
|
||||||
if let Some(frame) = EspTwaiFrame::new(target, &can_buffer) {
|
if let Some(frame) = EspTwaiFrame::new(target, &can_buffer) {
|
||||||
// Try a few times; we intentionally ignore rx here and rely on stub logic
|
// Try a few times; we intentionally ignore rx here and rely on stub logic
|
||||||
let resu = twai
|
let resu = twai
|
||||||
@@ -405,11 +409,11 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
.with_timeout(Duration::from_millis(3000))
|
.with_timeout(Duration::from_millis(3000))
|
||||||
.await;
|
.await;
|
||||||
match resu {
|
match resu {
|
||||||
Ok(_) => {
|
Ok(_) => {}
|
||||||
}
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
info!(
|
info!(
|
||||||
"Error sending test message to plant {} sensor {sensor:?}: {err:?}", plant +1
|
"Error sending test message to plant {} sensor {sensor:?}: {err:?}",
|
||||||
|
plant + 1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -424,20 +428,17 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
.with_timeout(Duration::from_millis(3000))
|
.with_timeout(Duration::from_millis(3000))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
|
||||||
let config = twai.stop().into_blocking();
|
let config = twai.stop().into_blocking();
|
||||||
self.twai_config.replace(config);
|
self.twai_config.replace(config);
|
||||||
|
|
||||||
self.can_power.set_low();
|
self.can_power.set_low();
|
||||||
|
|
||||||
|
|
||||||
let result = moistures.into();
|
let result = moistures.into();
|
||||||
|
|
||||||
info!("Autodetection result: {result:?}");
|
info!("Autodetection result: {result:?}");
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async fn general_fault(&mut self, enable: bool) {
|
async fn general_fault(&mut self, enable: bool) {
|
||||||
hold_disable(23);
|
hold_disable(23);
|
||||||
self.general_fault.set_level(enable.into());
|
self.general_fault.set_level(enable.into());
|
||||||
@@ -480,7 +481,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
LOG_ACCESS
|
LOG_ACCESS
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.log(LogMessage::TestSensor, a, b, &(plant+1).to_string(), "")
|
.log(LogMessage::TestSensor, a, b, &(plant + 1).to_string(), "")
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
Timer::after_millis(10).await;
|
Timer::after_millis(10).await;
|
||||||
@@ -509,7 +510,6 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async fn wait_for_can_measurements(
|
async fn wait_for_can_measurements(
|
||||||
as_async: &mut Twai<'_, Async>,
|
as_async: &mut Twai<'_, Async>,
|
||||||
moistures: &mut Moistures,
|
moistures: &mut Moistures,
|
||||||
@@ -536,10 +536,12 @@ async fn wait_for_can_measurements(
|
|||||||
let frequency = u32::from_be_bytes(bytes);
|
let frequency = u32::from_be_bytes(bytes);
|
||||||
match sensor {
|
match sensor {
|
||||||
SensorSlot::A => {
|
SensorSlot::A => {
|
||||||
moistures.sensor_a_hz[plant-1] = Some(frequency as f32);
|
moistures.sensor_a_hz[plant - 1] =
|
||||||
|
Some(frequency as f32);
|
||||||
}
|
}
|
||||||
SensorSlot::B => {
|
SensorSlot::B => {
|
||||||
moistures.sensor_b_hz[plant-1] = Some(frequency as f32);
|
moistures.sensor_b_hz[plant - 1] =
|
||||||
|
Some(frequency as f32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ use ::log::{error, info, warn};
|
|||||||
use alloc::borrow::ToOwned;
|
use alloc::borrow::ToOwned;
|
||||||
use alloc::string::{String, ToString};
|
use alloc::string::{String, ToString};
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use alloc::{format, vec};
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
use alloc::{format, vec};
|
||||||
use chrono::{DateTime, Datelike, Timelike, Utc};
|
use chrono::{DateTime, Datelike, Timelike, Utc};
|
||||||
use chrono_tz::Tz::{self, UTC};
|
use chrono_tz::Tz::{self, UTC};
|
||||||
use core::sync::atomic::{AtomicBool, Ordering};
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
@@ -42,6 +42,7 @@ use embassy_sync::once_lock::OnceLock;
|
|||||||
use embassy_time::{Duration, Timer, WithTimeout};
|
use embassy_time::{Duration, Timer, WithTimeout};
|
||||||
use esp_hal::rom::ets_delay_us;
|
use esp_hal::rom::ets_delay_us;
|
||||||
use esp_hal::system::software_reset;
|
use esp_hal::system::software_reset;
|
||||||
|
use esp_hal_ota::OtaImgState;
|
||||||
use esp_println::{logger, println};
|
use esp_println::{logger, println};
|
||||||
use hal::battery::BatteryState;
|
use hal::battery::BatteryState;
|
||||||
use log::LogMessage;
|
use log::LogMessage;
|
||||||
@@ -501,16 +502,17 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
|||||||
|
|
||||||
info!("state of charg");
|
info!("state of charg");
|
||||||
let is_day = board.board_hal.is_day();
|
let is_day = board.board_hal.is_day();
|
||||||
let battery_state = board.board_hal.get_battery_monitor().get_state().await.unwrap_or(BatteryState::Unknown);
|
let battery_state = board
|
||||||
|
.board_hal
|
||||||
|
.get_battery_monitor()
|
||||||
|
.get_state()
|
||||||
|
.await
|
||||||
|
.unwrap_or(BatteryState::Unknown);
|
||||||
info!("Battery state is {battery_state:?}");
|
info!("Battery state is {battery_state:?}");
|
||||||
|
|
||||||
let state_of_charge = match &battery_state {
|
let state_of_charge = match &battery_state {
|
||||||
BatteryState::Unknown => {
|
BatteryState::Unknown => 0,
|
||||||
0
|
BatteryState::Info(data) => data.state_of_charge,
|
||||||
}
|
|
||||||
BatteryState::Info(data) => {
|
|
||||||
data.state_of_charge
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut light_state = LightState {
|
let mut light_state = LightState {
|
||||||
@@ -529,22 +531,10 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
|||||||
board.board_hal.get_config().night_lamp.night_lamp_hour_end,
|
board.board_hal.get_config().night_lamp.night_lamp_hour_end,
|
||||||
);
|
);
|
||||||
|
|
||||||
if state_of_charge
|
if state_of_charge < board.board_hal.get_config().night_lamp.low_soc_cutoff {
|
||||||
< board
|
|
||||||
.board_hal
|
|
||||||
.get_config()
|
|
||||||
.night_lamp
|
|
||||||
.low_soc_cutoff
|
|
||||||
{
|
|
||||||
board.board_hal.get_esp().set_low_voltage_in_cycle();
|
board.board_hal.get_esp().set_low_voltage_in_cycle();
|
||||||
info!("Set low voltage in cycle");
|
info!("Set low voltage in cycle");
|
||||||
} else if state_of_charge
|
} else if state_of_charge > board.board_hal.get_config().night_lamp.low_soc_restore {
|
||||||
> board
|
|
||||||
.board_hal
|
|
||||||
.get_config()
|
|
||||||
.night_lamp
|
|
||||||
.low_soc_restore
|
|
||||||
{
|
|
||||||
board.board_hal.get_esp().clear_low_voltage_in_cycle();
|
board.board_hal.get_esp().clear_low_voltage_in_cycle();
|
||||||
info!("Clear low voltage in cycle");
|
info!("Clear low voltage in cycle");
|
||||||
}
|
}
|
||||||
@@ -758,7 +748,7 @@ pub async fn do_secure_pump(
|
|||||||
|
|
||||||
async fn update_charge_indicator(
|
async fn update_charge_indicator(
|
||||||
board: &mut MutexGuard<'static, CriticalSectionRawMutex, HAL<'static>>,
|
board: &mut MutexGuard<'static, CriticalSectionRawMutex, HAL<'static>>,
|
||||||
) -> FatResult<()>{
|
) -> FatResult<()> {
|
||||||
//FIXME add config and code to allow power supply mode, in this case this is a nop
|
//FIXME add config and code to allow power supply mode, in this case this is a nop
|
||||||
//we have mppt controller, ask it for charging current
|
//we have mppt controller, ask it for charging current
|
||||||
let current = board.board_hal.get_mptt_current().await?;
|
let current = board.board_hal.get_mptt_current().await?;
|
||||||
@@ -769,7 +759,6 @@ async fn update_charge_indicator(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async fn publish_tank_state(
|
async fn publish_tank_state(
|
||||||
board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>,
|
board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>,
|
||||||
tank_state: &TankState,
|
tank_state: &TankState,
|
||||||
@@ -949,11 +938,7 @@ async fn publish_mppt_state(
|
|||||||
async fn publish_battery_state(
|
async fn publish_battery_state(
|
||||||
board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>,
|
board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>,
|
||||||
) -> () {
|
) -> () {
|
||||||
let state = board
|
let state = board.board_hal.get_battery_monitor().get_state().await;
|
||||||
.board_hal
|
|
||||||
.get_battery_monitor()
|
|
||||||
.get_state()
|
|
||||||
.await;
|
|
||||||
let value = match state {
|
let value = match state {
|
||||||
Ok(state) => {
|
Ok(state) => {
|
||||||
let json = serde_json::to_string(&state).unwrap().to_owned();
|
let json = serde_json::to_string(&state).unwrap().to_owned();
|
||||||
@@ -986,7 +971,7 @@ async fn wait_infinity(
|
|||||||
loop {
|
loop {
|
||||||
{
|
{
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||||
match update_charge_indicator(&mut board).await{
|
match update_charge_indicator(&mut board).await {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
if !suppress_further_mppt_error {
|
if !suppress_further_mppt_error {
|
||||||
@@ -1069,18 +1054,23 @@ async fn wait_infinity(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_serial_config(board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'_>>, serial_config_receive: &AtomicBool, reboot_now: &AtomicBool) -> FatResult<()> {
|
async fn handle_serial_config(
|
||||||
|
board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'_>>,
|
||||||
|
serial_config_receive: &AtomicBool,
|
||||||
|
reboot_now: &AtomicBool,
|
||||||
|
) -> FatResult<()> {
|
||||||
match board.board_hal.get_esp().read_serial_line().await {
|
match board.board_hal.get_esp().read_serial_line().await {
|
||||||
Ok(serial_line) => {
|
Ok(serial_line) => match serial_line {
|
||||||
match serial_line {
|
None => Ok(()),
|
||||||
None => {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Some(line) => {
|
Some(line) => {
|
||||||
if serial_config_receive.load(Ordering::Relaxed) {
|
if serial_config_receive.load(Ordering::Relaxed) {
|
||||||
let ll = line.as_str();
|
let ll = line.as_str();
|
||||||
let config: PlantControllerConfig = serde_json::from_str(ll)?;
|
let config: PlantControllerConfig = serde_json::from_str(ll)?;
|
||||||
board.board_hal.get_esp().save_config(Vec::from(ll.as_bytes())).await?;
|
board
|
||||||
|
.board_hal
|
||||||
|
.get_esp()
|
||||||
|
.save_config(Vec::from(ll.as_bytes()))
|
||||||
|
.await?;
|
||||||
board.board_hal.set_config(config);
|
board.board_hal.set_config(config);
|
||||||
serial_config_receive.store(false, Ordering::Relaxed);
|
serial_config_receive.store(false, Ordering::Relaxed);
|
||||||
info!("Config received, rebooting");
|
info!("Config received, rebooting");
|
||||||
@@ -1094,10 +1084,8 @@ async fn handle_serial_config(board: &mut MutexGuard<'_, CriticalSectionRawMutex
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
error!("Error reading serial line");
|
error!("Error reading serial line");
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1153,13 +1141,35 @@ async fn get_version(
|
|||||||
let branch = env!("VERGEN_GIT_BRANCH").to_owned();
|
let branch = env!("VERGEN_GIT_BRANCH").to_owned();
|
||||||
let hash = &env!("VERGEN_GIT_SHA")[0..8];
|
let hash = &env!("VERGEN_GIT_SHA")[0..8];
|
||||||
|
|
||||||
let board = board.board_hal.get_esp();
|
let esp = board.board_hal.get_esp();
|
||||||
|
let current_partition: String = match esp.ota.get_currently_booted_partition() {
|
||||||
|
Some(partition) => match partition {
|
||||||
|
0 => "OTA0".to_string(),
|
||||||
|
1 => "OTA1".to_string(),
|
||||||
|
_ => format!("Pentry: {:?}", partition),
|
||||||
|
},
|
||||||
|
None => "Missing otainfo".to_string(),
|
||||||
|
};
|
||||||
|
let state = esp.ota.get_ota_image_state();
|
||||||
|
let current_state: String = match state {
|
||||||
|
Ok(state) => match state {
|
||||||
|
OtaImgState::EspOtaImgNew => "New".to_string(),
|
||||||
|
OtaImgState::EspOtaImgPendingVerify => "PendingVerify".to_string(),
|
||||||
|
OtaImgState::EspOtaImgValid => "Valid".to_string(),
|
||||||
|
OtaImgState::EspOtaImgInvalid => "Invalid".to_string(),
|
||||||
|
OtaImgState::EspOtaImgAborted => "Aborted".to_string(),
|
||||||
|
OtaImgState::EspOtaImgUndefined => "Undefined".to_string(),
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
format!("Error: {:?}", err)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
VersionInfo {
|
VersionInfo {
|
||||||
git_hash: branch + "@" + hash,
|
git_hash: branch + "@" + hash,
|
||||||
build_time: env!("VERGEN_BUILD_TIMESTAMP").to_owned(),
|
build_time: env!("VERGEN_BUILD_TIMESTAMP").to_owned(),
|
||||||
current: format!("{:?}", board.current),
|
current: format!("{:?}", current_partition),
|
||||||
slot0_state: format!("{:?}", board.slot0_state),
|
state: format!("{:?}", current_state),
|
||||||
slot1_state: format!("{:?}", board.slot1_state),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1168,6 +1178,5 @@ struct VersionInfo {
|
|||||||
git_hash: String,
|
git_hash: String,
|
||||||
build_time: String,
|
build_time: String,
|
||||||
current: String,
|
current: String,
|
||||||
slot0_state: String,
|
state: String,
|
||||||
slot1_state: String,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ pub enum MoistureSensorError {
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize)]
|
#[derive(Debug, PartialEq, Serialize)]
|
||||||
pub enum MoistureSensorState {
|
pub enum MoistureSensorState {
|
||||||
Disabled,
|
|
||||||
MoistureValue { raw_hz: f32, moisture_percent: f32 },
|
MoistureValue { raw_hz: f32, moisture_percent: f32 },
|
||||||
SensorError(MoistureSensorError),
|
SensorError(MoistureSensorError),
|
||||||
}
|
}
|
||||||
@@ -117,12 +116,11 @@ impl PlantState {
|
|||||||
plant_id: usize,
|
plant_id: usize,
|
||||||
board: &mut HAL<'_>,
|
board: &mut HAL<'_>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let sensor_a = { //if board.board_hal.get_config().plants[plant_id].sensor_a {
|
let sensor_a = {
|
||||||
|
//if board.board_hal.get_config().plants[plant_id].sensor_a {
|
||||||
let raw = moistures.sensor_a_hz[plant_id];
|
let raw = moistures.sensor_a_hz[plant_id];
|
||||||
match raw {
|
match raw {
|
||||||
None => {
|
None => MoistureSensorState::SensorError(MoistureSensorError::NoMessage),
|
||||||
MoistureSensorState::SensorError(MoistureSensorError::NoMessage)
|
|
||||||
}
|
|
||||||
Some(raw) => {
|
Some(raw) => {
|
||||||
match map_range_moisture(
|
match map_range_moisture(
|
||||||
raw,
|
raw,
|
||||||
@@ -141,17 +139,15 @@ impl PlantState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}; // else {
|
}; // else {
|
||||||
// MoistureSensorState::Disabled
|
// MoistureSensorState::Disabled
|
||||||
//};
|
//};
|
||||||
|
|
||||||
let sensor_b = { //if board.board_hal.get_config().plants[plant_id].sensor_b {
|
let sensor_b = {
|
||||||
|
//if board.board_hal.get_config().plants[plant_id].sensor_b {
|
||||||
let raw = moistures.sensor_b_hz[plant_id];
|
let raw = moistures.sensor_b_hz[plant_id];
|
||||||
match raw {
|
match raw {
|
||||||
None => {
|
None => MoistureSensorState::SensorError(MoistureSensorError::NoMessage),
|
||||||
MoistureSensorState::SensorError(MoistureSensorError::NoMessage)
|
|
||||||
}
|
|
||||||
Some(raw) => {
|
Some(raw) => {
|
||||||
match map_range_moisture(
|
match map_range_moisture(
|
||||||
raw,
|
raw,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::plant_state::{MoistureSensorState, PlantState};
|
|||||||
use crate::tank::determine_tank_state;
|
use crate::tank::determine_tank_state;
|
||||||
use crate::{get_version, BOARD_ACCESS};
|
use crate::{get_version, BOARD_ACCESS};
|
||||||
use alloc::format;
|
use alloc::format;
|
||||||
use alloc::string::{String, ToString};
|
use alloc::string::String;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use chrono_tz::Tz;
|
use chrono_tz::Tz;
|
||||||
use core::str::FromStr;
|
use core::str::FromStr;
|
||||||
@@ -43,7 +43,6 @@ where
|
|||||||
plant_state.push(PlantState::read_hardware_state(moistures, i, &mut board).await);
|
plant_state.push(PlantState::read_hardware_state(moistures, i, &mut board).await);
|
||||||
}
|
}
|
||||||
let a = Vec::from_iter(plant_state.iter().map(|s| match &s.sensor_a {
|
let a = Vec::from_iter(plant_state.iter().map(|s| match &s.sensor_a {
|
||||||
MoistureSensorState::Disabled => "disabled".to_string(),
|
|
||||||
MoistureSensorState::MoistureValue {
|
MoistureSensorState::MoistureValue {
|
||||||
raw_hz,
|
raw_hz,
|
||||||
moisture_percent,
|
moisture_percent,
|
||||||
@@ -53,7 +52,6 @@ where
|
|||||||
MoistureSensorState::SensorError(err) => format!("{err:?}"),
|
MoistureSensorState::SensorError(err) => format!("{err:?}"),
|
||||||
}));
|
}));
|
||||||
let b = Vec::from_iter(plant_state.iter().map(|s| match &s.sensor_b {
|
let b = Vec::from_iter(plant_state.iter().map(|s| match &s.sensor_b {
|
||||||
MoistureSensorState::Disabled => "disabled".to_string(),
|
|
||||||
MoistureSensorState::MoistureValue {
|
MoistureSensorState::MoistureValue {
|
||||||
raw_hz,
|
raw_hz,
|
||||||
moisture_percent,
|
moisture_percent,
|
||||||
@@ -128,11 +126,7 @@ pub(crate) async fn get_battery_state<T, const N: usize>(
|
|||||||
_request: &mut Connection<'_, T, N>,
|
_request: &mut Connection<'_, T, N>,
|
||||||
) -> FatResult<Option<String>> {
|
) -> FatResult<Option<String>> {
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||||
let battery_state = board
|
let battery_state = board.board_hal.get_battery_monitor().get_state().await?;
|
||||||
.board_hal
|
|
||||||
.get_battery_monitor()
|
|
||||||
.get_state()
|
|
||||||
.await?;
|
|
||||||
Ok(Some(serde_json::to_string(&battery_state)?))
|
Ok(Some(serde_json::to_string(&battery_state)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ use crate::BOARD_ACCESS;
|
|||||||
use edge_http::io::server::Connection;
|
use edge_http::io::server::Connection;
|
||||||
use edge_http::Method;
|
use edge_http::Method;
|
||||||
use edge_nal::io::{Read, Write};
|
use edge_nal::io::{Read, Write};
|
||||||
use log::info;
|
use esp_hal_ota::OtaError;
|
||||||
|
use log::{error, info};
|
||||||
|
|
||||||
pub(crate) async fn ota_operations<T, const N: usize>(
|
pub(crate) async fn ota_operations<T, const N: usize>(
|
||||||
conn: &mut Connection<'_, T, { N }>,
|
conn: &mut Connection<'_, T, { N }>,
|
||||||
@@ -28,27 +29,45 @@ where
|
|||||||
Some(200)
|
Some(200)
|
||||||
}
|
}
|
||||||
Method::Post => {
|
Method::Post => {
|
||||||
let mut offset = 0_usize;
|
let size = read_up_to_bytes_from_request(conn, Some(4)).await?;
|
||||||
|
let flash_size = u32::from_le_bytes(size[..4].try_into().unwrap());
|
||||||
|
info!("flash size: {flash_size}");
|
||||||
|
let crc32 = read_up_to_bytes_from_request(conn, Some(4)).await?;
|
||||||
|
let target_crc = u32::from_le_bytes(crc32[..4].try_into().unwrap());
|
||||||
|
info!("crc32: {target_crc}");
|
||||||
|
|
||||||
|
BOARD_ACCESS
|
||||||
|
.get()
|
||||||
|
.await
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.board_hal
|
||||||
|
.get_esp()
|
||||||
|
.ota
|
||||||
|
.ota_begin(flash_size, target_crc)?;
|
||||||
|
|
||||||
let mut chunk = 0;
|
let mut chunk = 0;
|
||||||
loop {
|
loop {
|
||||||
let buf = read_up_to_bytes_from_request(conn, Some(4096)).await?;
|
let buf = read_up_to_bytes_from_request(conn, Some(4096)).await?;
|
||||||
if buf.is_empty() {
|
if buf.is_empty() {
|
||||||
info!("file request for ota finished");
|
error!("Upload finished, but no enough data received.");
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
return Err(OtaError::WrongCRC)?;
|
||||||
board.board_hal.get_esp().finalize_ota().await?;
|
|
||||||
break;
|
|
||||||
} else {
|
} else {
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||||
board.board_hal.progress(chunk as u32).await;
|
board.board_hal.progress(chunk as u32).await;
|
||||||
// Erase next block if we are at a 4K boundary (including the first block at offset 0)
|
// Erase next block if we are at a 4K boundary (including the first block at offset 0)
|
||||||
info!("erasing and writing block 0x{offset:x}");
|
let finsihed = board.board_hal.get_esp().ota.ota_write_chunk(&buf)?;
|
||||||
board
|
if finsihed {
|
||||||
.board_hal
|
board.board_hal.get_esp().ota.ota_flush(true, true)?;
|
||||||
.get_esp()
|
|
||||||
.write_ota(offset as u32, &buf)
|
board.board_hal.get_esp().set_restart_to_conf(true);
|
||||||
.await?;
|
break;
|
||||||
|
}
|
||||||
|
info!(
|
||||||
|
"Progress: {}%",
|
||||||
|
(board.board_hal.get_esp().ota.get_ota_progress() * 100.0) as u8
|
||||||
|
);
|
||||||
}
|
}
|
||||||
offset += buf.len();
|
|
||||||
chunk += 1;
|
chunk += 1;
|
||||||
}
|
}
|
||||||
BOARD_ACCESS
|
BOARD_ACCESS
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::config::PlantControllerConfig;
|
use crate::config::PlantControllerConfig;
|
||||||
use crate::fat_error::FatResult;
|
use crate::fat_error::FatResult;
|
||||||
use crate::hal::{esp_set_time, Detection, DetectionRequest};
|
use crate::hal::{esp_set_time, Detection};
|
||||||
use crate::webserver::read_up_to_bytes_from_request;
|
use crate::webserver::read_up_to_bytes_from_request;
|
||||||
use crate::{do_secure_pump, BOARD_ACCESS};
|
use crate::{do_secure_pump, BOARD_ACCESS};
|
||||||
use alloc::string::{String, ToString};
|
use alloc::string::{String, ToString};
|
||||||
@@ -142,9 +142,6 @@ where
|
|||||||
|
|
||||||
board.board_hal.can_power(can_power_request.state).await?;
|
board.board_hal.can_power(can_power_request.state).await?;
|
||||||
let enable = can_power_request.state;
|
let enable = can_power_request.state;
|
||||||
info!(
|
info!("set can power to {enable}");
|
||||||
"set can power to {enable}"
|
|
||||||
);
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -163,8 +163,7 @@ export interface VersionInfo {
|
|||||||
git_hash: string,
|
git_hash: string,
|
||||||
build_time: string,
|
build_time: string,
|
||||||
current: string,
|
current: string,
|
||||||
slot0_state: string,
|
state: string
|
||||||
slot1_state: string,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BatteryState {
|
export interface BatteryState {
|
||||||
|
|||||||
@@ -191,6 +191,19 @@ export class Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uploadNewFirmware(file: File) {
|
uploadNewFirmware(file: File) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = () => {
|
||||||
|
const arrayBuffer = reader.result as ArrayBuffer;
|
||||||
|
const data = new Uint8Array(arrayBuffer);
|
||||||
|
const crc = this.crc32(data);
|
||||||
|
const size = data.length;
|
||||||
|
|
||||||
|
const payload = new Uint8Array(size + 8);
|
||||||
|
const view = new DataView(payload.buffer);
|
||||||
|
view.setUint32(0, size, true);
|
||||||
|
view.setUint32(4, crc, true);
|
||||||
|
payload.set(data, 8);
|
||||||
|
|
||||||
let current = 0;
|
let current = 0;
|
||||||
let 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 + ")")
|
||||||
@@ -220,7 +233,28 @@ export class Controller {
|
|||||||
toast.error("OTA upload was aborted.");
|
toast.error("OTA upload was aborted.");
|
||||||
}, false);
|
}, false);
|
||||||
ajax.open("POST", PUBLIC_URL + "/ota");
|
ajax.open("POST", PUBLIC_URL + "/ota");
|
||||||
ajax.send(file);
|
ajax.send(payload);
|
||||||
|
};
|
||||||
|
reader.onerror = () => {
|
||||||
|
toast.error("Error reading firmware file.");
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
private crc32(data: Uint8Array): number {
|
||||||
|
let crc = 0xFFFFFFFF;
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
let byte = data[i];
|
||||||
|
crc ^= byte;
|
||||||
|
for (let j = 0; j < 8; j++) {
|
||||||
|
if (crc & 1) {
|
||||||
|
crc = (crc >>> 1) ^ 0xEDB88320;
|
||||||
|
} else {
|
||||||
|
crc = crc >>> 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (crc ^ 0xFFFFFFFF) >>> 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
async version(): Promise<void> {
|
async version(): Promise<void> {
|
||||||
@@ -266,9 +300,11 @@ export class Controller {
|
|||||||
})
|
})
|
||||||
.then(response => response.text())
|
.then(response => response.text())
|
||||||
.then(text => statusCallback(text))
|
.then(text => statusCallback(text))
|
||||||
.then( _ => {
|
.then(_ => {
|
||||||
controller.progressview.removeProgress("set_config");
|
controller.progressview.removeProgress("set_config");
|
||||||
setTimeout(() => { controller.downloadConfig() }, 250)
|
setTimeout(() => {
|
||||||
|
controller.downloadConfig()
|
||||||
|
}, 250)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,7 +326,6 @@ export class Controller {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
body: pretty
|
body: pretty
|
||||||
}).then(
|
}).then(
|
||||||
|
|
||||||
_ => controller.progressview.removeProgress("write_rtc")
|
_ => controller.progressview.removeProgress("write_rtc")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -378,9 +413,9 @@ export class Controller {
|
|||||||
|
|
||||||
var pretty = JSON.stringify(detection, undefined, 1);
|
var pretty = JSON.stringify(detection, undefined, 1);
|
||||||
|
|
||||||
fetch(PUBLIC_URL + "/detect_sensors", { method: "POST", body: pretty })
|
fetch(PUBLIC_URL + "/detect_sensors", {method: "POST", body: pretty})
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then (json => json as Detection)
|
.then(json => json as Detection)
|
||||||
.then(json => {
|
.then(json => {
|
||||||
clearTimeout(timerId);
|
clearTimeout(timerId);
|
||||||
controller.progressview.removeProgress("detect_sensors");
|
controller.progressview.removeProgress("detect_sensors");
|
||||||
@@ -531,7 +566,7 @@ export class Controller {
|
|||||||
|
|
||||||
private setCanPower(checked: boolean) {
|
private setCanPower(checked: boolean) {
|
||||||
var body: CanPower = {
|
var body: CanPower = {
|
||||||
state : checked
|
state: checked
|
||||||
}
|
}
|
||||||
var pretty = JSON.stringify(body, undefined, 1);
|
var pretty = JSON.stringify(body, undefined, 1);
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ export class OTAView {
|
|||||||
readonly firmware_buildtime: HTMLDivElement;
|
readonly firmware_buildtime: HTMLDivElement;
|
||||||
readonly firmware_githash: HTMLDivElement;
|
readonly firmware_githash: HTMLDivElement;
|
||||||
readonly firmware_partition: HTMLDivElement;
|
readonly firmware_partition: HTMLDivElement;
|
||||||
readonly firmware_state0: HTMLDivElement;
|
readonly firmware_state: HTMLDivElement;
|
||||||
readonly firmware_state1: HTMLDivElement;
|
|
||||||
|
|
||||||
constructor(controller: Controller) {
|
constructor(controller: Controller) {
|
||||||
(document.getElementById("firmwareview") as HTMLElement).innerHTML = require("./ota.html")
|
(document.getElementById("firmwareview") as HTMLElement).innerHTML = require("./ota.html")
|
||||||
@@ -18,8 +18,7 @@ export class OTAView {
|
|||||||
this.firmware_githash = document.getElementById("firmware_githash") as HTMLDivElement;
|
this.firmware_githash = document.getElementById("firmware_githash") as HTMLDivElement;
|
||||||
this.firmware_partition = document.getElementById("firmware_partition") as HTMLDivElement;
|
this.firmware_partition = document.getElementById("firmware_partition") as HTMLDivElement;
|
||||||
|
|
||||||
this.firmware_state0 = document.getElementById("firmware_state0") as HTMLDivElement;
|
this.firmware_state = document.getElementById("firmware_state") as HTMLDivElement;
|
||||||
this.firmware_state1 = document.getElementById("firmware_state1") as HTMLDivElement;
|
|
||||||
|
|
||||||
|
|
||||||
const file = document.getElementById("firmware_file") as HTMLInputElement;
|
const file = document.getElementById("firmware_file") as HTMLInputElement;
|
||||||
@@ -42,7 +41,6 @@ export class OTAView {
|
|||||||
this.firmware_buildtime.innerText = versionInfo.build_time;
|
this.firmware_buildtime.innerText = versionInfo.build_time;
|
||||||
this.firmware_githash.innerText = versionInfo.git_hash;
|
this.firmware_githash.innerText = versionInfo.git_hash;
|
||||||
this.firmware_partition.innerText = versionInfo.current;
|
this.firmware_partition.innerText = versionInfo.current;
|
||||||
this.firmware_state0.innerText = versionInfo.slot0_state;
|
this.firmware_state.innerText = versionInfo.state;
|
||||||
this.firmware_state1.innerText = versionInfo.slot1_state;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user