implemented SD card abstraction & used it in IDStore

This commit is contained in:
Djeeberjr 2025-09-08 18:11:33 +02:00
parent b031a47e85
commit fe90ca9aa9
8 changed files with 227 additions and 84 deletions

79
Cargo.lock generated
View File

@ -312,7 +312,7 @@ dependencies = [
"edge-nal", "edge-nal",
"edge-raw", "edge-raw",
"embassy-futures", "embassy-futures",
"embassy-time", "embassy-time 0.4.0",
"heapless", "heapless",
"log", "log",
"num_enum", "num_enum",
@ -325,7 +325,7 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac19c3edcdad839c71cb919cd09a632d9915d630760b37f0b74290188c08f248" checksum = "ac19c3edcdad839c71cb919cd09a632d9915d630760b37f0b74290188c08f248"
dependencies = [ dependencies = [
"embassy-time", "embassy-time 0.4.0",
"embedded-io-async", "embedded-io-async",
] ]
@ -358,7 +358,7 @@ dependencies = [
"embassy-embedded-hal 0.4.0", "embassy-embedded-hal 0.4.0",
"embassy-futures", "embassy-futures",
"embassy-sync 0.6.2", "embassy-sync 0.6.2",
"embassy-time", "embassy-time 0.4.0",
"embedded-hal 0.2.7", "embedded-hal 0.2.7",
"embedded-hal 1.0.0", "embedded-hal 1.0.0",
"embedded-hal-async", "embedded-hal-async",
@ -375,7 +375,7 @@ checksum = "d1611b7a7ab5d1fbed84c338df26d56fd9bded58006ebb029075112ed2c5e039"
dependencies = [ dependencies = [
"embassy-futures", "embassy-futures",
"embassy-hal-internal", "embassy-hal-internal",
"embassy-sync 0.7.0", "embassy-sync 0.7.2",
"embedded-hal 0.2.7", "embedded-hal 0.2.7",
"embedded-hal 1.0.0", "embedded-hal 1.0.0",
"embedded-hal-async", "embedded-hal-async",
@ -409,9 +409,9 @@ dependencies = [
[[package]] [[package]]
name = "embassy-futures" name = "embassy-futures"
version = "0.1.1" version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f878075b9794c1e4ac788c95b728f26aa6366d32eeb10c7051389f898f7d067" checksum = "dc2d050bdc5c21e0862a89256ed8029ae6c290a93aecefc73084b3002cdebb01"
[[package]] [[package]]
name = "embassy-hal-internal" name = "embassy-hal-internal"
@ -424,14 +424,14 @@ dependencies = [
[[package]] [[package]]
name = "embassy-net" name = "embassy-net"
version = "0.7.0" version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "940c4b9fe5c1375b09a0c6722c0100d6b2ed46a717a34f632f26e8d7327c4383" checksum = "0558a231a47e7d4a06a28b5278c92e860f1200f24821d2f365a2f40fe3f3c7b2"
dependencies = [ dependencies = [
"document-features", "document-features",
"embassy-net-driver", "embassy-net-driver",
"embassy-sync 0.6.2", "embassy-sync 0.7.2",
"embassy-time", "embassy-time 0.5.0",
"embedded-io-async", "embedded-io-async",
"embedded-nal-async", "embedded-nal-async",
"heapless", "heapless",
@ -461,15 +461,15 @@ dependencies = [
[[package]] [[package]]
name = "embassy-sync" name = "embassy-sync"
version = "0.7.0" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cef1a8a1ea892f9b656de0295532ac5d8067e9830d49ec75076291fd6066b136" checksum = "73974a3edbd0bd286759b3d483540f0ebef705919a5f56f4fc7709066f71689b"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"critical-section", "critical-section",
"embedded-io-async", "embedded-io-async",
"futures-core",
"futures-sink", "futures-sink",
"futures-util",
"heapless", "heapless",
"log", "log",
] ]
@ -492,10 +492,26 @@ dependencies = [
] ]
[[package]] [[package]]
name = "embassy-time-driver" name = "embassy-time"
version = "0.2.0" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" 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 = [ dependencies = [
"document-features", "document-features",
] ]
@ -544,6 +560,16 @@ dependencies = [
"embedded-hal 1.0.0", "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]] [[package]]
name = "embedded-io" name = "embedded-io"
version = "0.6.1" version = "0.6.1"
@ -579,14 +605,13 @@ dependencies = [
] ]
[[package]] [[package]]
name = "embedded-sdmmc-dev" name = "embedded-sdmmc"
version = "0.8.2" version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ceec3a654b00a4acf9e2788d72b66fc3ba86d91b834586a4ed576324e351e38b" checksum = "a1132a10f327a9dff9d0629124e8b26cb2b3a930ab3dfedbadd295c107955e92"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"embedded-hal 1.0.0", "embedded-hal 1.0.0",
"embedded-io",
"heapless", "heapless",
"log", "log",
] ]
@ -747,7 +772,7 @@ dependencies = [
"document-features", "document-features",
"embassy-executor", "embassy-executor",
"embassy-sync 0.6.2", "embassy-sync 0.6.2",
"embassy-time", "embassy-time 0.4.0",
"embassy-time-driver", "embassy-time-driver",
"embassy-time-queue-utils", "embassy-time-queue-utils",
"esp-config 0.5.0", "esp-config 0.5.0",
@ -1046,12 +1071,13 @@ dependencies = [
"edge-nal-embassy", "edge-nal-embassy",
"embassy-executor", "embassy-executor",
"embassy-net", "embassy-net",
"embassy-sync 0.7.0", "embassy-sync 0.7.2",
"embassy-time", "embassy-time 0.4.0",
"embedded-hal 1.0.0", "embedded-hal 1.0.0",
"embedded-hal-bus",
"embedded-io", "embedded-io",
"embedded-io-async", "embedded-io-async",
"embedded-sdmmc-dev", "embedded-sdmmc",
"esp-alloc", "esp-alloc",
"esp-bootloader-esp-idf", "esp-bootloader-esp-idf",
"esp-hal", "esp-hal",
@ -1063,6 +1089,7 @@ dependencies = [
"log", "log",
"picoserve", "picoserve",
"serde", "serde",
"serde_json",
"smart-leds", "smart-leds",
"smoltcp", "smoltcp",
"static_cell", "static_cell",
@ -1395,7 +1422,7 @@ dependencies = [
"const-sha1", "const-sha1",
"data-encoding", "data-encoding",
"embassy-net", "embassy-net",
"embassy-time", "embassy-time 0.5.0",
"embedded-io-async", "embedded-io-async",
"futures-util", "futures-util",
"heapless", "heapless",
@ -1644,9 +1671,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.142" version = "1.0.143"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",

View File

@ -63,10 +63,12 @@ embassy-sync = { version = "0.7.0", features = ["log"] }
ds3231 = { version = "0.3.0", features = ["async", "temperature_f32"] } ds3231 = { version = "0.3.0", features = ["async", "temperature_f32"] }
chrono = { version = "0.4.41", default-features = false } chrono = { version = "0.4.41", default-features = false }
dir-embed = "0.3.0" 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"]} 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" smart-leds = "0.4.0"
serde = { version = "1.0.219", default-features = false, features = ["derive", "alloc"] } 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] [profile.dev]
# Rust debug is too slow. # Rust debug is too slow.

View File

@ -1,3 +1,4 @@
pub mod hardware; pub mod hardware;
pub mod network; pub mod network;
pub mod wifi; pub mod wifi;
pub mod sd_card;

View File

@ -1,18 +1,18 @@
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_net::Stack; use embassy_net::Stack;
use embassy_time::{Duration, Timer}; use embassy_time::{Duration, Timer};
use embedded_sdmmc_dev::SdCard;
use esp_hal::i2c::master::Config; use esp_hal::i2c::master::Config;
use esp_hal::peripherals::{ use esp_hal::peripherals::{
self, GPIO0, GPIO1, GPIO2, GPIO10, GPIO16, GPIO17, GPIO18, GPIO19, GPIO20, GPIO21, GPIO22, GPIO0, GPIO1, GPIO16, GPIO17, GPIO18, GPIO19, GPIO20, GPIO21, GPIO22, GPIO23, I2C0, RMT, SPI2,
GPIO23, I2C0, RMT, SPI2, UART1, UART1,
}; };
use esp_hal::rmt::{ConstChannelAccess, Rmt, Tx}; use esp_hal::rmt::{ConstChannelAccess, Rmt};
use esp_hal::spi::{ use esp_hal::spi::{
Mode,
master::{Config as Spi_config, Spi}, master::{Config as Spi_config, Spi},
}; };
use esp_hal::Blocking;
use esp_hal::time::Rate; use esp_hal::time::Rate;
use esp_hal::timer::timg::TimerGroup; use esp_hal::timer::timg::TimerGroup;
use esp_hal::{ use esp_hal::{
@ -23,19 +23,12 @@ use esp_hal::{
timer::systimer::SystemTimer, timer::systimer::SystemTimer,
uart::Uart, uart::Uart,
}; };
use esp_hal_smartled::{SmartLedsAdapterAsync, buffer_size_async}; 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 esp_println::logger::init_logger;
use log::{debug, error}; use log::{debug, error};
use crate::init::network; use crate::init::network;
use crate::init::sd_card::setup_sdcard;
use crate::init::wifi; 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; pub const LED_BUFFER_SIZE: usize = NUM_LEDS * 25;
#[panic_handler] #[panic_handler]
@ -101,15 +94,20 @@ pub async fn hardware_init(
let i2c_device = setup_i2c(peripherals.I2C0, peripherals.GPIO22, peripherals.GPIO23); let i2c_device = setup_i2c(peripherals.I2C0, peripherals.GPIO22, peripherals.GPIO23);
let spi_device = setup_spi( let spi_bus = setup_spi(
peripherals.SPI2, peripherals.SPI2,
peripherals.GPIO19, peripherals.GPIO19,
peripherals.GPIO20, peripherals.GPIO20,
peripherals.GPIO18, 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; 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) // 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>) { fn init_lvl_shifter(oe_pin: GPIO0<'static>) {
let mut oe_lvl_shifter = let mut oe_lvl_shifter = Output::new(
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_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(); oe_lvl_shifter.set_high();
} }
@ -167,24 +170,14 @@ fn setup_spi(
sck: GPIO19<'static>, sck: GPIO19<'static>,
miso: GPIO20<'static>, miso: GPIO20<'static>,
mosi: GPIO18<'static>, mosi: GPIO18<'static>,
cs: GPIO2<'static>, ) -> Spi<'static, Blocking> {
) -> Spi<'static, Async> {
let spi = match Spi::new(spi2, Spi_config::default()) { let spi = match Spi::new(spi2, Spi_config::default()) {
Ok(spi) => spi Ok(spi) => spi.with_sck(sck).with_miso(miso).with_mosi(mosi),
.with_sck(sck)
.with_miso(miso)
.with_mosi(mosi)
.with_cs(cs)
.into_async(),
Err(e) => panic!("Failed to initialize SPI: {:?}", e), Err(e) => panic!("Failed to initialize SPI: {:?}", e),
}; };
spi 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> { pub fn setup_buzzer(buzzer_gpio: GPIO21<'static>) -> Output<'static> {
let config = esp_hal::gpio::OutputConfig::default() let config = esp_hal::gpio::OutputConfig::default()
.with_drive_strength(esp_hal::gpio::DriveStrength::_40mA); .with_drive_strength(esp_hal::gpio::DriveStrength::_40mA);

84
src/init/sd_card.rs Normal file
View File

@ -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<ExclusiveDevice<Spi<'static, Blocking>, 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<AttendanceDay> {
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<crate::store::IDMapping> {
todo!()
}
async fn save_mapping(&mut self, data: &crate::store::IDMapping) {
todo!()
}
async fn list_days(&mut self) -> Vec<Date> {
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
}
}

View File

@ -1,10 +1,12 @@
use crate::store::persistence::Persistence;
use super::Date; use super::Date;
use super::IDMapping; use super::IDMapping;
use super::TallyID; use super::TallyID;
use alloc::collections::BTreeMap;
use alloc::vec::Vec; use alloc::vec::Vec;
use serde::Deserialize;
#[derive(Clone)] #[derive(Clone, Deserialize)]
pub struct AttendanceDay { pub struct AttendanceDay {
date: Date, date: Date,
ids: Vec<TallyID>, ids: Vec<TallyID>,
@ -30,42 +32,63 @@ impl AttendanceDay {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct IDStore { pub struct IDStore<T: Persistence> {
pub days: BTreeMap<Date, AttendanceDay>, pub current_day: AttendanceDay,
pub mapping: IDMapping, pub mapping: IDMapping,
persistence_layer: T,
} }
impl IDStore { impl<T: Persistence> IDStore<T> {
pub fn new() -> Self { pub async fn new_from_storage(mut persistence_layer: T) -> Self {
IDStore { let mapping = match persistence_layer.load_mapping().await {
days: BTreeMap::new(), Some(map) => map,
mapping: IDMapping::new(), 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 { async fn persist_day(&mut self) {
// TODO: implement self.persistence_layer
todo!() .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 /// Add a new id for the current day
/// Returns false if ID is already present at the current day. /// Returns false if ID is already present at the current day.
pub fn add_id(&mut self, id: TallyID) -> bool { pub async fn add_id(&mut self, id: TallyID) -> bool {
self.get_current_day().add_id(id) let current_date: Date = 1;
}
/// Get the `AttendanceDay` of the current day if self.current_day.date == current_date {
/// Creates a new if not exists let changed = self.current_day.add_id(id);
pub fn get_current_day(&mut self) -> &mut AttendanceDay { if changed {
let current_day: Date = 1; self.persist_day().await;
}
if self.days.contains_key(&current_day) { return changed;
return self.days.get_mut(&current_day).unwrap();
} }
self.days let new_day = AttendanceDay::new(current_date);
.insert(current_day, AttendanceDay::new(current_day)); self.current_day = new_day;
self.days.get_mut(&current_day.clone()).unwrap() let changed = self.current_day.add_id(id);
if changed {
self.persist_day().await;
}
changed
} }
} }

View File

@ -1,8 +1,9 @@
mod id_mapping; mod id_mapping;
pub mod persistence;
mod id_store; mod id_store;
pub use id_mapping::{IDMapping, Name}; pub use id_mapping::{IDMapping, Name};
pub use id_store::IDStore; pub use id_store::{IDStore,AttendanceDay};
pub type TallyID = [u8; 8]; pub type TallyID = [u8; 8];
pub type Date = u64; pub type Date = u64;

12
src/store/persistence.rs Normal file
View File

@ -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<AttendanceDay>;
async fn save_day(&mut self, day: Date, data: &AttendanceDay);
async fn list_days(&mut self) -> Vec<Date>;
async fn load_mapping(&mut self) -> Option<IDMapping>;
async fn save_mapping(&mut self, data: &IDMapping);
}