From fe90ca9aa9420b34f956cdf4ba09bcd86b6701c7 Mon Sep 17 00:00:00 2001 From: Djeeberjr Date: Mon, 8 Sep 2025 18:11:33 +0200 Subject: [PATCH] implemented SD card abstraction & used it in IDStore --- Cargo.lock | 79 ++++++++++++++++++++++++------------- Cargo.toml | 4 +- src/init.rs | 1 + src/init/hardware.rs | 55 ++++++++++++-------------- src/init/sd_card.rs | 84 ++++++++++++++++++++++++++++++++++++++++ src/store/id_store.rs | 73 ++++++++++++++++++++++------------ src/store/mod.rs | 3 +- src/store/persistence.rs | 12 ++++++ 8 files changed, 227 insertions(+), 84 deletions(-) create mode 100644 src/init/sd_card.rs create mode 100644 src/store/persistence.rs diff --git a/Cargo.lock b/Cargo.lock index bed633e..005a334 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -312,7 +312,7 @@ dependencies = [ "edge-nal", "edge-raw", "embassy-futures", - "embassy-time", + "embassy-time 0.4.0", "heapless", "log", "num_enum", @@ -325,7 +325,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac19c3edcdad839c71cb919cd09a632d9915d630760b37f0b74290188c08f248" dependencies = [ - "embassy-time", + "embassy-time 0.4.0", "embedded-io-async", ] @@ -358,7 +358,7 @@ dependencies = [ "embassy-embedded-hal 0.4.0", "embassy-futures", "embassy-sync 0.6.2", - "embassy-time", + "embassy-time 0.4.0", "embedded-hal 0.2.7", "embedded-hal 1.0.0", "embedded-hal-async", @@ -375,7 +375,7 @@ checksum = "d1611b7a7ab5d1fbed84c338df26d56fd9bded58006ebb029075112ed2c5e039" dependencies = [ "embassy-futures", "embassy-hal-internal", - "embassy-sync 0.7.0", + "embassy-sync 0.7.2", "embedded-hal 0.2.7", "embedded-hal 1.0.0", "embedded-hal-async", @@ -409,9 +409,9 @@ dependencies = [ [[package]] name = "embassy-futures" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f878075b9794c1e4ac788c95b728f26aa6366d32eeb10c7051389f898f7d067" +checksum = "dc2d050bdc5c21e0862a89256ed8029ae6c290a93aecefc73084b3002cdebb01" [[package]] name = "embassy-hal-internal" @@ -424,14 +424,14 @@ dependencies = [ [[package]] name = "embassy-net" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "940c4b9fe5c1375b09a0c6722c0100d6b2ed46a717a34f632f26e8d7327c4383" +checksum = "0558a231a47e7d4a06a28b5278c92e860f1200f24821d2f365a2f40fe3f3c7b2" dependencies = [ "document-features", "embassy-net-driver", - "embassy-sync 0.6.2", - "embassy-time", + "embassy-sync 0.7.2", + "embassy-time 0.5.0", "embedded-io-async", "embedded-nal-async", "heapless", @@ -461,15 +461,15 @@ dependencies = [ [[package]] name = "embassy-sync" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cef1a8a1ea892f9b656de0295532ac5d8067e9830d49ec75076291fd6066b136" +checksum = "73974a3edbd0bd286759b3d483540f0ebef705919a5f56f4fc7709066f71689b" dependencies = [ "cfg-if", "critical-section", "embedded-io-async", + "futures-core", "futures-sink", - "futures-util", "heapless", "log", ] @@ -492,10 +492,26 @@ dependencies = [ ] [[package]] -name = "embassy-time-driver" -version = "0.2.0" +name = "embassy-time" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d45f5d833b6d98bd2aab0c2de70b18bfaa10faf661a1578fd8e5dfb15eb7eba" +checksum = "f4fa65b9284d974dad7a23bb72835c4ec85c0b540d86af7fc4098c88cff51d65" +dependencies = [ + "cfg-if", + "critical-section", + "document-features", + "embassy-time-driver", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "futures-core", +] + +[[package]] +name = "embassy-time-driver" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0a244c7dc22c8d0289379c8d8830cae06bb93d8f990194d0de5efb3b5ae7ba6" dependencies = [ "document-features", ] @@ -544,6 +560,16 @@ dependencies = [ "embedded-hal 1.0.0", ] +[[package]] +name = "embedded-hal-bus" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513e0b3a8fb7d3013a8ae17a834283f170deaf7d0eeab0a7c1a36ad4dd356d22" +dependencies = [ + "critical-section", + "embedded-hal 1.0.0", +] + [[package]] name = "embedded-io" version = "0.6.1" @@ -579,14 +605,13 @@ dependencies = [ ] [[package]] -name = "embedded-sdmmc-dev" +name = "embedded-sdmmc" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceec3a654b00a4acf9e2788d72b66fc3ba86d91b834586a4ed576324e351e38b" +checksum = "a1132a10f327a9dff9d0629124e8b26cb2b3a930ab3dfedbadd295c107955e92" dependencies = [ "byteorder", "embedded-hal 1.0.0", - "embedded-io", "heapless", "log", ] @@ -747,7 +772,7 @@ dependencies = [ "document-features", "embassy-executor", "embassy-sync 0.6.2", - "embassy-time", + "embassy-time 0.4.0", "embassy-time-driver", "embassy-time-queue-utils", "esp-config 0.5.0", @@ -1046,12 +1071,13 @@ dependencies = [ "edge-nal-embassy", "embassy-executor", "embassy-net", - "embassy-sync 0.7.0", - "embassy-time", + "embassy-sync 0.7.2", + "embassy-time 0.4.0", "embedded-hal 1.0.0", + "embedded-hal-bus", "embedded-io", "embedded-io-async", - "embedded-sdmmc-dev", + "embedded-sdmmc", "esp-alloc", "esp-bootloader-esp-idf", "esp-hal", @@ -1063,6 +1089,7 @@ dependencies = [ "log", "picoserve", "serde", + "serde_json", "smart-leds", "smoltcp", "static_cell", @@ -1395,7 +1422,7 @@ dependencies = [ "const-sha1", "data-encoding", "embassy-net", - "embassy-time", + "embassy-time 0.5.0", "embedded-io-async", "futures-util", "heapless", @@ -1644,9 +1671,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.142" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ "itoa", "memchr", diff --git a/Cargo.toml b/Cargo.toml index 2f4a832..408303c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,10 +63,12 @@ embassy-sync = { version = "0.7.0", features = ["log"] } ds3231 = { version = "0.3.0", features = ["async", "temperature_f32"] } chrono = { version = "0.4.41", default-features = false } dir-embed = "0.3.0" -embedded-sdmmc-dev = "0.8.2" esp-hal-smartled = { git = "https://github.com/esp-rs/esp-hal-community.git", package = "esp-hal-smartled", branch = "main", features = ["esp32c6"]} smart-leds = "0.4.0" serde = { version = "1.0.219", default-features = false, features = ["derive", "alloc"] } +embedded-sdmmc = "0.8.0" +embedded-hal-bus = "0.3.0" +serde_json = { version = "1.0.143", default-features = false, features = ["alloc"]} [profile.dev] # Rust debug is too slow. diff --git a/src/init.rs b/src/init.rs index ef04822..3a47068 100644 --- a/src/init.rs +++ b/src/init.rs @@ -1,3 +1,4 @@ pub mod hardware; pub mod network; pub mod wifi; +pub mod sd_card; diff --git a/src/init/hardware.rs b/src/init/hardware.rs index 2c9b7d4..652e9cd 100644 --- a/src/init/hardware.rs +++ b/src/init/hardware.rs @@ -1,18 +1,18 @@ use embassy_executor::Spawner; use embassy_net::Stack; + use embassy_time::{Duration, Timer}; -use embedded_sdmmc_dev::SdCard; use esp_hal::i2c::master::Config; use esp_hal::peripherals::{ - self, GPIO0, GPIO1, GPIO2, GPIO10, GPIO16, GPIO17, GPIO18, GPIO19, GPIO20, GPIO21, GPIO22, - GPIO23, I2C0, RMT, SPI2, UART1, + GPIO0, GPIO1, GPIO16, GPIO17, GPIO18, GPIO19, GPIO20, GPIO21, GPIO22, GPIO23, I2C0, RMT, SPI2, + UART1, }; -use esp_hal::rmt::{ConstChannelAccess, Rmt, Tx}; +use esp_hal::rmt::{ConstChannelAccess, Rmt}; use esp_hal::spi::{ - Mode, master::{Config as Spi_config, Spi}, }; +use esp_hal::Blocking; use esp_hal::time::Rate; use esp_hal::timer::timg::TimerGroup; use esp_hal::{ @@ -23,19 +23,12 @@ use esp_hal::{ timer::systimer::SystemTimer, uart::Uart, }; - use esp_hal_smartled::{SmartLedsAdapterAsync, buffer_size_async}; - -use smart_leds::colors::{BLUE, GREEN, RED}; -use smart_leds::{ - RGB8, SmartLedsWriteAsync, brightness, gamma, - hsv::{Hsv, hsv2rgb}, -}; - use esp_println::logger::init_logger; use log::{debug, error}; use crate::init::network; +use crate::init::sd_card::setup_sdcard; use crate::init::wifi; /************************************************* @@ -55,7 +48,7 @@ use crate::init::wifi; * *************************************************/ -pub const NUM_LEDS: usize = 66; +pub const NUM_LEDS: usize = 66; pub const LED_BUFFER_SIZE: usize = NUM_LEDS * 25; #[panic_handler] @@ -101,15 +94,20 @@ pub async fn hardware_init( let i2c_device = setup_i2c(peripherals.I2C0, peripherals.GPIO22, peripherals.GPIO23); - let spi_device = setup_spi( + let spi_bus = setup_spi( peripherals.SPI2, peripherals.GPIO19, peripherals.GPIO20, peripherals.GPIO18, - peripherals.GPIO2, ); - let sd_card = setup_sdcard(spi_device); + let sd_cs_pin = Output::new( + peripherals.GPIO2, + esp_hal::gpio::Level::High, + OutputConfig::default(), + ); + + let vol_mgr = setup_sdcard(spi_bus, sd_cs_pin); let buzzer_gpio = peripherals.GPIO21; @@ -124,8 +122,13 @@ pub async fn hardware_init( // Initialize the level shifter for the NFC reader and LED (output-enable (OE) input is low, all outputs are placed in the high-impedance (Hi-Z) state) fn init_lvl_shifter(oe_pin: GPIO0<'static>) { - let mut oe_lvl_shifter = - Output::new(oe_pin, esp_hal::gpio::Level::Low, OutputConfig::default().with_drive_mode(esp_hal::gpio::DriveMode::PushPull).with_drive_strength(esp_hal::gpio::DriveStrength::_10mA)); + let mut oe_lvl_shifter = Output::new( + oe_pin, + esp_hal::gpio::Level::Low, + OutputConfig::default() + .with_drive_mode(esp_hal::gpio::DriveMode::PushPull) + .with_drive_strength(esp_hal::gpio::DriveStrength::_10mA), + ); oe_lvl_shifter.set_high(); } @@ -167,24 +170,14 @@ fn setup_spi( sck: GPIO19<'static>, miso: GPIO20<'static>, mosi: GPIO18<'static>, - cs: GPIO2<'static>, -) -> Spi<'static, Async> { +) -> Spi<'static, Blocking> { let spi = match Spi::new(spi2, Spi_config::default()) { - Ok(spi) => spi - .with_sck(sck) - .with_miso(miso) - .with_mosi(mosi) - .with_cs(cs) - .into_async(), + Ok(spi) => spi.with_sck(sck).with_miso(miso).with_mosi(mosi), Err(e) => panic!("Failed to initialize SPI: {:?}", e), }; spi } -fn setup_sdcard(spi_device: Spi<'static, Async>) { - //let sdcard = SdCard::new(spi_device as embedded_hal::spi::SpiDevice(), delayer) -} - pub fn setup_buzzer(buzzer_gpio: GPIO21<'static>) -> Output<'static> { let config = esp_hal::gpio::OutputConfig::default() .with_drive_strength(esp_hal::gpio::DriveStrength::_40mA); diff --git a/src/init/sd_card.rs b/src/init/sd_card.rs new file mode 100644 index 0000000..4970412 --- /dev/null +++ b/src/init/sd_card.rs @@ -0,0 +1,84 @@ +use alloc::vec::Vec; +use embassy_time::Delay; +use embedded_hal_bus::spi::ExclusiveDevice; +use embedded_sdmmc::{SdCard, TimeSource, Timestamp, VolumeIdx, VolumeManager}; +use esp_hal::{Blocking, gpio::Output, spi::master::Spi}; + +use crate::store::{AttendanceDay, Date, persistence::Persistence}; + +pub struct DummyTimesource; + +impl TimeSource for DummyTimesource { + fn get_timestamp(&self) -> Timestamp { + Timestamp { + year_since_1970: 0, + zero_indexed_month: 0, + zero_indexed_day: 0, + hours: 0, + minutes: 0, + seconds: 0, + } + } +} + +pub type VolMgr = VolumeManager< + SdCard, Output<'static>, Delay>, Delay>, + DummyTimesource, +>; + +pub fn setup_sdcard(spi_bus: Spi<'static, Blocking>, cs_pin: Output<'static>) -> SDCardPersistence { + let spi_device = ExclusiveDevice::new(spi_bus, cs_pin, Delay).unwrap(); + let sd_card = SdCard::new(spi_device, Delay); + let vol_mgr = VolumeManager::new(sd_card, DummyTimesource); + + SDCardPersistence { vol_mgr } +} + +pub struct SDCardPersistence { + vol_mgr: VolMgr, +} + +impl Persistence for SDCardPersistence { + async fn load_day(&mut self, day: crate::store::Date) -> Option { + let mut vol_0 = self.vol_mgr.open_volume(VolumeIdx(0)).unwrap(); + let mut root_dir = vol_0.open_root_dir().unwrap(); + let mut file = root_dir + .open_file_in_dir("days/TODO", embedded_sdmmc::Mode::ReadOnly) + .unwrap(); + + let mut read_buffer: [u8; 1024] = [0; 1024]; + let read = file.read(&mut read_buffer).unwrap(); + file.close().unwrap(); + + let day: AttendanceDay = serde_json::from_slice(&read_buffer).unwrap(); + + Some(day) + } + + async fn save_day(&mut self, day: Date, data: &AttendanceDay) { + todo!() + } + + async fn load_mapping(&mut self) -> Option { + todo!() + } + + async fn save_mapping(&mut self, data: &crate::store::IDMapping) { + todo!() + } + + async fn list_days(&mut self) -> Vec { + let mut vol_0 = self.vol_mgr.open_volume(VolumeIdx(0)).unwrap(); + let mut root_dir = vol_0.open_root_dir().unwrap(); + let mut days_dir = root_dir.open_dir("days").unwrap(); + + let mut days = Vec::new(); + days_dir + .iterate_dir(|e| { + days.push(1); + }) + .unwrap(); + + days + } +} diff --git a/src/store/id_store.rs b/src/store/id_store.rs index 45bf2f8..9f41e0e 100644 --- a/src/store/id_store.rs +++ b/src/store/id_store.rs @@ -1,10 +1,12 @@ +use crate::store::persistence::Persistence; + use super::Date; use super::IDMapping; use super::TallyID; -use alloc::collections::BTreeMap; use alloc::vec::Vec; +use serde::Deserialize; -#[derive(Clone)] +#[derive(Clone, Deserialize)] pub struct AttendanceDay { date: Date, ids: Vec, @@ -30,42 +32,63 @@ impl AttendanceDay { } #[derive(Clone)] -pub struct IDStore { - pub days: BTreeMap, +pub struct IDStore { + pub current_day: AttendanceDay, pub mapping: IDMapping, + persistence_layer: T, } -impl IDStore { - pub fn new() -> Self { - IDStore { - days: BTreeMap::new(), - mapping: IDMapping::new(), +impl IDStore { + pub async fn new_from_storage(mut persistence_layer: T) -> Self { + let mapping = match persistence_layer.load_mapping().await { + Some(map) => map, + None => IDMapping::new(), + }; + + let current_date: Date = 1; + + let day = persistence_layer + .load_day(current_date) + .await + .unwrap_or(AttendanceDay::new(current_date)); + + Self { + current_day: day, + mapping, + persistence_layer, } } - pub fn new_from_storage() -> Self { - // TODO: implement - todo!() + async fn persist_day(&mut self) { + self.persistence_layer + .save_day(self.current_day.date, &self.current_day) + .await + } + + async fn persist_mapping(&mut self) { + self.persistence_layer.save_mapping(&self.mapping).await } /// Add a new id for the current day /// Returns false if ID is already present at the current day. - pub fn add_id(&mut self, id: TallyID) -> bool { - self.get_current_day().add_id(id) - } + pub async fn add_id(&mut self, id: TallyID) -> bool { + let current_date: Date = 1; - /// Get the `AttendanceDay` of the current day - /// Creates a new if not exists - pub fn get_current_day(&mut self) -> &mut AttendanceDay { - let current_day: Date = 1; - - if self.days.contains_key(¤t_day) { - return self.days.get_mut(¤t_day).unwrap(); + if self.current_day.date == current_date { + let changed = self.current_day.add_id(id); + if changed { + self.persist_day().await; + } + return changed; } - self.days - .insert(current_day, AttendanceDay::new(current_day)); + let new_day = AttendanceDay::new(current_date); + self.current_day = new_day; - self.days.get_mut(¤t_day.clone()).unwrap() + let changed = self.current_day.add_id(id); + if changed { + self.persist_day().await; + } + changed } } diff --git a/src/store/mod.rs b/src/store/mod.rs index d72bfee..ae39628 100644 --- a/src/store/mod.rs +++ b/src/store/mod.rs @@ -1,8 +1,9 @@ mod id_mapping; +pub mod persistence; mod id_store; pub use id_mapping::{IDMapping, Name}; -pub use id_store::IDStore; +pub use id_store::{IDStore,AttendanceDay}; pub type TallyID = [u8; 8]; pub type Date = u64; diff --git a/src/store/persistence.rs b/src/store/persistence.rs new file mode 100644 index 0000000..30ece88 --- /dev/null +++ b/src/store/persistence.rs @@ -0,0 +1,12 @@ +use alloc::vec::Vec; + +use crate::store::{Date, IDMapping, id_store::AttendanceDay}; + +pub trait Persistence { + async fn load_day(&mut self, day: Date) -> Option; + async fn save_day(&mut self, day: Date, data: &AttendanceDay); + async fn list_days(&mut self) -> Vec; + + async fn load_mapping(&mut self) -> Option; + async fn save_mapping(&mut self, data: &IDMapping); +}