Use ekv for storage

This commit is contained in:
Mona Mayrhofer 2025-06-28 11:19:09 +02:00
parent b828cc4bde
commit 299504d1b5
No known key found for this signature in database
GPG key ID: 3E2BDA732A957188
5 changed files with 145 additions and 35 deletions

23
Cargo.lock generated
View file

@ -83,7 +83,7 @@ dependencies = [
"embedded-io-async", "embedded-io-async",
"futures", "futures",
"log", "log",
"rand_core", "rand_core 0.6.4",
] ]
[[package]] [[package]]
@ -226,6 +226,15 @@ dependencies = [
"litrs 0.4.1", "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]] [[package]]
name = "embassy-embedded-hal" name = "embassy-embedded-hal"
version = "0.3.0" version = "0.3.0"
@ -561,7 +570,7 @@ dependencies = [
"nb 1.1.0", "nb 1.1.0",
"paste", "paste",
"portable-atomic", "portable-atomic",
"rand_core", "rand_core 0.6.4",
"riscv", "riscv",
"serde", "serde",
"strum 0.27.1", "strum 0.27.1",
@ -697,7 +706,7 @@ dependencies = [
"num-traits", "num-traits",
"portable-atomic", "portable-atomic",
"portable_atomic_enum", "portable_atomic_enum",
"rand_core", "rand_core 0.6.4",
] ]
[[package]] [[package]]
@ -964,6 +973,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"bleps", "bleps",
"critical-section", "critical-section",
"ekv",
"embassy-executor", "embassy-executor",
"embassy-futures", "embassy-futures",
"embassy-net", "embassy-net",
@ -982,6 +992,7 @@ dependencies = [
"libm", "libm",
"log", "log",
"postcard", "postcard",
"rand_core 0.9.3",
"serde", "serde",
"smart-leds", "smart-leds",
"smoltcp", "smoltcp",
@ -1192,6 +1203,12 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "rand_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
[[package]] [[package]]
name = "rgb" name = "rgb"
version = "0.8.50" version = "0.8.50"

View file

@ -71,6 +71,9 @@ esp-storage = { version = "0.6.0", features = ["esp32c3"] }
embedded-storage = "0.3.1" embedded-storage = "0.3.1"
postcard = { version = "1.1.2", features = [] } postcard = { version = "1.1.2", features = [] }
serde = { version = "1.0.219", features = ["derive"], default-features = false } 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] [profile.dev]
# Rust debug is too slow. # Rust debug is too slow.

View file

@ -3,6 +3,6 @@
nvs, data, nvs, 0x9000, 0x6000, nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000, 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, app_store,0x40, 0x01, 0x10000, 0x10000,
factory, app, factory, 0x20000, 1M, factory, app, factory, 0x20000, 1M,

1 # ESP-IDF Partition Table
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 # TODO Keep in sync with main.rs APP_DATA_OFFSET # TODO Keep in sync with lib.rs APP_DATA_START/SIZE
6 app_store,0x40, 0x01, 0x10000, 0x10000,
7 factory, app, factory, 0x20000, 1M,
8

View file

@ -1,6 +1,7 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
use ekv::Database;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_futures::select::select; use embassy_futures::select::select;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
@ -13,14 +14,16 @@ use embedded_storage::nor_flash::ReadNorFlash;
use esp_hal::Blocking; use esp_hal::Blocking;
use esp_hal::clock::CpuClock; use esp_hal::clock::CpuClock;
use esp_hal::gpio::{Input, InputConfig, Pull}; use esp_hal::gpio::{Input, InputConfig, Pull};
use esp_hal::rng::Rng;
use esp_hal::spi::master::{Config, Spi}; use esp_hal::spi::master::{Config, Spi};
use esp_hal::time::Rate; use esp_hal::time::Rate;
use esp_hal::timer::systimer::SystemTimer; use esp_hal::timer::systimer::SystemTimer;
use esp_hal::timer::timg::TimerGroup; use esp_hal::timer::timg::TimerGroup;
use esp_storage::FlashStorage; use esp_storage::FlashStorage;
use log::info; use log::info;
use m5stamp_c3_ws2812::FlashEkvStorage;
use postcard::from_bytes; use postcard::from_bytes;
use postcard::to_vec; use rand_core::RngCore;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use smart_leds::RGB8; use smart_leds::RGB8;
@ -35,7 +38,7 @@ fn panic(_: &core::panic::PanicInfo) -> ! {
extern crate alloc; extern crate alloc;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
struct State { struct State {
pub count: u8, pub count: u8,
} }
@ -50,6 +53,8 @@ static STATE: Mutex<CriticalSectionRawMutex, TargetState> = Mutex::new(TargetSta
enabled: [false; 12], 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]; const ORDER: [usize; 12] = [5, 6, 4, 7, 3, 8, 2, 9, 1, 10, 0, 11];
//Keep in syn with partitions.csv //Keep in syn with partitions.csv
@ -71,28 +76,57 @@ async fn main(spawner: Spawner) {
info!("Embassy initialized!"); info!("Embassy initialized!");
let mut flash = FlashStorage::new(); let mut rng = Rng::new(peripherals.RNG);
info!("Flash: {flash:?}");
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 state = {
let mut bytes = [0; 128]; let mut bytes = [0; 32];
flash.read(APP_DATA_OFFSET, &mut bytes[..]).unwrap();
info!("{bytes:x?}"); let r = {
if let Ok(data) = from_bytes::<State>(&bytes[..]) { let trx = db.read_transaction().await;
info!("Successfully restored data."); trx.read(STATE_DB_KEY, bytes.as_mut_slice()).await
data };
} else {
State { count: 0 } match r {
Ok(_) => {
info!("{bytes:x?}");
match from_bytes::<State>(&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 timer1 = TimerGroup::new(peripherals.TIMG0);
let _init = esp_wifi::init( let _init = esp_wifi::init(timer1.timer0, rng, peripherals.RADIO_CLK).unwrap();
timer1.timer0,
esp_hal::rng::Rng::new(peripherals.RNG),
peripherals.RADIO_CLK,
)
.unwrap();
let led_strip = { let led_strip = {
let spi = Spi::new( 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); state.count = state.count.saturating_add_signed(delta as i8).clamp(0, 12);
//Write Updated state //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]; let mut d = [10u8; 32];
flash.read(APP_DATA_OFFSET, &mut bytes[..]).unwrap(); postcard::to_slice(&state, d.as_mut_slice()).unwrap();
info!("Write -> Reaa: {bytes:x?}"); 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> { struct RotaryEncoderConfig<'a, 'b> {

View file

@ -1 +1,63 @@
#![no_std] #![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)
}
}