From 27b18df78e8ee16ecb08b2ffff92e93eb08595f9 Mon Sep 17 00:00:00 2001 From: Empire Date: Wed, 1 Oct 2025 21:56:16 +0200 Subject: [PATCH] ota is back --- rust/src/hal/esp.rs | 28 +++++++++++++-- rust/src/hal/mod.rs | 23 ++++++------ rust/src/webserver/mod.rs | 5 ++- rust/src/webserver/ota.rs | 76 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 15 deletions(-) create mode 100644 rust/src/webserver/ota.rs diff --git a/rust/src/hal/esp.rs b/rust/src/hal/esp.rs index 915fa47..24f1382 100644 --- a/rust/src/hal/esp.rs +++ b/rust/src/hal/esp.rs @@ -20,8 +20,9 @@ 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 embedded_storage::nor_flash::ReadNorFlash; +use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; use esp_bootloader_esp_idf::ota::{Ota, OtaImageState}; +use esp_bootloader_esp_idf::ota::OtaImageState::Valid; use esp_bootloader_esp_idf::partitions::FlashRegion; use esp_hal::gpio::{Input, RtcPinWithResistors}; use esp_hal::rng::Rng; @@ -29,6 +30,7 @@ use esp_hal::rtc_cntl::{ sleep::{TimerWakeupSource, WakeupLevel}, Rtc, }; +use esp_hal::sha::Digest; use esp_hal::system::software_reset; use esp_println::println; use esp_storage::FlashStorage; @@ -127,7 +129,7 @@ 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_next: &'static mut FlashRegion<'static, FlashStorage> } // SAFETY: On this target we never move Esp across OS threads; the firmware runs single-core @@ -234,6 +236,28 @@ impl Esp<'_> { } } + pub(crate) async fn write_ota( + &mut self, + offset: u32, + buf: &[u8], + ) -> Result<(), FatError> { + self.ota_next.write(offset, buf)?; + Ok(()) + } + + pub(crate) async fn finalize_ota( + &mut self, + ) -> Result<(), FatError> { + let current_state = self.ota.current_ota_state()?; + let current_slot = self.ota.current_slot()?; + if current_state == OtaImageState::PendingVerify { + self.ota.set_current_ota_state(Valid)?; + } + self.ota.set_current_slot(current_slot.next())?; + self.set_restart_to_conf(true); + Ok(()) + } + // let current = ota.current_slot()?; // println!( // "current image state {:?} (only relevant if the bootloader was built with auto-rollback support)", diff --git a/rust/src/hal/mod.rs b/rust/src/hal/mod.rs index f38e70f..b34acb4 100644 --- a/rust/src/hal/mod.rs +++ b/rust/src/hal/mod.rs @@ -70,14 +70,12 @@ use eeprom24x::{Eeprom24x, SlaveAddr, Storage}; use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::CriticalSectionMutex; -use esp_bootloader_esp_idf::partitions::{ - AppPartitionSubType, DataPartitionSubType, FlashRegion, PartitionEntry, -}; +use esp_bootloader_esp_idf::partitions::{AppPartitionSubType, DataPartitionSubType, Error, FlashRegion, PartitionEntry}; use esp_hal::clock::CpuClock; use esp_hal::gpio::{Input, InputConfig, Pull}; use measurements::{Current, Voltage}; -use crate::fat_error::{FatError, FatResult}; +use crate::fat_error::{ContextExt, FatError, FatResult}; use crate::hal::battery::{print_battery_bq34z100, BQ34Z100G1}; use crate::hal::little_fs2storage_adapter::LittleFs2Filesystem; use crate::hal::water::TankSensor; @@ -87,6 +85,7 @@ use embassy_sync::once_lock::OnceLock; use esp_alloc as _; use esp_backtrace as _; use esp_bootloader_esp_idf::ota::Slot; +use esp_bootloader_esp_idf::ota::Slot::{Slot0, Slot1}; use esp_hal::delay::Delay; use esp_hal::i2c::master::{BusTimeout, Config, I2c}; use esp_hal::pcnt::unit::Unit; @@ -323,23 +322,23 @@ impl PlantHal { let mut ota = esp_bootloader_esp_idf::ota::Ota::new(ota_data)?; - let ota_partition = match ota.current_slot()? { + let next_slot = ota.current_slot()?.next(); + let ota_next = match next_slot { Slot::None => { panic!("No OTA slot active?"); } Slot::Slot0 => pt .find_partition(esp_bootloader_esp_idf::partitions::PartitionType::App( AppPartitionSubType::Ota0, - ))? - .expect("No OTA slot0 found"), + ))?.context("Partition table invalid no ota0")?, Slot::Slot1 => pt .find_partition(esp_bootloader_esp_idf::partitions::PartitionType::App( AppPartitionSubType::Ota1, - ))? - .expect("No OTA slot1 found"), + ))?.context("Partition table invalid no ota1")? + , }; - let ota_next = mk_static!(PartitionEntry, ota_partition); + let ota_next = mk_static!(PartitionEntry, ota_next); let storage_ota = mk_static!(FlashStorage, FlashStorage::new()); let ota_next = mk_static!( FlashRegion, @@ -388,7 +387,7 @@ impl PlantHal { boot_button, wake_gpio1, ota, - ota_next, + ota_next }; //init,reset rtc memory depending on cause @@ -589,4 +588,4 @@ pub async fn esp_set_time(time: DateTime) -> FatResult<()> { .get_rtc_module() .set_rtc_time(&time.to_utc()) .await -} +} \ No newline at end of file diff --git a/rust/src/webserver/mod.rs b/rust/src/webserver/mod.rs index 1dee0a8..bfeef01 100644 --- a/rust/src/webserver/mod.rs +++ b/rust/src/webserver/mod.rs @@ -6,6 +6,7 @@ mod get_json; mod get_log; mod get_static; mod post_json; +mod ota; use crate::fat_error::{FatError, FatResult}; use crate::webserver::backup_manager::{backup_config, backup_info, get_backup_config}; @@ -36,7 +37,7 @@ use embassy_net::Stack; use embassy_time::Instant; use embedded_io_async::{Read, Write}; use log::info; - +use crate::webserver::ota::ota_operations; // fn ota( // request: &mut Request<&mut EspHttpConnection>, // ) -> Result, anyhow::Error> { @@ -107,6 +108,8 @@ impl Handler for HTTPRequestRouter { let prefix = "/file?filename="; let status = if path.starts_with(prefix) { file_operations(conn, method, &path, &prefix).await? + } else if path == "/ota" { + ota_operations(conn,method).await? } else { match method { Method::Get => match path { diff --git a/rust/src/webserver/ota.rs b/rust/src/webserver/ota.rs new file mode 100644 index 0000000..f4b98b1 --- /dev/null +++ b/rust/src/webserver/ota.rs @@ -0,0 +1,76 @@ +use alloc::borrow::ToOwned; +use alloc::format; +use edge_http::io::server::Connection; +use edge_http::Method; +use embedded_io_async::{Read, Write}; +use log::info; +use crate::BOARD_ACCESS; +use crate::fat_error::FatError; + +pub(crate) async fn ota_operations( + conn: &mut Connection<'_, T, { N }>, + method: Method +) -> Result, FatError> +where + T: Read + Write, +{ + Ok(match method { + Method::Options => { + conn.initiate_response( + 200, + Some("OK"), + &[ + ("Access-Control-Allow-Origin", "*"), + ("Access-Control-Allow-Headers", "*"), + ("Access-Control-Allow-Methods", "*"), + ], + ) + .await?; + Some(200) + } + Method::Post => { + let mut offset = 0_usize; + let mut chunk = 0; + loop { + let mut buf = [0_u8; 1024]; + let to_write = conn.read(&mut buf).await?; + if to_write == 0 { + info!("file request for ota finished"); + let mut board = BOARD_ACCESS.get().await.lock().await; + board.board_hal.get_esp().finalize_ota().await?; + break; + } else { + let mut board = BOARD_ACCESS.get().await.lock().await; + board.board_hal.progress(chunk as u32).await; + board + .board_hal + .get_esp() + .write_ota(offset as u32, &buf[0..to_write]) + .await?; + } + offset = offset + to_write; + chunk = chunk + 1; + } + BOARD_ACCESS + .get() + .await + .lock() + .await + .board_hal + .clear_progress() + .await; + conn.initiate_response( + 200, + Some("OK"), + &[ + ("Access-Control-Allow-Origin", "*"), + ("Access-Control-Allow-Headers", "*"), + ("Access-Control-Allow-Methods", "*"), + ], + ) + .await?; + Some(200) + } + _ => None, + }) +}