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-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",

View File

@ -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.

View File

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

View File

@ -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;
/*************************************************
@ -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);

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::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<TallyID>,
@ -30,42 +32,63 @@ impl AttendanceDay {
}
#[derive(Clone)]
pub struct IDStore {
pub days: BTreeMap<Date, AttendanceDay>,
pub struct IDStore<T: Persistence> {
pub current_day: AttendanceDay,
pub mapping: IDMapping,
persistence_layer: T,
}
impl IDStore {
pub fn new() -> Self {
IDStore {
days: BTreeMap::new(),
mapping: IDMapping::new(),
impl<T: Persistence> IDStore<T> {
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;
if self.current_day.date == current_date {
let changed = self.current_day.add_id(id);
if changed {
self.persist_day().await;
}
return changed;
}
/// 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;
let new_day = AttendanceDay::new(current_date);
self.current_day = new_day;
if self.days.contains_key(&current_day) {
return self.days.get_mut(&current_day).unwrap();
let changed = self.current_day.add_id(id);
if changed {
self.persist_day().await;
}
self.days
.insert(current_day, AttendanceDay::new(current_day));
self.days.get_mut(&current_day.clone()).unwrap()
changed
}
}

View File

@ -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;

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);
}