From 4c54edbceafec8e0c97ddcb03a5c2f19f9691457 Mon Sep 17 00:00:00 2001 From: Empire Phoenix Date: Wed, 17 Sep 2025 01:36:53 +0200 Subject: [PATCH] littlefs2 impl stuff --- rust/Cargo.toml | 2 + rust/src/hal/LittleFS2StorageAdapter.rs | 63 ++++++++++++++++++++ rust/src/hal/esp.rs | 78 +++++++++---------------- rust/src/hal/mod.rs | 39 +++++++++++-- 4 files changed, 124 insertions(+), 58 deletions(-) create mode 100644 rust/src/hal/LittleFS2StorageAdapter.rs diff --git a/rust/Cargo.toml b/rust/Cargo.toml index b389b79..8a6cd59 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -159,6 +159,8 @@ edge-nal-embassy = "0.6.0" static_cell = "2.1.1" cfg-if = "1.0.3" edge-http = { version = "0.6.1", features = ["log"] } +littlefs2 = { version = "0.6.1", features = ["c-stubs"] } +littlefs2-core = "0.1.1" [patch.crates-io] diff --git a/rust/src/hal/LittleFS2StorageAdapter.rs b/rust/src/hal/LittleFS2StorageAdapter.rs new file mode 100644 index 0000000..fb03951 --- /dev/null +++ b/rust/src/hal/LittleFS2StorageAdapter.rs @@ -0,0 +1,63 @@ +use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use esp_bootloader_esp_idf::partitions::FlashRegion; +use esp_storage::FlashStorage; +use littlefs2::consts::U1 as lfs2Array1; +use littlefs2::consts::U512 as lfs2Array512; +use littlefs2::driver::Storage as lfs2Storage; +use littlefs2::fs::Filesystem as lfs2Filesystem; +use littlefs2::io::Error as lfs2Error; +use littlefs2::io::Result as lfs2Result; +use log::error; + +pub struct LittleFs2Filesystem { + pub(crate) storage: &'static mut FlashRegion<'static, FlashStorage>, +} + +impl lfs2Storage for LittleFs2Filesystem { + const READ_SIZE: usize = 512; + const WRITE_SIZE: usize = 512; + const BLOCK_SIZE: usize = 32 * 1024; //usually optimal for flash access + const BLOCK_COUNT: usize = 8 * 1024 * 1024 / 32 * 1024; //8mb in 32blocks + const BLOCK_CYCLES: isize = 0; + type CACHE_SIZE = lfs2Array512; + type LOOKAHEAD_SIZE = lfs2Array1; + + fn read(&mut self, off: usize, buf: &mut [u8]) -> lfs2Result { + let read_size: usize = Self::READ_SIZE; + assert_eq!(off % read_size, 0); + assert_eq!(buf.len() % read_size, 0); + match self.storage.read(off as u32, buf) { + anyhow::Result::Ok(..) => lfs2Result::Ok(buf.len()), + Err(err) => { + error!("Littlefs2Filesystem read error: {:?}", err); + Err(lfs2Error::IO) + } + } + } + + fn write(&mut self, off: usize, data: &[u8]) -> lfs2Result { + let write_size: usize = Self::WRITE_SIZE; + assert_eq!(off % write_size, 0); + assert_eq!(data.len() % write_size, 0); + match self.storage.write(off as u32, data) { + anyhow::Result::Ok(..) => lfs2Result::Ok(data.len()), + Err(err) => { + error!("Littlefs2Filesystem write error: {:?}", err); + Err(lfs2Error::IO) + } + } + } + + fn erase(&mut self, off: usize, len: usize) -> lfs2Result { + let block_size: usize = Self::BLOCK_SIZE; + debug_assert!(off % block_size == 0); + debug_assert!(len % block_size == 0); + match self.storage.erase(off as u32, len as u32) { + anyhow::Result::Ok(..) => lfs2Result::Ok(len), + Err(err) => { + error!("Littlefs2Filesystem erase error: {:?}", err); + Err(lfs2Error::IO) + } + } + } +} diff --git a/rust/src/hal/esp.rs b/rust/src/hal/esp.rs index 887f324..3279cc4 100644 --- a/rust/src/hal/esp.rs +++ b/rust/src/hal/esp.rs @@ -6,6 +6,7 @@ use anyhow::{anyhow, bail, Context}; use chrono::{DateTime, Utc}; use serde::Serialize; +use crate::hal::LittleFS2StorageAdapter::LittleFs2Filesystem; use alloc::string::ToString; use alloc::sync::Arc; use alloc::{format, string::String, vec::Vec}; @@ -32,6 +33,8 @@ use esp_wifi::wifi::{ AccessPointConfiguration, AccessPointInfo, Configuration, Interfaces, ScanConfig, ScanTypeConfig, WifiController, WifiDevice, WifiEvent, WifiState, }; +use littlefs2::fs::Filesystem; +use littlefs2_core::{FileType, PathBuf}; use log::{info, warn}; #[link_section = ".rtc.data"] @@ -54,8 +57,6 @@ pub struct FileList { total: usize, used: usize, files: Vec, - file_system_corrupt: Option, - iter_error: Option, } pub struct FileSystemSizeInfo { @@ -70,6 +71,7 @@ pub struct MqttClient<'a> { base_topic: heapless::String<64>, } pub struct Esp<'a> { + pub fs: Arc>>, pub rng: Rng, //first starter (ap or sta will take these) pub interfaces: Option>, @@ -432,60 +434,32 @@ impl Esp<'_> { // }) } - pub(crate) async fn list_files(&self) -> FileList { - return FileList { + pub(crate) async fn list_files(&self) -> anyhow::Result { + let path = PathBuf::new(); + + let mut result = FileList { total: 0, used: 0, - file_system_corrupt: None, files: Vec::new(), - iter_error: None, }; - // - // let storage = CString::new(Self::SPIFFS_PARTITION_NAME).unwrap(); - // - // let mut file_system_corrupt = None; - // - // let mut iter_error = None; - // let mut result = Vec::new(); - // - // let filepath = Path::new(Self::BASE_PATH); - // let read_dir = fs::read_dir(filepath); - // match read_dir { - // OkStd(read_dir) => { - // for item in read_dir { - // match item { - // OkStd(file) => { - // let f = FileInfo { - // filename: file.file_name().into_string().unwrap(), - // size: file.metadata().map(|it| it.len()).unwrap_or_default() - // as usize, - // }; - // result.push(f); - // } - // Err(err) => { - // iter_error = Some(format!("{err:?}")); - // break; - // } - // } - // } - // } - // Err(err) => { - // file_system_corrupt = Some(format!("{err:?}")); - // } - // } - // let mut total: usize = 0; - // let mut used: usize = 0; - // unsafe { - // esp_spiffs_info(storage.as_ptr(), &mut total, &mut used); - // } - // - // FileList { - // total, - // used, - // file_system_corrupt, - // files: result, - // iter_error, - // } + + match self.fs.lock().await.read_dir_and_then(&path, |dir| { + for entry in dir { + let e = entry?; + + result.files.push(FileInfo { + filename: e.path().to_string(), + size: e.metadata().len(), + }); + } + Result::Ok(()) + }) { + Ok(_) => {} + Err(err) => { + bail!(format!("{err:?}")) + } + } + Ok(result) } pub(crate) async fn delete_file(&self, _filename: &str) -> anyhow::Result<()> { bail!("todo"); diff --git a/rust/src/hal/mod.rs b/rust/src/hal/mod.rs index b53eade..091b62c 100644 --- a/rust/src/hal/mod.rs +++ b/rust/src/hal/mod.rs @@ -1,3 +1,4 @@ +mod LittleFS2StorageAdapter; pub(crate) mod battery; pub mod esp; mod initial_hal; @@ -22,7 +23,7 @@ use crate::{ use alloc::boxed::Box; use alloc::format; use alloc::sync::Arc; -use anyhow::{Ok, Result}; +use anyhow::{bail, Ok, Result}; use async_trait::async_trait; use embassy_executor::Spawner; //use battery::BQ34Z100G1; @@ -32,11 +33,13 @@ use esp_bootloader_esp_idf::partitions::{ AppPartitionSubType, DataPartitionSubType, FlashRegion, PartitionEntry, }; use esp_hal::clock::CpuClock; -use esp_hal::gpio::{Input, InputConfig, Io, Pull}; +use esp_hal::gpio::{Input, InputConfig, Pull}; use esp_println::println; use measurements::{Current, Voltage}; +use crate::hal::LittleFS2StorageAdapter::LittleFs2Filesystem; use embassy_sync::mutex::Mutex; +use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; use esp_alloc as _; use esp_backtrace as _; use esp_bootloader_esp_idf::ota::Slot; @@ -44,7 +47,8 @@ use esp_hal::rng::Rng; use esp_hal::timer::timg::TimerGroup; use esp_storage::FlashStorage; use esp_wifi::{init, EspWifiController}; -use littlefs2::fs::Filesystem; +use littlefs2::fs::{Allocation, Filesystem as lfs2Filesystem}; +use littlefs2::object_safe::DynStorage; //Only support for 8 right now! pub const PLANT_COUNT: usize = 8; @@ -275,6 +279,7 @@ impl PlantHal { }; let ota_next = mk_static!(PartitionEntry, ota_partition); + let storage_ota = mk_static!(FlashStorage, FlashStorage::new()); let ota_next = mk_static!( FlashRegion, ota_next.as_embedded_storage(storage_ota) @@ -285,12 +290,34 @@ impl PlantHal { DataPartitionSubType::LittleFs, ))? .unwrap(); + let data_partition = mk_static!(PartitionEntry, data_partition); - let mut data = data_partition.as_embedded_storage(storage_ota); - let mut alloc = Filesystem::allocate(); - let mut fs = Filesystem::mount(&mut alloc, &mut data).unwrap(); + let storage_data = mk_static!(FlashStorage, FlashStorage::new()); + let mut data = mk_static!( + FlashRegion, + data_partition.as_embedded_storage(storage_data) + ); + let lfs2filesystem = mk_static!(LittleFs2Filesystem, LittleFs2Filesystem { storage: data }); + let alloc = mk_static!(Allocation, lfs2Filesystem::allocate()); + if lfs2filesystem.is_mountable() { + log::info!("Littlefs2 filesystem is mountable"); + } else { + match lfs2filesystem.format() { + Result::Ok(..) => { + log::info!("Littlefs2 filesystem is formatted"); + } + Err(err) => { + bail!("Littlefs2 filesystem could not be formatted: {:?}", err); + } + } + } + + let fs = Arc::new(Mutex::new( + lfs2Filesystem::mount(alloc, lfs2filesystem).unwrap(), + )); let mut esp = Esp { + fs, rng, controller: Arc::new(Mutex::new(controller)), interfaces: Some(interfaces),