diff --git a/Software/MainBoard/rust/src/hal/esp.rs b/Software/MainBoard/rust/src/hal/esp.rs index a619f41..71531c1 100644 --- a/Software/MainBoard/rust/src/hal/esp.rs +++ b/Software/MainBoard/rust/src/hal/esp.rs @@ -563,7 +563,7 @@ impl Esp<'_> { match self.savegame.load_slot(idx)? { None => bail!("Slot {idx} is empty or invalid"), Some(data) => { - Ok(String::from_utf8_lossy(&*data).to_string()) + Ok(String::from_utf8_lossy(&data).to_string()) } } } diff --git a/Software/MainBoard/rust/src/hal/savegame_manager.rs b/Software/MainBoard/rust/src/hal/savegame_manager.rs index 7f9479b..195d355 100644 --- a/Software/MainBoard/rust/src/hal/savegame_manager.rs +++ b/Software/MainBoard/rust/src/hal/savegame_manager.rs @@ -9,9 +9,10 @@ use crate::hal::shared_flash::MutexFlashStorage; /// Size of each save slot in bytes (16 KB). pub const SAVEGAME_SLOT_SIZE: usize = 16384; - +//keep a little of space at the end due to partition table offsets +const SAFETY: usize = 5; /// Number of slots in the 8 MB storage partition. -pub const SAVEGAME_SLOT_COUNT: usize = 8 * 1024 * 1024 / SAVEGAME_SLOT_SIZE; // 512 +pub const SAVEGAME_SLOT_COUNT: usize = (8 * 1024 * 1024) / SAVEGAME_SLOT_SIZE - SAFETY; // 507 /// Metadata about a single existing save slot, returned by [`SavegameManager::list_saves`]. #[derive(Serialize, Debug, Clone)] @@ -51,9 +52,15 @@ impl Flash for SavegameFlashAdapter<'_> { /// embedded-savegame calls this before writing to a slot, so we erase /// the entire `SAVEGAME_SLOT_SIZE` bytes so subsequent writes land on /// pre-erased (0xFF) pages. + /// Ensures addresses are aligned to ERASE_SIZE (4KB) boundaries. fn erase(&mut self, addr: u32) -> Result<(), Self::Error> { + const ERASE_SIZE: u32 = 4096; + // Align start address down to erase boundary + let aligned_start = (addr / ERASE_SIZE) * ERASE_SIZE; + // Align end address up to erase boundary let end = addr + SAVEGAME_SLOT_SIZE as u32; - NorFlash::erase(self.region, addr, end).map_err(SavegameFlashError) + let aligned_end = ((end + ERASE_SIZE - 1) / ERASE_SIZE) * ERASE_SIZE; + NorFlash::erase(self.region, aligned_start, aligned_end).map_err(SavegameFlashError) } } @@ -91,9 +98,10 @@ impl SavegameManager { /// /// `scan()` advances the internal wear-leveling pointer to the latest valid /// slot before `append()` writes to the next free one. + /// Both operations are performed atomically on the same Storage instance. pub fn save(&mut self, data: &mut [u8]) -> FatResult<()> { let mut st = self.storage(); - st.scan()?; + let _slot = st.scan()?; st.append(data)?; Ok(()) } diff --git a/Software/MainBoard/rust/src/hal/v4_hal.rs b/Software/MainBoard/rust/src/hal/v4_hal.rs index d4df758..fe7fcce 100644 --- a/Software/MainBoard/rust/src/hal/v4_hal.rs +++ b/Software/MainBoard/rust/src/hal/v4_hal.rs @@ -577,9 +577,9 @@ impl<'a> BoardInteraction<'a> for V4<'a> { start ); to_write -= part.len(); - chunk += 1; self.get_rtc_module() .write((BACKUP_HEADER_MAX_SIZE + chunk * EEPROM_PAGE) as u32, part)?; + chunk += 1; } info!("Backup complete"); self.clear_progress().await; @@ -591,13 +591,19 @@ impl<'a> BoardInteraction<'a> for V4<'a> { let mut store = alloc::vec![0_u8; info.size as usize]; self.rtc_module .read(BACKUP_HEADER_MAX_SIZE as u32, store.as_mut_slice())?; + info!("Read backup data of size {}", store.len()); let mut checksum = X25.digest(); + info!("Calculating CRC"); checksum.update(&store[..]); let crc = checksum.finalize(); + info!("CRC is {:04x}", crc); if crc != info.crc16 { + warn!("CRC mismatch in backup data"); bail!("CRC mismatch in backup data") } + info!("CRC is correct"); let (decoded, _) = bincode::decode_from_slice(&store[..], CONFIG)?; + info!("Backup data decoded"); Ok(decoded) } diff --git a/Software/MainBoard/rust/src/main.rs b/Software/MainBoard/rust/src/main.rs index 64374af..caeb907 100644 --- a/Software/MainBoard/rust/src/main.rs +++ b/Software/MainBoard/rust/src/main.rs @@ -443,7 +443,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { board.board_hal.get_esp().last_pump_time(plant_id); //state.active = true; - pump_info(&mut board, plant_id, true, pump_ineffective, 0, 0, 0, false).await; + pump_info(&mut board, plant_id, true, pump_ineffective, 0, 0, 0).await; let result = do_secure_pump(&mut board, plant_id, plant_config, dry_run).await?; //stop pump regardless of prior result//todo refactor to inner? @@ -455,8 +455,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { pump_ineffective, result.median_current_ma, result.max_current_ma, - result.min_current_ma, - result.error, + result.min_current_ma ) .await; } else if !state.pump_in_timeout(plant_config, &timezone_time) { @@ -908,8 +907,7 @@ async fn pump_info( pump_ineffective: bool, median_current_ma: u16, max_current_ma: u16, - min_current_ma: u16, - _error: bool, + min_current_ma: u16 ) { let pump_info = PumpInfo { enabled: pump_active,