From 894be7c373f2a1d45c7f6c56801126a284384c77 Mon Sep 17 00:00:00 2001 From: Empire Phoenix Date: Sat, 4 Oct 2025 03:05:11 +0200 Subject: [PATCH] fix some ota stuff --- rust/src/hal/esp.rs | 66 ++++++++++-------------------- rust/src/hal/mod.rs | 34 +++++++-------- rust/src/main.rs | 1 + rust/src/webserver/file_manager.rs | 10 ++--- rust/src/webserver/mod.rs | 21 ++++++---- rust/src/webserver/ota.rs | 25 +++++------ 6 files changed, 66 insertions(+), 91 deletions(-) diff --git a/rust/src/hal/esp.rs b/rust/src/hal/esp.rs index 373204f..99dc322 100644 --- a/rust/src/hal/esp.rs +++ b/rust/src/hal/esp.rs @@ -1,4 +1,4 @@ -use crate::{bail}; +use crate::bail; use crate::config::{NetworkConfig, PlantControllerConfig}; use crate::hal::{get_next_slot, PLANT_COUNT, TIME_ACCESS}; use crate::log::{LogMessage, LOG_ACCESS}; @@ -9,7 +9,7 @@ use crate::fat_error::{ContextExt, FatError, FatResult}; use crate::hal::little_fs2storage_adapter::LittleFs2Filesystem; use alloc::string::ToString; use alloc::sync::Arc; -use alloc::{format, string::String, vec::Vec}; +use alloc::{format, string::String, vec, vec::Vec}; use core::net::{IpAddr, Ipv4Addr, SocketAddr}; use core::str::FromStr; use core::sync::atomic::Ordering; @@ -21,8 +21,8 @@ use embassy_sync::mutex::{Mutex, MutexGuard}; use embassy_sync::once_lock::OnceLock; use embassy_time::{Duration, Timer}; use embedded_storage::nor_flash::{check_erase, NorFlash, ReadNorFlash}; -use esp_bootloader_esp_idf::ota::{Ota, OtaImageState}; use esp_bootloader_esp_idf::ota::OtaImageState::Valid; +use esp_bootloader_esp_idf::ota::{Ota, OtaImageState}; use esp_bootloader_esp_idf::partitions::FlashRegion; use esp_hal::gpio::{Input, RtcPinWithResistors}; use esp_hal::rng::Rng; @@ -128,7 +128,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 @@ -235,53 +235,32 @@ impl Esp<'_> { } } - pub(crate) async fn write_ota( - &mut self, - offset: u32, - buf: &[u8], - ) -> Result<(), FatError> { + 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 mut read_back = [0_u8; 1024]; - let useful = &mut read_back[..buf.len()]; + 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, useful)?; - if buf != useful { - info!("Expected {:?} but got {:?}", buf, useful); - bail!("Flash error, read back does not match write buffer at offset {:x}", offset) + self.ota_next.read(offset, read_back)?; + if buf != read_back { + info!("Expected {:?} but got {:?}", buf, read_back); + bail!( + "Flash error, read back does not match write buffer at offset {:x}", + offset + ) } Ok(()) } - pub(crate) async fn ota_erase(&mut self, block_start: u32) -> FatResult<()> { - // Ensure 4K block size and alignment - if self.ota_next.capacity() % 4096 != 0 { - bail!("Partition size is not a multiple of 4096") - } - if block_start % 4096 != 0 { - bail!("ota_erase called with unaligned block_start: {:x}", block_start) - } - let capacity = self.ota_next.capacity() as u32; - if block_start >= capacity { - bail!("ota_erase block_start out of range: {:x}", block_start) - } - let end = core::cmp::min(block_start + 4096, capacity); - // Check current erase state (will error if not erased); we ignore the result and erase anyway - let _ = check_erase(self.ota_next, block_start, end); - info!("erasing block {:x}-{:x}", block_start, end); - self.ota_next.erase(block_start, end)?; - Ok(()) - } - - pub(crate) async fn finalize_ota( - &mut self, - ) -> Result<(), FatError> { + 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") } @@ -289,20 +268,19 @@ impl Esp<'_> { bail!("Invalid OTA state, refusing ota write") } - let current_state = self.ota.current_ota_state()?; - info!("current state {:?}", current_state); + info!("current state {:?}", current_state); let next_slot = get_next_slot(&mut self.ota)?; - info!("current slot {:?}", next_slot.next()); + info!("current slot {:?}", next_slot.next()); if current_state == OtaImageState::PendingVerify { info!("verifying ota image from pending"); self.ota.set_current_ota_state(Valid)?; } self.ota.set_current_slot(next_slot)?; - info!("switched slot"); + info!("switched slot"); self.ota.set_current_ota_state(OtaImageState::New)?; - info!("switched state for new partition"); + 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 @@ -344,6 +322,7 @@ impl Esp<'_> { if ntp_addrs.is_empty() { bail!("Failed to resolve DNS"); } + info!("NTP server: {:?}", ntp_addrs); let mut counter = 0; loop { @@ -368,7 +347,6 @@ impl Esp<'_> { } } - pub(crate) async fn wifi_scan(&mut self) -> FatResult> { info!("start wifi scan"); let mut lock = self.controller.try_lock()?; diff --git a/rust/src/hal/mod.rs b/rust/src/hal/mod.rs index 712bb36..e3b3a44 100644 --- a/rust/src/hal/mod.rs +++ b/rust/src/hal/mod.rs @@ -69,7 +69,9 @@ 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, FlashRegion, PartitionEntry, +}; use esp_hal::clock::CpuClock; use esp_hal::gpio::{Input, InputConfig, Pull}; use measurements::{Current, Voltage}; @@ -318,13 +320,12 @@ impl PlantHal { ota_data.as_embedded_storage(storage_ota) ); - let mut ota = esp_bootloader_esp_idf::ota::Ota::new(ota_data)?; + let mut ota = Ota::new(ota_data)?; 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 { Slot::None => { panic!("No OTA slot active?"); @@ -332,12 +333,13 @@ impl PlantHal { Slot::Slot0 => pt .find_partition(esp_bootloader_esp_idf::partitions::PartitionType::App( AppPartitionSubType::Ota0, - ))?.context("Partition table invalid no ota0")?, + ))? + .context("Partition table invalid no ota0")?, Slot::Slot1 => pt .find_partition(esp_bootloader_esp_idf::partitions::PartitionType::App( AppPartitionSubType::Ota1, - ))?.context("Partition table invalid no ota1")? - , + ))? + .context("Partition table invalid no ota1")?, }; let ota_next = mk_static!(PartitionEntry, ota_next); @@ -389,7 +391,7 @@ impl PlantHal { boot_button, wake_gpio1, ota, - ota_next + ota_next, }; //init,reset rtc memory depending on cause @@ -569,24 +571,16 @@ impl PlantHal { Ok(Mutex::new(hal)) } - } - fn get_next_slot(ota: &mut Ota) -> Result { let next_slot = { let state = ota.current_ota_state().unwrap_or_default(); let current = ota.current_slot()?; match state { - OtaImageState::New => { - current.next() - } - OtaImageState::PendingVerify => { - current.next() - } - OtaImageState::Valid => { - current.next() - } + OtaImageState::New => current.next(), + OtaImageState::PendingVerify => current.next(), + OtaImageState::Valid => current.next(), OtaImageState::Invalid => { //we actually booted other slot, than partition table assumes current @@ -623,4 +617,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/main.rs b/rust/src/main.rs index e932f8d..ff2e065 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -1096,6 +1096,7 @@ 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 { diff --git a/rust/src/webserver/file_manager.rs b/rust/src/webserver/file_manager.rs index ad9d1ce..acc6c1b 100644 --- a/rust/src/webserver/file_manager.rs +++ b/rust/src/webserver/file_manager.rs @@ -1,4 +1,5 @@ use crate::fat_error::{FatError, FatResult}; +use crate::webserver::read_up_to_bytes_from_request; use crate::BOARD_ACCESS; use alloc::borrow::ToOwned; use alloc::format; @@ -118,9 +119,8 @@ where let mut offset = 0_usize; let mut chunk = 0; loop { - let mut buf = [0_u8; 4096]; - let to_write = conn.read(&mut buf).await?; - if to_write == 0 { + let buf = read_up_to_bytes_from_request(conn, Some(4096)).await?; + if buf.len() == 0 { info!("file request for {} finished", filename); break; } else { @@ -129,10 +129,10 @@ where board .board_hal .get_esp() - .write_file(filename.to_owned(), offset as u32, &buf[0..to_write]) + .write_file(filename.to_owned(), offset as u32, &buf) .await?; } - offset = offset + to_write; + offset = offset + buf.len(); chunk = chunk + 1; } BOARD_ACCESS diff --git a/rust/src/webserver/mod.rs b/rust/src/webserver/mod.rs index 6009be7..3a9eb7b 100644 --- a/rust/src/webserver/mod.rs +++ b/rust/src/webserver/mod.rs @@ -5,8 +5,8 @@ mod file_manager; mod get_json; mod get_log; mod get_static; -mod post_json; mod ota; +mod post_json; use crate::fat_error::{FatError, FatResult}; use crate::webserver::backup_manager::{backup_config, backup_info, get_backup_config}; @@ -17,6 +17,7 @@ use crate::webserver::get_json::{ }; use crate::webserver::get_log::get_log; use crate::webserver::get_static::{serve_bundle, serve_favicon, serve_index}; +use crate::webserver::ota::ota_operations; use crate::webserver::post_json::{ board_test, night_lamp_test, pump_test, set_config, wifi_scan, write_time, }; @@ -37,7 +38,6 @@ use embassy_net::Stack; use embassy_time::Instant; use embedded_io_async::{Read, Write}; use log::{error, info}; -use crate::webserver::ota::ota_operations; // fn ota( // request: &mut Request<&mut EspHttpConnection>, // ) -> Result, anyhow::Error> { @@ -109,11 +109,10 @@ impl Handler for HTTPRequestRouter { let status = if path.starts_with(prefix) { file_operations(conn, method, &path, &prefix).await? } else if path == "/ota" { - ota_operations(conn,method).await.map_err(|e| { + ota_operations(conn, method).await.map_err(|e| { error!("Error handling ota: {}", e); - e - } - )? + e + })? } else { match method { Method::Get => match path { @@ -202,12 +201,18 @@ where let mut data_store = Vec::new(); let mut total_read = 0; loop { + let left = max_read - total_read; let mut buf = [0_u8; 64]; - let read = request.read(&mut buf).await?; + let s_buf = if buf.len() <= left { + &mut buf + } else { + &mut buf[0..left] + }; + let read = request.read(s_buf).await?; if read == 0 { break; } - let actual_data = &buf[0..read]; + let actual_data = &s_buf[0..read]; total_read += read; if total_read > max_read { bail!("Request too large {total_read} > {max_read}"); diff --git a/rust/src/webserver/ota.rs b/rust/src/webserver/ota.rs index 09f0cd5..8c63935 100644 --- a/rust/src/webserver/ota.rs +++ b/rust/src/webserver/ota.rs @@ -1,13 +1,14 @@ +use crate::fat_error::FatError; +use crate::webserver::read_up_to_bytes_from_request; +use crate::BOARD_ACCESS; 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 + method: Method, ) -> Result, FatError> where T: Read + Write, @@ -23,7 +24,7 @@ where ("Access-Control-Allow-Methods", "*"), ], ) - .await?; + .await?; Some(200) } Method::Post => { @@ -33,9 +34,8 @@ where // Erase only a single 4K block right before writing into it. // The first block will be erased when offset == 0 below. loop { - let mut buf = [0_u8; 1024]; - let to_write = conn.read(&mut buf).await?; - if to_write == 0 { + let buf = read_up_to_bytes_from_request(conn, Some(4096)).await?; + if buf.len() == 0 { info!("file request for ota finished"); let mut board = BOARD_ACCESS.get().await.lock().await; board.board_hal.get_esp().finalize_ota().await?; @@ -44,17 +44,14 @@ where let mut board = BOARD_ACCESS.get().await.lock().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) - if offset % 4096 >= offset+to_write % 4096 { - info!("erasing block {} during write between {} with size {}", offset / 4096, offset, to_write); - board.board_hal.get_esp().ota_erase(offset as u32).await?; - } + info!("erasing and writing block 0x{offset:x}"); board .board_hal .get_esp() - .write_ota(offset as u32, &buf[0..to_write]) + .write_ota(offset as u32, &*buf) .await?; } - offset = offset + to_write; + offset = offset + buf.len(); chunk = chunk + 1; } BOARD_ACCESS @@ -74,7 +71,7 @@ where ("Access-Control-Allow-Methods", "*"), ], ) - .await?; + .await?; Some(200) } _ => None,