implemented SD card abstraction & used it in IDStore

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

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

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;
/// 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(&current_day) {
return self.days.get_mut(&current_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(&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;
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);
}