shared flash test

This commit is contained in:
2025-11-02 02:30:21 +01:00
parent 0519ca3efe
commit 8cd9e08e93
6 changed files with 117 additions and 28 deletions

View File

@@ -7,6 +7,7 @@ use serde::Serialize;
use crate::fat_error::{ContextExt, FatError, FatResult}; use crate::fat_error::{ContextExt, FatError, FatResult};
use crate::hal::little_fs2storage_adapter::LittleFs2Filesystem; use crate::hal::little_fs2storage_adapter::LittleFs2Filesystem;
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::Vec};
@@ -36,7 +37,6 @@ use esp_radio::wifi::{
AccessPointConfig, AccessPointInfo, AuthMethod, ClientConfig, ModeConfig, ScanConfig, AccessPointConfig, AccessPointInfo, AuthMethod, ClientConfig, ModeConfig, ScanConfig,
ScanTypeConfig, WifiController, WifiDevice, WifiStaState, ScanTypeConfig, WifiController, WifiDevice, WifiStaState,
}; };
use esp_storage::FlashStorage;
use littlefs2::fs::Filesystem; use littlefs2::fs::Filesystem;
use littlefs2_core::{FileType, PathBuf, SeekFrom}; use littlefs2_core::{FileType, PathBuf, SeekFrom};
use log::{info, warn}; use log::{info, warn};
@@ -127,8 +127,8 @@ 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 ota: Ota<'static, FlashStorage<'static>>, pub ota: Ota<'static, MutexFlashStorage>,
pub ota_target: &'static mut FlashRegion<'static, FlashStorage<'static>>, pub ota_target: &'static mut FlashRegion<'static, MutexFlashStorage>,
pub current: AppPartitionSubType, pub current: AppPartitionSubType,
pub slot0_state: OtaImageState, pub slot0_state: OtaImageState,
pub slot1_state: OtaImageState, pub slot1_state: OtaImageState,

View File

@@ -1,6 +1,6 @@
use crate::hal::shared_flash::MutexFlashStorage;
use embedded_storage::nor_flash::{check_erase, NorFlash, ReadNorFlash}; use embedded_storage::nor_flash::{check_erase, NorFlash, ReadNorFlash};
use esp_bootloader_esp_idf::partitions::FlashRegion; use esp_bootloader_esp_idf::partitions::FlashRegion;
use esp_storage::FlashStorage;
use littlefs2::consts::U4096 as lfsCache; use littlefs2::consts::U4096 as lfsCache;
use littlefs2::consts::U512 as lfsLookahead; use littlefs2::consts::U512 as lfsLookahead;
use littlefs2::driver::Storage as lfs2Storage; use littlefs2::driver::Storage as lfs2Storage;
@@ -9,7 +9,7 @@ use littlefs2::io::Result as lfs2Result;
use log::error; use log::error;
pub struct LittleFs2Filesystem { pub struct LittleFs2Filesystem {
pub(crate) storage: &'static mut FlashRegion<'static, FlashStorage<'static>>, pub(crate) storage: &'static mut FlashRegion<'static, MutexFlashStorage>,
} }
impl lfs2Storage for LittleFs2Filesystem { impl lfs2Storage for LittleFs2Filesystem {

View File

@@ -4,11 +4,13 @@ pub mod esp;
mod initial_hal; mod initial_hal;
mod little_fs2storage_adapter; mod little_fs2storage_adapter;
pub(crate) mod rtc; pub(crate) mod rtc;
mod shared_flash;
mod v3_hal; mod v3_hal;
mod v3_shift_register; mod v3_shift_register;
mod v4_hal; mod v4_hal;
pub(crate) mod v4_sensor; pub(crate) mod v4_sensor;
mod water; mod water;
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;
@@ -79,7 +81,7 @@ 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::nor_flash::ReadNorFlash; 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_bootloader_esp_idf::ota::{Ota, OtaImageState};
@@ -101,6 +103,7 @@ use littlefs2::object_safe::DynStorage;
use log::{error, info, warn}; use log::{error, info, warn};
use portable_atomic::AtomicBool; use portable_atomic::AtomicBool;
use serde::Serialize; use serde::Serialize;
use shared_flash::MutexFlashStorage;
pub static TIME_ACCESS: OnceLock<Mutex<CriticalSectionRawMutex, Rtc>> = OnceLock::new(); pub static TIME_ACCESS: OnceLock<Mutex<CriticalSectionRawMutex, Rtc>> = OnceLock::new();
@@ -306,7 +309,15 @@ impl PlantHal {
[u8; esp_bootloader_esp_idf::partitions::PARTITION_TABLE_MAX_LEN], [u8; esp_bootloader_esp_idf::partitions::PARTITION_TABLE_MAX_LEN],
[0u8; esp_bootloader_esp_idf::partitions::PARTITION_TABLE_MAX_LEN] [0u8; esp_bootloader_esp_idf::partitions::PARTITION_TABLE_MAX_LEN]
); );
let flash_storage = mk_static!(FlashStorage, FlashStorage::new(peripherals.FLASH));
let bullshit = MutexFlashStorage {
inner: Arc::new(CriticalSectionMutex::new(RefCell::new(FlashStorage::new(
peripherals.FLASH,
)))),
};
let flash_storage = mk_static!(MutexFlashStorage, bullshit.clone());
let flash_storage_2 = mk_static!(MutexFlashStorage, bullshit.clone());
let flash_storage_3 = mk_static!(MutexFlashStorage, bullshit.clone());
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)?;
@@ -320,8 +331,8 @@ impl PlantHal {
); );
let ota_data = mk_static!( let ota_data = mk_static!(
FlashRegion<FlashStorage>, FlashRegion<MutexFlashStorage>,
ota_data.as_embedded_storage(flash_storage) ota_data.as_embedded_storage(flash_storage_2)
); );
let state_0 = ota_state(AppPartitionSubType::Ota0, ota_data); let state_0 = ota_state(AppPartitionSubType::Ota0, ota_data);
@@ -353,7 +364,7 @@ impl PlantHal {
let ota_target = mk_static!(PartitionEntry, ota_target); let ota_target = mk_static!(PartitionEntry, ota_target);
let ota_target = mk_static!( let ota_target = mk_static!(
FlashRegion<FlashStorage>, FlashRegion<MutexFlashStorage>,
ota_target.as_embedded_storage(flash_storage) ota_target.as_embedded_storage(flash_storage)
); );
@@ -365,8 +376,8 @@ impl PlantHal {
let data_partition = mk_static!(PartitionEntry, data_partition); let data_partition = mk_static!(PartitionEntry, data_partition);
let data = mk_static!( let data = mk_static!(
FlashRegion<FlashStorage>, FlashRegion<MutexFlashStorage>,
data_partition.as_embedded_storage(flash_storage) data_partition.as_embedded_storage(flash_storage_3)
); );
let lfs2filesystem = mk_static!(LittleFs2Filesystem, LittleFs2Filesystem { storage: data }); let lfs2filesystem = mk_static!(LittleFs2Filesystem, LittleFs2Filesystem { storage: data });
let alloc = mk_static!(Allocation<LittleFs2Filesystem>, lfs2Filesystem::allocate()); let alloc = mk_static!(Allocation<LittleFs2Filesystem>, lfs2Filesystem::allocate());
@@ -584,15 +595,18 @@ impl PlantHal {
} }
} }
fn ota_state(slot: AppPartitionSubType, ota_data: &mut FlashRegion<FlashStorage>) -> OtaImageState { fn ota_state(
slot: AppPartitionSubType,
ota_data: &mut FlashRegion<MutexFlashStorage>,
) -> OtaImageState {
// Read and log OTA states for both slots before constructing Ota // 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] // 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 // Offsets within the OTA data partition: slot0 @ 0x0000, slot1 @ 0x1000
let mut slot_buf = [0u8; 32]; let mut slot_buf = [0u8; 32];
if slot == AppPartitionSubType::Ota0 { if slot == AppPartitionSubType::Ota0 {
let _ = ota_data.read(0x0000, &mut slot_buf); let _ = ReadStorage::read(ota_data, 0x0000, &mut slot_buf);
} else { } else {
let _ = ota_data.read(0x1000, &mut slot_buf); 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])); let raw_state = u32::from_le_bytes(slot_buf[24..28].try_into().unwrap_or([0xff; 4]));
@@ -600,7 +614,7 @@ fn ota_state(slot: AppPartitionSubType, ota_data: &mut FlashRegion<FlashStorage>
} }
fn get_current_slot_and_fix_ota_data( fn get_current_slot_and_fix_ota_data(
ota: &mut Ota<FlashStorage>, ota: &mut Ota<MutexFlashStorage>,
state0: OtaImageState, state0: OtaImageState,
state1: OtaImageState, state1: OtaImageState,
) -> Result<AppPartitionSubType, FatError> { ) -> Result<AppPartitionSubType, FatError> {

View File

@@ -0,0 +1,63 @@
use alloc::sync::Arc;
use core::cell::RefCell;
use core::ops::{Deref, DerefMut};
use embassy_sync::blocking_mutex::CriticalSectionMutex;
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
use embedded_storage::ReadStorage;
use esp_storage::{FlashStorage, FlashStorageError};
#[derive(Clone)]
pub struct MutexFlashStorage {
pub(crate) inner: Arc<CriticalSectionMutex<RefCell<FlashStorage<'static>>>>,
}
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()))
}
}
impl embedded_storage::Storage for MutexFlashStorage {
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
NorFlash::write(self, offset, bytes)
}
}
impl ErrorType for MutexFlashStorage {
type Error = FlashStorageError;
}
impl ReadNorFlash for MutexFlashStorage {
const READ_SIZE: usize = 0;
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
ReadStorage::read(self, offset, bytes)
}
fn capacity(&self) -> usize {
ReadStorage::capacity(self)
}
}
impl NorFlash for MutexFlashStorage {
const WRITE_SIZE: usize = 0;
const ERASE_SIZE: usize = 0;
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
self.inner
.lock(|f| NorFlash::erase(f.borrow_mut().deref_mut(), from, to))
}
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
self.inner
.lock(|f| NorFlash::write(f.borrow_mut().deref_mut(), offset, bytes))
}
}

View File

@@ -101,8 +101,9 @@ impl SensorInteraction for SensorImpl {
Timer::after_millis(10).await; Timer::after_millis(10).await;
let mut moistures = Moistures::default(); let mut moistures = Moistures::default();
let _ = Self::wait_for_can_measurements(&mut twai, &mut moistures).with_timeout(Duration::from_millis(5000)).await; let _ = Self::wait_for_can_measurements(&mut twai, &mut moistures)
.with_timeout(Duration::from_millis(5000))
.await;
can_power.set_low(); can_power.set_low();
@@ -182,7 +183,6 @@ impl SensorImpl {
as_async: &mut Twai<'_, Async>, as_async: &mut Twai<'_, Async>,
moistures: &mut Moistures, moistures: &mut Moistures,
) -> FatResult<()> { ) -> FatResult<()> {
loop { loop {
match as_async.receive_async().await { match as_async.receive_async().await {
Ok(can_frame) => match can_frame.id() { Ok(can_frame) => match can_frame.id() {

View File

@@ -120,8 +120,12 @@ impl PlantState {
let raw = moistures.sensor_a_hz[plant_id]; let raw = moistures.sensor_a_hz[plant_id];
match map_range_moisture( match map_range_moisture(
raw, raw,
board.board_hal.get_config().plants[plant_id].moisture_sensor_min_frequency.map(|a| a as f32), board.board_hal.get_config().plants[plant_id]
board.board_hal.get_config().plants[plant_id].moisture_sensor_max_frequency.map(|b| b as f32), .moisture_sensor_min_frequency
.map(|a| a as f32),
board.board_hal.get_config().plants[plant_id]
.moisture_sensor_max_frequency
.map(|b| b as f32),
) { ) {
Ok(moisture_percent) => MoistureSensorState::MoistureValue { Ok(moisture_percent) => MoistureSensorState::MoistureValue {
raw_hz: raw, raw_hz: raw,
@@ -137,8 +141,12 @@ impl PlantState {
let raw = moistures.sensor_b_hz[plant_id]; let raw = moistures.sensor_b_hz[plant_id];
match map_range_moisture( match map_range_moisture(
raw, raw,
board.board_hal.get_config().plants[plant_id].moisture_sensor_min_frequency.map(|a| a as f32), board.board_hal.get_config().plants[plant_id]
board.board_hal.get_config().plants[plant_id].moisture_sensor_max_frequency.map(|b| b as f32) .moisture_sensor_min_frequency
.map(|a| a as f32),
board.board_hal.get_config().plants[plant_id]
.moisture_sensor_max_frequency
.map(|b| b as f32),
) { ) {
Ok(moisture_percent) => MoistureSensorState::MoistureValue { Ok(moisture_percent) => MoistureSensorState::MoistureValue {
raw_hz: raw, raw_hz: raw,
@@ -196,8 +204,12 @@ impl PlantState {
(Some(moisture_a), Some(moisture_b)) => { (Some(moisture_a), Some(moisture_b)) => {
(Some(((moisture_a + moisture_b) / 2.) as u8), (None, None)) (Some(((moisture_a + moisture_b) / 2.) as u8), (None, None))
} }
(Some(moisture_percent), _) => (Some(moisture_percent as u8), (None, self.sensor_b.is_err())), (Some(moisture_percent), _) => {
(_, Some(moisture_percent)) => (Some(moisture_percent as u8), (self.sensor_a.is_err(), None)), (Some(moisture_percent as u8), (None, self.sensor_b.is_err()))
}
(_, Some(moisture_percent)) => {
(Some(moisture_percent as u8), (self.sensor_a.is_err(), None))
}
_ => (None, (self.sensor_a.is_err(), self.sensor_b.is_err())), _ => (None, (self.sensor_a.is_err(), self.sensor_b.is_err())),
} }
} }