set read config initial somewhat ready

This commit is contained in:
2025-09-17 03:50:21 +02:00
parent 4c54edbcea
commit cd63e76469
9 changed files with 164 additions and 156 deletions

View File

@@ -1,4 +1,4 @@
use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
use embedded_storage::{ReadStorage, Storage};
use esp_bootloader_esp_idf::partitions::FlashRegion;
use esp_storage::FlashStorage;
use littlefs2::consts::U1 as lfs2Array1;
@@ -7,7 +7,7 @@ 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;
use log::{error, info};
pub struct LittleFs2Filesystem {
pub(crate) storage: &'static mut FlashRegion<'static, FlashStorage>,
@@ -16,13 +16,18 @@ pub struct LittleFs2Filesystem {
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;
const BLOCK_SIZE: usize = 1024; //usually optimal for flash access
const BLOCK_COUNT: usize = 8 * 1024 * 1024 / 1024; //8mb in 32kb blocks
const BLOCK_CYCLES: isize = 100;
type CACHE_SIZE = lfs2Array512;
type LOOKAHEAD_SIZE = lfs2Array1;
fn read(&mut self, off: usize, buf: &mut [u8]) -> lfs2Result<usize> {
info!(
"Littlefs2Filesystem read at offset {} with len {}",
off,
buf.len()
);
let read_size: usize = Self::READ_SIZE;
assert_eq!(off % read_size, 0);
assert_eq!(buf.len() % read_size, 0);
@@ -36,6 +41,11 @@ impl lfs2Storage for LittleFs2Filesystem {
}
fn write(&mut self, off: usize, data: &[u8]) -> lfs2Result<usize> {
info!(
"Littlefs2Filesystem write at offset {} with len {}",
off,
data.len()
);
let write_size: usize = Self::WRITE_SIZE;
assert_eq!(off % write_size, 0);
assert_eq!(data.len() % write_size, 0);
@@ -49,15 +59,20 @@ impl lfs2Storage for LittleFs2Filesystem {
}
fn erase(&mut self, off: usize, len: usize) -> lfs2Result<usize> {
info!(
"Littlefs2Filesystem erase at offset {} with len {}",
off, len
);
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)
}
}
//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)
// }
//}
lfs2Result::Ok(len)
}
}

View File

@@ -46,6 +46,8 @@ static mut LOW_VOLTAGE_DETECTED: bool = false;
#[link_section = ".rtc.data"]
static mut RESTART_TO_CONF: bool = false;
static CONFIG_FILE: &str = "config.json";
#[derive(Serialize, Debug)]
pub struct FileInfo {
filename: String,
@@ -87,6 +89,14 @@ pub struct Esp<'a> {
pub ota_next: &'static mut FlashRegion<'static, FlashStorage>,
}
// SAFETY: On this target we never move Esp across OS threads; the firmware runs single-core
// cooperative tasks with Embassy. All interior mutability of non-Send peripherals is gated
// behind &mut self or embassy_sync Mutex with CriticalSectionRawMutex, which does not rely on
// thread scheduling. Therefore it is sound to mark Esp as Send to satisfy trait object bounds
// (e.g., Box<dyn BoardInteraction + Send>). If you add fields that are accessed from multiple
// CPU cores/threads, reconsider this.
unsafe impl Send for Esp<'_> {}
pub struct IpInfo {
pub(crate) ip: IpAddr,
netmask: IpAddr,
@@ -224,7 +234,7 @@ impl Esp<'_> {
}
pub(crate) async fn wifi_ap(&mut self) -> anyhow::Result<Stack<'static>> {
let ssid = match self.load_config() {
let ssid = match self.load_config().await {
Ok(config) => config.network.ap_ssid.as_str().to_string(),
Err(_) => "PlantCtrl Emergency Mode".to_string(),
};
@@ -370,50 +380,29 @@ impl Esp<'_> {
// log(LogMessage::WifiInfo, 0, 0, "", &format!("{address:?}"));
// anyhow::Ok(address)
}
pub(crate) fn load_config(&mut self) -> anyhow::Result<PlantControllerConfig> {
bail!("todo");
// let cfg = File::open(Self::CONFIG_FILE)?;
// let config: PlantControllerConfig = serde_json::from_reader(cfg)?;
// anyhow::Ok(config)
pub(crate) async fn load_config(&mut self) -> anyhow::Result<PlantControllerConfig> {
let cfg = PathBuf::try_from(CONFIG_FILE).unwrap();
match self.fs.lock().await.read::<4096>(&cfg) {
Ok(data) => {
let config: PlantControllerConfig = serde_json::from_slice(&data)?;
anyhow::Ok(config)
}
Err(err) => {
bail!(format!("{err:?}"))
}
}
}
pub(crate) async fn save_config(
&mut self,
_config: &PlantControllerConfig,
) -> anyhow::Result<()> {
bail!("todo");
// let mut cfg = File::create(Self::CONFIG_FILE)?;
// serde_json::to_writer(&mut cfg, &config)?;
// log::info!("Wrote config config {:?}", config);
// anyhow::Ok(())
}
pub(crate) fn mount_file_system(&mut self) -> anyhow::Result<()> {
bail!("fail");
// log(LogMessage::MountingFilesystem, 0, 0, "", "");
// let base_path = String::try_from("/spiffs")?;
// let storage = String::try_from(Self::SPIFFS_PARTITION_NAME)?;
//let conf = todo!();
pub(crate) async fn save_config(&mut self, config: Vec<u8>) -> anyhow::Result<()> {
let filesystem = self.fs.lock().await;
let cfg = PathBuf::try_from(CONFIG_FILE).unwrap();
//let conf = esp_idf_sys::esp_vfs_spiffs_conf_t {
//base_path: base_path.as_ptr(),
//partition_label: storage.as_ptr(),
//max_files: 5,
//format_if_mount_failed: true,
//};
//TODO
//unsafe {
//esp_idf_sys::esp!(esp_idf_sys::esp_vfs_spiffs_register(&conf))?;
//}
// let free_space = self.file_system_size()?;
// log(
// LogMessage::FilesystemMount,
// free_space.free_size as u32,
// free_space.total_size as u32,
// &free_space.used_size.to_string(),
// "",
// );
// anyhow::Ok(())
match filesystem.write(&cfg, &*config) {
Ok(_) => {}
Err(err) => {
bail!(format!("{err:?}"))
}
}
anyhow::Ok(())
}
async fn file_system_size(&mut self) -> anyhow::Result<FileSystemSizeInfo> {
bail!("fail");

View File

@@ -125,11 +125,8 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
bail!("Please configure board revision")
}
async fn set_config(&mut self, config: PlantControllerConfig) -> anyhow::Result<()> {
fn set_config(&mut self, config: PlantControllerConfig) {
self.config = config;
//TODO
// self.esp.save_config(&self.config)?;
anyhow::Ok(())
}
async fn get_mptt_voltage(&mut self) -> Result<Voltage> {

View File

@@ -49,6 +49,7 @@ use esp_storage::FlashStorage;
use esp_wifi::{init, EspWifiController};
use littlefs2::fs::{Allocation, Filesystem as lfs2Filesystem};
use littlefs2::object_safe::DynStorage;
use log::{info, warn};
//Only support for 8 right now!
pub const PLANT_COUNT: usize = 8;
@@ -88,7 +89,7 @@ pub trait BoardInteraction<'a> {
async fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result<f32>;
async fn general_fault(&mut self, enable: bool);
async fn test(&mut self) -> Result<()>;
async fn set_config(&mut self, config: PlantControllerConfig) -> Result<()>;
fn set_config(&mut self, config: PlantControllerConfig);
async fn get_mptt_voltage(&mut self) -> anyhow::Result<Voltage>;
async fn get_mptt_current(&mut self) -> anyhow::Result<Current>;
}
@@ -99,7 +100,12 @@ impl dyn BoardInteraction<'_> {
let even = counter % 2 == 0;
let current = counter / (PLANT_COUNT as u32);
for led in 0..PLANT_COUNT {
self.fault(led, current == led as u32).await.unwrap();
match self.fault(led, current == led as u32).await {
Result::Ok(_) => {}
Err(err) => {
warn!("Fault on plant {}: {:?}", led, err);
}
}
}
let _ = self.general_fault(even.into());
}
@@ -187,11 +193,11 @@ impl PlantHal {
let timg0 = TimerGroup::new(peripherals.TIMG0);
let esp_wifi_ctrl = &*mk_static!(
EspWifiController<'static>,
init(timg0.timer0, rng.clone()).unwrap()
init(timg0.timer0, rng.clone()).expect("Could not init wifi controller")
);
let (controller, interfaces) =
esp_wifi::wifi::new(&esp_wifi_ctrl, peripherals.WIFI).unwrap();
esp_wifi::wifi::new(&esp_wifi_ctrl, peripherals.WIFI).expect("Could not init wifi");
use esp_hal::timer::systimer::SystemTimer;
esp_hal_embassy::init(systimer.alarm0);
@@ -234,11 +240,9 @@ impl PlantHal {
};
//
let tablebuffer: [u8; esp_bootloader_esp_idf::partitions::PARTITION_TABLE_MAX_LEN] =
[0u8; esp_bootloader_esp_idf::partitions::PARTITION_TABLE_MAX_LEN];
let tablebuffer = mk_static!(
[u8; esp_bootloader_esp_idf::partitions::PARTITION_TABLE_MAX_LEN],
tablebuffer
[0u8; esp_bootloader_esp_idf::partitions::PARTITION_TABLE_MAX_LEN]
);
let storage_ota = mk_static!(FlashStorage, FlashStorage::new());
let pt =
@@ -246,36 +250,37 @@ impl PlantHal {
// List all partitions - this is just FYI
for i in 0..pt.len() {
println!("{:?}", pt.get_partition(i));
info!("{:?}", pt.get_partition(i));
}
let ota_data = pt
.find_partition(esp_bootloader_esp_idf::partitions::PartitionType::Data(
let ota_data = mk_static!(
PartitionEntry,
pt.find_partition(esp_bootloader_esp_idf::partitions::PartitionType::Data(
DataPartitionSubType::Ota,
))?
.unwrap();
.expect("No OTA data partition found")
);
let ota_data = mk_static!(PartitionEntry, ota_data);
let ota_data = ota_data.as_embedded_storage(storage_ota);
let ota_data = mk_static!(FlashRegion<FlashStorage>, ota_data);
let ota_data = mk_static!(
FlashRegion<FlashStorage>,
ota_data.as_embedded_storage(storage_ota)
);
let mut ota = esp_bootloader_esp_idf::ota::Ota::new(ota_data)?;
let ota_partition = match ota.current_slot()? {
Slot::None => {
panic!("No OTA slot found");
panic!("No OTA slot active?");
}
Slot::Slot0 => pt
.find_partition(esp_bootloader_esp_idf::partitions::PartitionType::App(
AppPartitionSubType::Ota0,
))?
.unwrap(),
.expect("No OTA slot0 found"),
Slot::Slot1 => pt
.find_partition(esp_bootloader_esp_idf::partitions::PartitionType::App(
AppPartitionSubType::Ota1,
))?
.unwrap(),
.expect("No OTA slot1 found"),
};
let ota_next = mk_static!(PartitionEntry, ota_partition);
@@ -289,11 +294,11 @@ impl PlantHal {
.find_partition(esp_bootloader_esp_idf::partitions::PartitionType::Data(
DataPartitionSubType::LittleFs,
))?
.unwrap();
.expect("Data partition with littlefs not found");
let data_partition = mk_static!(PartitionEntry, data_partition);
let storage_data = mk_static!(FlashStorage, FlashStorage::new());
let mut data = mk_static!(
let data = mk_static!(
FlashRegion<FlashStorage>,
data_partition.as_embedded_storage(storage_data)
);
@@ -313,7 +318,7 @@ impl PlantHal {
}
let fs = Arc::new(Mutex::new(
lfs2Filesystem::mount(alloc, lfs2filesystem).unwrap(),
lfs2Filesystem::mount(alloc, lfs2filesystem).expect("Could not mount lfs2 filesystem"),
));
let mut esp = Esp {
@@ -362,9 +367,8 @@ impl PlantHal {
);
esp.init_rtc_deepsleep_memory(init_rtc_store, to_config_mode);
let fs_mount_error = esp.mount_file_system().is_err();
let config = esp.load_config();
let config = esp.load_config().await;
log::info!("Init rtc driver");
// let mut rtc = Ds323x::new_ds3231(MutexDevice::new(&I2C_DRIVER));