diff --git a/Cargo.lock b/Cargo.lock index ffa4b65..109e3dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,7 +83,7 @@ dependencies = [ "embedded-io-async", "futures", "log", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -226,6 +226,15 @@ dependencies = [ "litrs 0.4.1", ] +[[package]] +name = "ekv" +version = "1.0.0" +source = "git+https://github.com/embassy-rs/ekv.git#68b27c52eb587ba883f39f3a8000f671c858f587" +dependencies = [ + "embassy-sync 0.7.0", + "heapless 0.8.0", +] + [[package]] name = "embassy-embedded-hal" version = "0.3.0" @@ -561,7 +570,7 @@ dependencies = [ "nb 1.1.0", "paste", "portable-atomic", - "rand_core", + "rand_core 0.6.4", "riscv", "serde", "strum 0.27.1", @@ -697,7 +706,7 @@ dependencies = [ "num-traits", "portable-atomic", "portable_atomic_enum", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -964,6 +973,7 @@ version = "0.1.0" dependencies = [ "bleps", "critical-section", + "ekv", "embassy-executor", "embassy-futures", "embassy-net", @@ -982,6 +992,7 @@ dependencies = [ "libm", "log", "postcard", + "rand_core 0.9.3", "serde", "smart-leds", "smoltcp", @@ -1192,6 +1203,12 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" + [[package]] name = "rgb" version = "0.8.50" diff --git a/Cargo.toml b/Cargo.toml index 1f5e0cc..b066e30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,6 +71,9 @@ esp-storage = { version = "0.6.0", features = ["esp32c3"] } embedded-storage = "0.3.1" postcard = { version = "1.1.2", features = [] } serde = { version = "1.0.219", features = ["derive"], default-features = false } +# Use git version, because released versions use old version of embassy-sync +ekv = { git = "https://github.com/embassy-rs/ekv.git" } +rand_core = "0.9.3" [profile.dev] # Rust debug is too slow. diff --git a/partitions.csv b/partitions.csv index 8d9f325..4c809f2 100644 --- a/partitions.csv +++ b/partitions.csv @@ -3,6 +3,6 @@ nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, -# TODO Keep in sync with main.rs APP_DATA_OFFSET +# TODO Keep in sync with lib.rs APP_DATA_START/SIZE app_store,0x40, 0x01, 0x10000, 0x10000, factory, app, factory, 0x20000, 1M, diff --git a/src/bin/main.rs b/src/bin/main.rs index 61246b3..ee2e1aa 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -1,6 +1,7 @@ #![no_std] #![no_main] +use ekv::Database; use embassy_executor::Spawner; use embassy_futures::select::select; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; @@ -13,14 +14,16 @@ use embedded_storage::nor_flash::ReadNorFlash; use esp_hal::Blocking; use esp_hal::clock::CpuClock; use esp_hal::gpio::{Input, InputConfig, Pull}; +use esp_hal::rng::Rng; use esp_hal::spi::master::{Config, Spi}; use esp_hal::time::Rate; use esp_hal::timer::systimer::SystemTimer; use esp_hal::timer::timg::TimerGroup; use esp_storage::FlashStorage; use log::info; +use m5stamp_c3_ws2812::FlashEkvStorage; use postcard::from_bytes; -use postcard::to_vec; +use rand_core::RngCore; use serde::Deserialize; use serde::Serialize; use smart_leds::RGB8; @@ -35,7 +38,7 @@ fn panic(_: &core::panic::PanicInfo) -> ! { extern crate alloc; -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] struct State { pub count: u8, } @@ -50,6 +53,8 @@ static STATE: Mutex = Mutex::new(TargetSta enabled: [false; 12], }); +const STATE_DB_KEY: &[u8] = b"STATE"; + const ORDER: [usize; 12] = [5, 6, 4, 7, 3, 8, 2, 9, 1, 10, 0, 11]; //Keep in syn with partitions.csv @@ -71,28 +76,57 @@ async fn main(spawner: Spawner) { info!("Embassy initialized!"); - let mut flash = FlashStorage::new(); - info!("Flash: {flash:?}"); + let mut rng = Rng::new(peripherals.RNG); + + let db = { + let random_seed = rng.random(); + let mut config = ekv::Config::default(); + config.random_seed = random_seed; + Database::<_, NoopRawMutex>::new(FlashEkvStorage::default(), config) + }; + info!("Database created."); let mut state = { - let mut bytes = [0; 128]; - flash.read(APP_DATA_OFFSET, &mut bytes[..]).unwrap(); - info!("{bytes:x?}"); - if let Ok(data) = from_bytes::(&bytes[..]) { - info!("Successfully restored data."); - data - } else { - State { count: 0 } + let mut bytes = [0; 32]; + + let r = { + let trx = db.read_transaction().await; + trx.read(STATE_DB_KEY, bytes.as_mut_slice()).await + }; + + match r { + Ok(_) => { + info!("{bytes:x?}"); + match from_bytes::(&bytes[..]) { + Ok(data) => { + info!("Successfully restored data."); + data + } + Err(err) => { + info!("Stored data was corrupted. {err:?}"); + State::default() + } + } + } + Err(ekv::ReadError::KeyNotFound) => { + info!("No data was saved yet."); + State::default() + } + Err(ekv::ReadError::Corrupted) => { + info!("Database was corrupt - formatting database"); + let r = db.format().await; + info!("{r:?}"); + State::default() + } + Err(err) => { + info!("Error while reading stored state: {err:?}"); + State::default() + } } }; let timer1 = TimerGroup::new(peripherals.TIMG0); - let _init = esp_wifi::init( - timer1.timer0, - esp_hal::rng::Rng::new(peripherals.RNG), - peripherals.RADIO_CLK, - ) - .unwrap(); + let _init = esp_wifi::init(timer1.timer0, rng, peripherals.RADIO_CLK).unwrap(); let led_strip = { let spi = Spi::new( @@ -137,22 +171,16 @@ async fn main(spawner: Spawner) { state.count = state.count.saturating_add_signed(delta as i8).clamp(0, 12); //Write Updated state - let mut d = [10u8; 128]; - postcard::to_slice(&state, d.as_mut_slice()).unwrap(); - info!("{d:x?}"); - let r = flash.erase(APP_DATA_OFFSET, APP_DATA_OFFSET + (4 << 10)); - info!("{r:?}"); - let r = flash.write(APP_DATA_OFFSET, d.as_slice()); - info!("{r:?}"); - { - let mut bytes = [0; 128]; - flash.read(APP_DATA_OFFSET, &mut bytes[..]).unwrap(); - info!("Write -> Reaa: {bytes:x?}"); + let mut d = [10u8; 32]; + postcard::to_slice(&state, d.as_mut_slice()).unwrap(); + let mut trx = db.write_transaction().await; + let r = trx.write(STATE_DB_KEY, d.as_slice()).await; + info!("{r:?}"); + let r = trx.commit().await; + info!("{r:?}"); } } - - // for inspiration have a look at the examples at https://github.com/esp-rs/esp-hal/tree/esp-hal-v1.0.0-beta.0/examples/src/bin } struct RotaryEncoderConfig<'a, 'b> { diff --git a/src/lib.rs b/src/lib.rs index 0c9ac1a..ab6e5d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,63 @@ #![no_std] +use ekv::{config, flash::Flash}; +use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use esp_storage::{FlashStorage, FlashStorageError}; + +pub const APP_DATA_START: usize = 0x10_000; +pub const APP_DATA_SIZE: usize = 0x10_000; + +pub struct FlashEkvStorage { + pub storage: FlashStorage, + pub partition_start: usize, + pub partition_size: usize, +} + +impl Default for FlashEkvStorage { + fn default() -> Self { + Self { + storage: FlashStorage::new(), + // Sync with Partitions.csv + partition_start: APP_DATA_START, + partition_size: APP_DATA_SIZE, + } + } +} + +impl Flash for FlashEkvStorage { + type Error = FlashStorageError; + + fn page_count(&self) -> usize { + self.partition_size / config::PAGE_SIZE + } + + async fn erase(&mut self, page_id: ekv::flash::PageID) -> Result<(), Self::Error> { + let from = page_id.index() * config::PAGE_SIZE; + let to = (page_id.index() + 1) * config::PAGE_SIZE; + self.storage.erase( + (from + self.partition_start) as u32, + (to + self.partition_start) as u32, + ) + } + + async fn read( + &mut self, + page_id: ekv::flash::PageID, + offset: usize, + data: &mut [u8], + ) -> Result<(), Self::Error> { + let from = (page_id.index() * config::PAGE_SIZE) + offset; + self.storage + .read((from + self.partition_start) as u32, data) + } + + async fn write( + &mut self, + page_id: ekv::flash::PageID, + offset: usize, + data: &[u8], + ) -> Result<(), Self::Error> { + let from = (page_id.index() * config::PAGE_SIZE) + offset; + self.storage + .write((from + self.partition_start) as u32, data) + } +}