fix ota abort/invalid switching
This commit is contained in:
12
rust/all.sh
Executable file
12
rust/all.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
rm ./src/webserver/index.html.gz
|
||||
rm ./src/webserver/bundle.js.gz
|
||||
set -e
|
||||
cd ./src_webpack/
|
||||
npx webpack build
|
||||
cp index.html.gz ../src/webserver/index.html.gz
|
||||
cp bundle.js.gz ../src/webserver/bundle.js.gz
|
||||
cd ../
|
||||
|
||||
cargo build --release
|
||||
espflash save-image --bootloader bootloader.bin --partition-table partitions.csv --chip esp32c6 target/riscv32imac-unknown-none-elf/release/plant-ctrl2 image.bin
|
||||
espflash flash --monitor --bootloader bootloader.bin --chip esp32c6 --baud 921600 --partition-table partitions.csv target/riscv32imac-unknown-none-elf/release/plant-ctrl2
|
@@ -7,5 +7,5 @@ cp index.html.gz ../src/webserver/index.html.gz
|
||||
cp bundle.js.gz ../src/webserver/bundle.js.gz
|
||||
cd ../
|
||||
|
||||
cargo build
|
||||
cargo build --release
|
||||
espflash flash --monitor --bootloader bootloader.bin --chip esp32c6 --baud 921600 --partition-table partitions.csv target/riscv32imac-unknown-none-elf/release/plant-ctrl2
|
||||
|
@@ -65,6 +65,9 @@ pub enum FatError {
|
||||
CanBusError {
|
||||
error: EspTwaiError,
|
||||
},
|
||||
SNTPError {
|
||||
error: sntpc::Error,
|
||||
},
|
||||
}
|
||||
|
||||
pub type FatResult<T> = Result<T, FatError>;
|
||||
@@ -96,6 +99,7 @@ impl fmt::Display for FatError {
|
||||
FatError::CanBusError { error } => {
|
||||
write!(f, "CanBusError {:?}", error)
|
||||
}
|
||||
FatError::SNTPError { error } => write!(f, "SNTPError {:?}", error),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -302,7 +306,13 @@ impl From<nb::Error<EspTwaiError>> for FatError {
|
||||
impl From<NorFlashErrorKind> for FatError {
|
||||
fn from(value: NorFlashErrorKind) -> Self {
|
||||
FatError::String {
|
||||
error: value.to_string()
|
||||
error: value.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sntpc::Error> for FatError {
|
||||
fn from(value: sntpc::Error) -> Self {
|
||||
FatError::SNTPError { error: value }
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
use crate::bail;
|
||||
use crate::config::{NetworkConfig, PlantControllerConfig};
|
||||
use crate::hal::{get_next_slot, PLANT_COUNT, TIME_ACCESS};
|
||||
use crate::hal::{get_current_slot_and_fix_ota_data, PLANT_COUNT, TIME_ACCESS};
|
||||
use crate::log::{LogMessage, LOG_ACCESS};
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::Serialize;
|
||||
@@ -19,10 +19,10 @@ use embassy_net::{DhcpConfig, Ipv4Cidr, Runner, Stack, StackResources, StaticCon
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::mutex::{Mutex, MutexGuard};
|
||||
use embassy_sync::once_lock::OnceLock;
|
||||
use embassy_time::{Duration, Timer};
|
||||
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::ota::{Ota, OtaImageState, Slot};
|
||||
use esp_bootloader_esp_idf::partitions::FlashRegion;
|
||||
use esp_hal::gpio::{Input, RtcPinWithResistors};
|
||||
use esp_hal::rng::Rng;
|
||||
@@ -128,7 +128,10 @@ pub struct Esp<'a> {
|
||||
pub wake_gpio1: esp_hal::peripherals::GPIO1<'static>,
|
||||
|
||||
pub ota: Ota<'static, FlashStorage>,
|
||||
pub ota_next: &'static mut FlashRegion<'static, FlashStorage>,
|
||||
pub ota_target: &'static mut FlashRegion<'static, FlashStorage>,
|
||||
pub current: Slot,
|
||||
pub slot0_state: OtaImageState,
|
||||
pub slot1_state: OtaImageState,
|
||||
}
|
||||
|
||||
// SAFETY: On this target we never move Esp across OS threads; the firmware runs single-core
|
||||
@@ -213,43 +216,15 @@ impl Esp<'_> {
|
||||
Ok((buf, read))
|
||||
}
|
||||
|
||||
pub(crate) fn get_current_ota_slot(&mut self) -> String {
|
||||
match get_next_slot(&mut self.ota) {
|
||||
Ok(slot) => {
|
||||
format!("{:?}", slot.next())
|
||||
}
|
||||
Err(err) => {
|
||||
format!("{:?}", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_ota_state(&mut self) -> String {
|
||||
match self.ota.current_ota_state() {
|
||||
Ok(state) => {
|
||||
format!("{:?}", state)
|
||||
}
|
||||
Err(err) => {
|
||||
format!("{:?}", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn write_ota(&mut self, offset: u32, buf: &[u8]) -> Result<(), FatError> {
|
||||
if self.ota.current_ota_state() == Ok(OtaImageState::Invalid) {
|
||||
bail!("Invalid OTA state, refusing ota write")
|
||||
}
|
||||
if self.ota.current_ota_state() == Ok(OtaImageState::Undefined) {
|
||||
bail!("Invalid OTA state, refusing ota write")
|
||||
}
|
||||
let _ = check_erase(self.ota_next, offset, offset + 4096);
|
||||
self.ota_next.erase(offset, offset + 4096)?;
|
||||
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_next.write(offset, buf)?;
|
||||
self.ota_next.read(offset, read_back)?;
|
||||
self.ota_target.write(offset, buf)?;
|
||||
self.ota_target.read(offset, read_back)?;
|
||||
if buf != read_back {
|
||||
info!("Expected {:?} but got {:?}", buf, read_back);
|
||||
bail!(
|
||||
@@ -261,23 +236,16 @@ impl Esp<'_> {
|
||||
}
|
||||
|
||||
pub(crate) async fn finalize_ota(&mut self) -> Result<(), FatError> {
|
||||
if self.ota.current_ota_state() == Ok(OtaImageState::Invalid) {
|
||||
bail!("Invalid OTA state, refusing ota write")
|
||||
}
|
||||
if self.ota.current_ota_state() == Ok(OtaImageState::Undefined) {
|
||||
bail!("Invalid OTA state, refusing ota write")
|
||||
}
|
||||
|
||||
let current_state = self.ota.current_ota_state()?;
|
||||
info!("current state {:?}", current_state);
|
||||
let next_slot = get_next_slot(&mut self.ota)?;
|
||||
info!("current slot {:?}", next_slot.next());
|
||||
if current_state == OtaImageState::PendingVerify {
|
||||
info!("verifying ota image from pending");
|
||||
let current = self.ota.current_slot()?;
|
||||
if self.ota.current_ota_state()? != OtaImageState::Valid {
|
||||
info!(
|
||||
"Validating current slot {:?} as it was able to ota",
|
||||
current
|
||||
);
|
||||
self.ota.set_current_ota_state(Valid)?;
|
||||
}
|
||||
|
||||
self.ota.set_current_slot(next_slot)?;
|
||||
self.ota.set_current_slot(current.next())?;
|
||||
info!("switched slot");
|
||||
self.ota.set_current_ota_state(OtaImageState::New)?;
|
||||
info!("switched state for new partition");
|
||||
@@ -327,16 +295,19 @@ impl Esp<'_> {
|
||||
let mut counter = 0;
|
||||
loop {
|
||||
let addr: IpAddr = ntp_addrs[0].into();
|
||||
let result = get_time(SocketAddr::from((addr, 123)), &socket, context).await;
|
||||
let timeout = get_time(SocketAddr::from((addr, 123)), &socket, context)
|
||||
.with_timeout(Duration::from_millis((_max_wait_ms / 10) as u64))
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(time) => {
|
||||
match timeout {
|
||||
Ok(result) => {
|
||||
let time = result?;
|
||||
info!("Time: {:?}", time);
|
||||
return DateTime::from_timestamp(time.seconds as i64, 0)
|
||||
.context("Could not convert Sntp result");
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error: {:?}", e);
|
||||
Err(err) => {
|
||||
warn!("sntp timeout, retry: {:?}", err);
|
||||
counter += 1;
|
||||
if counter > 10 {
|
||||
bail!("Failed to get time from NTP server");
|
||||
|
@@ -9,7 +9,6 @@ mod v3_shift_register;
|
||||
mod v4_hal;
|
||||
mod v4_sensor;
|
||||
mod water;
|
||||
|
||||
use crate::alloc::string::ToString;
|
||||
use crate::hal::rtc::{DS3231Module, RTCModuleInteraction};
|
||||
use esp_hal::peripherals::Peripherals;
|
||||
@@ -44,6 +43,7 @@ use esp_hal::peripherals::GPIO8;
|
||||
use esp_hal::peripherals::TWAI0;
|
||||
|
||||
use crate::{
|
||||
bail,
|
||||
config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig},
|
||||
hal::{
|
||||
battery::{BatteryInteraction, NoBatteryMonitor},
|
||||
@@ -83,9 +83,11 @@ use crate::hal::water::TankSensor;
|
||||
use crate::log::LOG_ACCESS;
|
||||
use embassy_sync::mutex::Mutex;
|
||||
use embassy_sync::once_lock::OnceLock;
|
||||
use embedded_storage::nor_flash::ReadNorFlash;
|
||||
use esp_alloc as _;
|
||||
use esp_backtrace as _;
|
||||
use esp_bootloader_esp_idf::ota::{Ota, OtaImageState, Slot};
|
||||
use esp_bootloader_esp_idf::ota::{Ota, OtaImageState};
|
||||
use esp_bootloader_esp_idf::ota::{Slot as ota_slot, Slot};
|
||||
use esp_hal::delay::Delay;
|
||||
use esp_hal::i2c::master::{BusTimeout, Config, I2c};
|
||||
use esp_hal::pcnt::unit::Unit;
|
||||
@@ -101,12 +103,15 @@ use esp_wifi::{init, EspWifiController};
|
||||
use littlefs2::fs::{Allocation, Filesystem as lfs2Filesystem};
|
||||
use littlefs2::object_safe::DynStorage;
|
||||
use log::{error, info, warn};
|
||||
use portable_atomic::AtomicBool;
|
||||
|
||||
pub static TIME_ACCESS: OnceLock<Mutex<CriticalSectionRawMutex, Rtc>> = OnceLock::new();
|
||||
|
||||
//Only support for 8 right now!
|
||||
pub const PLANT_COUNT: usize = 8;
|
||||
|
||||
pub static PROGRESS_ACTIVE: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
const TANK_MULTI_SAMPLE: usize = 11;
|
||||
pub static I2C_DRIVER: OnceLock<
|
||||
embassy_sync::blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<I2c<Blocking>>>,
|
||||
@@ -148,6 +153,9 @@ pub trait BoardInteraction<'a> {
|
||||
async fn get_mptt_current(&mut self) -> Result<Current, FatError>;
|
||||
|
||||
async fn progress(&mut self, counter: u32) {
|
||||
// Indicate progress is active to suppress default wait_infinity blinking
|
||||
crate::hal::PROGRESS_ACTIVE.store(true, core::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
let current = counter % PLANT_COUNT as u32;
|
||||
for led in 0..PLANT_COUNT {
|
||||
if let Err(err) = self.fault(led, current == led as u32).await {
|
||||
@@ -165,6 +173,9 @@ pub trait BoardInteraction<'a> {
|
||||
}
|
||||
}
|
||||
let _ = self.general_fault(false).await;
|
||||
|
||||
// Reset progress active flag so wait_infinity can resume blinking
|
||||
crate::hal::PROGRESS_ACTIVE.store(false, core::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,10 +314,6 @@ impl PlantHal {
|
||||
let pt =
|
||||
esp_bootloader_esp_idf::partitions::read_partition_table(storage_ota, tablebuffer)?;
|
||||
|
||||
// List all partitions - this is just FYI
|
||||
for i in 0..pt.len() {
|
||||
info!("{:?}", pt.get_partition(i));
|
||||
}
|
||||
let ota_data = mk_static!(
|
||||
PartitionEntry,
|
||||
pt.find_partition(esp_bootloader_esp_idf::partitions::PartitionType::Data(
|
||||
@@ -320,13 +327,18 @@ impl PlantHal {
|
||||
ota_data.as_embedded_storage(storage_ota)
|
||||
);
|
||||
|
||||
let state_0 = ota_state(ota_slot::Slot0, ota_data);
|
||||
let state_1 = ota_state(ota_slot::Slot1, ota_data);
|
||||
let mut ota = Ota::new(ota_data)?;
|
||||
let running = get_current_slot_and_fix_ota_data(&mut ota, state_0, state_1)?;
|
||||
let target = running.next();
|
||||
|
||||
let state = ota.current_ota_state().unwrap_or_default();
|
||||
info!("Current OTA state: {:?}", state);
|
||||
let next_slot = get_next_slot(&mut ota)?;
|
||||
info!("Next OTA slot: {:?}", next_slot);
|
||||
let ota_next = match next_slot {
|
||||
info!("Currently running OTA slot: {:?}", running);
|
||||
info!("Slot0 state: {:?}", state_0);
|
||||
info!("Slot1 state: {:?}", state_1);
|
||||
|
||||
//obtain current_state and next_state here!
|
||||
let ota_target = match target {
|
||||
Slot::None => {
|
||||
panic!("No OTA slot active?");
|
||||
}
|
||||
@@ -342,11 +354,11 @@ impl PlantHal {
|
||||
.context("Partition table invalid no ota1")?,
|
||||
};
|
||||
|
||||
let ota_next = mk_static!(PartitionEntry, ota_next);
|
||||
let ota_target = mk_static!(PartitionEntry, ota_target);
|
||||
let storage_ota = mk_static!(FlashStorage, FlashStorage::new());
|
||||
let ota_next = mk_static!(
|
||||
let ota_target = mk_static!(
|
||||
FlashRegion<FlashStorage>,
|
||||
ota_next.as_embedded_storage(storage_ota)
|
||||
ota_target.as_embedded_storage(storage_ota)
|
||||
);
|
||||
|
||||
let data_partition = pt
|
||||
@@ -391,7 +403,10 @@ impl PlantHal {
|
||||
boot_button,
|
||||
wake_gpio1,
|
||||
ota,
|
||||
ota_next,
|
||||
ota_target,
|
||||
current: running,
|
||||
slot0_state: state_0,
|
||||
slot1_state: state_1,
|
||||
};
|
||||
|
||||
//init,reset rtc memory depending on cause
|
||||
@@ -573,29 +588,75 @@ impl PlantHal {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_next_slot(ota: &mut Ota<FlashStorage>) -> Result<Slot, FatError> {
|
||||
let next_slot = {
|
||||
fn ota_state(slot: ota_slot, ota_data: &mut FlashRegion<FlashStorage>) -> 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
|
||||
if slot == ota_slot::None {
|
||||
return OtaImageState::Undefined;
|
||||
}
|
||||
let mut slot_buf = [0u8; 32];
|
||||
if slot == ota_slot::Slot0 {
|
||||
let _ = ota_data.read(0x0000, &mut slot_buf);
|
||||
} else {
|
||||
let _ = ota_data.read(0x1000, &mut slot_buf);
|
||||
}
|
||||
let raw_state = u32::from_le_bytes(slot_buf[24..28].try_into().unwrap_or([0xff; 4]));
|
||||
let state0 = OtaImageState::try_from(raw_state).unwrap_or(OtaImageState::Undefined);
|
||||
state0
|
||||
}
|
||||
|
||||
fn get_current_slot_and_fix_ota_data(
|
||||
ota: &mut Ota<FlashStorage>,
|
||||
state0: OtaImageState,
|
||||
state1: OtaImageState,
|
||||
) -> Result<ota_slot, 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_slot()?;
|
||||
match state {
|
||||
OtaImageState::New => current.next(),
|
||||
OtaImageState::PendingVerify => current.next(),
|
||||
OtaImageState::Valid => current.next(),
|
||||
if swap {
|
||||
let other = match current {
|
||||
ota_slot::Slot0 => state1,
|
||||
ota_slot::Slot1 => state0,
|
||||
_ => OtaImageState::Invalid,
|
||||
};
|
||||
|
||||
match other {
|
||||
OtaImageState::Invalid => {
|
||||
//we actually booted other slot, than partition table assumes
|
||||
current
|
||||
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 {:?} other state has {:?} swapping",
|
||||
state, other
|
||||
);
|
||||
ota.set_current_slot(current.next())?;
|
||||
//we actually booted other slot, than partition table assumes
|
||||
current
|
||||
}
|
||||
OtaImageState::Undefined => {
|
||||
//missing bootloader?
|
||||
current.next()
|
||||
}
|
||||
}
|
||||
return Ok(ota.current_slot()?);
|
||||
};
|
||||
Ok(next_slot)
|
||||
Ok(current)
|
||||
}
|
||||
|
||||
pub async fn esp_time() -> DateTime<Utc> {
|
||||
|
@@ -17,6 +17,7 @@ use crate::config::{NetworkConfig, PlantConfig};
|
||||
use crate::fat_error::FatResult;
|
||||
use crate::hal::esp::MQTT_STAY_ALIVE;
|
||||
use crate::hal::{esp_time, TIME_ACCESS};
|
||||
use crate::hal::PROGRESS_ACTIVE;
|
||||
use crate::log::{log, LOG_ACCESS};
|
||||
use crate::tank::{determine_tank_state, TankError, TankState, WATER_FROZEN_THRESH};
|
||||
use crate::webserver::http_server;
|
||||
@@ -822,18 +823,9 @@ async fn publish_firmware_info(
|
||||
let esp = board.board_hal.get_esp();
|
||||
let _ = esp.mqtt_publish("/firmware/address", ip_address).await;
|
||||
let _ = esp
|
||||
.mqtt_publish("/firmware/githash", &version.git_hash)
|
||||
.await;
|
||||
let _ = esp
|
||||
.mqtt_publish("/firmware/buildtime", &version.build_time)
|
||||
.mqtt_publish("/firmware/state", format!("{:?}", &version).as_str())
|
||||
.await;
|
||||
let _ = esp.mqtt_publish("/firmware/last_online", timezone_time);
|
||||
let state = esp.get_ota_state();
|
||||
let _ = esp.mqtt_publish("/firmware/ota_state", &state).await;
|
||||
let slot = esp.get_current_ota_slot();
|
||||
let _ = esp
|
||||
.mqtt_publish("/firmware/ota_slot", &format!("slot{slot}"))
|
||||
.await;
|
||||
let _ = esp.mqtt_publish("/state", "online").await;
|
||||
}
|
||||
macro_rules! mk_static {
|
||||
@@ -995,6 +987,8 @@ async fn wait_infinity(
|
||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||
update_charge_indicator(&mut board).await;
|
||||
|
||||
// Skip default blink code when a progress display is active
|
||||
if !PROGRESS_ACTIVE.load(Ordering::Relaxed) {
|
||||
match wait_type {
|
||||
WaitType::MissingConfig => {
|
||||
// Keep existing behavior: circular filling pattern
|
||||
@@ -1021,10 +1015,14 @@ async fn wait_infinity(
|
||||
}
|
||||
board.board_hal.general_fault(true).await;
|
||||
}
|
||||
}
|
||||
|
||||
Timer::after_millis(delay).await;
|
||||
{
|
||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||
|
||||
// Skip clearing LEDs when progress is active to avoid interrupting the progress display
|
||||
if !PROGRESS_ACTIVE.load(Ordering::Relaxed) {
|
||||
board.board_hal.general_fault(false).await;
|
||||
|
||||
// Clear all LEDs
|
||||
@@ -1032,6 +1030,7 @@ async fn wait_infinity(
|
||||
let _ = board.board_hal.fault(i, false).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer::after_millis(delay).await;
|
||||
|
||||
@@ -1096,14 +1095,12 @@ async fn get_version(
|
||||
let hash = &env!("VERGEN_GIT_SHA")[0..8];
|
||||
|
||||
let board = board.board_hal.get_esp();
|
||||
|
||||
let ota_slot = board.get_current_ota_slot();
|
||||
let ota_state = board.get_ota_state();
|
||||
VersionInfo {
|
||||
git_hash: branch + "@" + hash,
|
||||
build_time: env!("VERGEN_BUILD_TIMESTAMP").to_owned(),
|
||||
partition: ota_slot,
|
||||
ota_state,
|
||||
current: format!("{:?}", board.current),
|
||||
slot0_state: format!("{:?}", board.slot0_state),
|
||||
slot1_state: format!("{:?}", board.slot1_state),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1111,6 +1108,7 @@ async fn get_version(
|
||||
struct VersionInfo {
|
||||
git_hash: String,
|
||||
build_time: String,
|
||||
partition: String,
|
||||
ota_state: String,
|
||||
current: String,
|
||||
slot0_state: String,
|
||||
slot1_state: String,
|
||||
}
|
||||
|
@@ -240,34 +240,6 @@ pub async fn http_server(reboot_now: Arc<AtomicBool>, stack: Stack<'static>) {
|
||||
info!("Webserver started and waiting for connections");
|
||||
|
||||
//TODO https if mbed_esp lands
|
||||
|
||||
// server
|
||||
// .fn_handler("/ota", Method::Post, |request| {
|
||||
// handle_error_to500(request, ota)
|
||||
// })
|
||||
// .unwrap();
|
||||
// server
|
||||
// .fn_handler("/ota", Method::Options, |request| {
|
||||
// cors_response(request, 200, "")
|
||||
// })
|
||||
// .unwrap();
|
||||
// let reboot_now_for_reboot = reboot_now.clone();
|
||||
// server
|
||||
// .fn_handler("/reboot", Method::Post, move |_| {
|
||||
// BOARD_ACCESS
|
||||
// .lock()
|
||||
// .unwrap()
|
||||
// .board_hal
|
||||
// .get_esp()
|
||||
// .set_restart_to_conf(true);
|
||||
// reboot_now_for_reboot.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
// anyhow::Ok(())
|
||||
// })
|
||||
// .unwrap();
|
||||
//
|
||||
// unsafe { vTaskDelay(1) };
|
||||
//
|
||||
// server
|
||||
}
|
||||
|
||||
async fn handle_json<'a, T, const N: usize>(
|
||||
|
@@ -30,9 +30,6 @@ where
|
||||
Method::Post => {
|
||||
let mut offset = 0_usize;
|
||||
let mut chunk = 0;
|
||||
|
||||
// Erase only a single 4K block right before writing into it.
|
||||
// The first block will be erased when offset == 0 below.
|
||||
loop {
|
||||
let buf = read_up_to_bytes_from_request(conn, Some(4096)).await?;
|
||||
if buf.len() == 0 {
|
||||
|
@@ -157,9 +157,9 @@ export interface Moistures {
|
||||
export interface VersionInfo {
|
||||
git_hash: string,
|
||||
build_time: string,
|
||||
partition: string,
|
||||
ota_state: string
|
||||
|
||||
current: string,
|
||||
slot0_state: string,
|
||||
slot1_state: string,
|
||||
}
|
||||
|
||||
export interface BatteryState {
|
||||
|
@@ -2,13 +2,16 @@
|
||||
.otakey {
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.otavalue {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.otaform {
|
||||
min-width: 100px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.otachooser {
|
||||
min-width: 100px;
|
||||
width: 100%;
|
||||
@@ -32,8 +35,12 @@
|
||||
<span class="otavalue" id="firmware_partition"></span>
|
||||
</div>
|
||||
<div class="flexcontainer">
|
||||
<span class="otakey">Status:</span>
|
||||
<span class="otavalue" id="firmware_state"></span>
|
||||
<span class="otakey">State0:</span>
|
||||
<span class="otavalue" id="firmware_state0"></span>
|
||||
</div>
|
||||
<div class="flexcontainer">
|
||||
<span class="otakey">State1:</span>
|
||||
<span class="otavalue" id="firmware_state1"></span>
|
||||
</div>
|
||||
|
||||
<div class="flexcontainer">
|
||||
|
@@ -6,7 +6,8 @@ export class OTAView {
|
||||
readonly firmware_buildtime: HTMLDivElement;
|
||||
readonly firmware_githash: HTMLDivElement;
|
||||
readonly firmware_partition: HTMLDivElement;
|
||||
readonly firmware_state: HTMLDivElement;
|
||||
readonly firmware_state0: HTMLDivElement;
|
||||
readonly firmware_state1: HTMLDivElement;
|
||||
|
||||
constructor(controller: Controller) {
|
||||
(document.getElementById("firmwareview") as HTMLElement).innerHTML = require("./ota.html")
|
||||
@@ -16,7 +17,9 @@ export class OTAView {
|
||||
this.firmware_buildtime = document.getElementById("firmware_buildtime") as HTMLDivElement;
|
||||
this.firmware_githash = document.getElementById("firmware_githash") as HTMLDivElement;
|
||||
this.firmware_partition = document.getElementById("firmware_partition") as HTMLDivElement;
|
||||
this.firmware_state = document.getElementById("firmware_state") as HTMLDivElement;
|
||||
|
||||
this.firmware_state0 = document.getElementById("firmware_state0") as HTMLDivElement;
|
||||
this.firmware_state1 = document.getElementById("firmware_state1") as HTMLDivElement;
|
||||
|
||||
|
||||
const file = document.getElementById("firmware_file") as HTMLInputElement;
|
||||
@@ -38,7 +41,8 @@ export class OTAView {
|
||||
setVersion(versionInfo: VersionInfo) {
|
||||
this.firmware_buildtime.innerText = versionInfo.build_time;
|
||||
this.firmware_githash.innerText = versionInfo.git_hash;
|
||||
this.firmware_partition.innerText = versionInfo.partition;
|
||||
this.firmware_state.innerText = versionInfo.ota_state;
|
||||
this.firmware_partition.innerText = versionInfo.current;
|
||||
this.firmware_state0.innerText = versionInfo.slot0_state;
|
||||
this.firmware_state1.innerText = versionInfo.slot1_state;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user