Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: add sd card manager support recovering from sd card getting disconnected #94

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion boards/communication/src/health.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use atsamd_hal::gpio::{Alternate, Pin, B, PB00, PB01, PB02, PB03, PB05, PB06, PB07, PB08, PB09};
use atsamd_hal::{adc::Adc, ehal::adc::OneShot, pac::ADC0, pac::ADC1};
use common_arm::HealthMonitorChannels;
use common_arm::{HealthMonitorChannels, SdManager};

// make sure to define the ADC types in types.rs

Expand All @@ -13,6 +13,7 @@ use common_arm::HealthMonitorChannels;
pub struct HealthMonitorChannelsCommunication {
reader: Adc<ADC0>,
reader1: Adc<ADC1>,
sd_manager: SdManager,
pin_3v3: Pin<PB01, Alternate<B>>,
pin_5v: Pin<PB02, Alternate<B>>,
pin_pyro: Pin<PB03, Alternate<B>>,
Expand Down Expand Up @@ -52,12 +53,16 @@ impl HealthMonitorChannels for HealthMonitorChannelsCommunication {
fn get_failover(&mut self) -> Option<u16> {
self.reader1.read(&mut self.pin_failover).ok()
}
fn get_sd_card_status(&mut self) -> Option<bool> {
self.sd_manager.is_mounted()
}
}

impl HealthMonitorChannelsCommunication {
pub fn new(
reader: Adc<ADC0>,
reader1: Adc<ADC1>,
sd_manager: SdManager,
pin_3v3: Pin<PB01, Alternate<B>>,
pin_5v: Pin<PB02, Alternate<B>>,
pin_pyro: Pin<PB03, Alternate<B>>,
Expand All @@ -71,6 +76,7 @@ impl HealthMonitorChannelsCommunication {
HealthMonitorChannelsCommunication {
reader,
reader1,
sd_manager,
pin_3v3,
pin_5v,
pin_pyro,
Expand Down
1 change: 1 addition & 0 deletions boards/communication/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ mod app {
let health_monitor_channels = HealthMonitorChannelsCommunication::new(
adc0,
adc1,
sd_manager,
pins.pb01.into(),
pins.pb02.into(),
pins.pb03.into(),
Expand Down
3 changes: 3 additions & 0 deletions libraries/common-arm/src/health/health_monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub trait HealthMonitorChannels {
fn get_ext_5v(&mut self) -> Option<u16>;
fn get_ext_3v3(&mut self) -> Option<u16>;
fn get_failover(&mut self) -> Option<u16>;
fn get_sd_card_status(&mut self) -> Option<bool>;
}

pub struct HealthMonitor<T: HealthMonitorChannels> {
Expand Down Expand Up @@ -74,6 +75,7 @@ where
ext_v5: None,
ext_3v3: None,
failover_sense: None,
sd_card: None
},
range_5v,
range_3v3,
Expand Down Expand Up @@ -102,6 +104,7 @@ where
self.data.ext_v5 = self.channels.get_ext_5v();
self.data.ext_3v3 = self.channels.get_ext_3v3();
self.data.failover_sense = self.channels.get_failover();
self.data.sd_card = self.channels.get_sd_card_status();
}
}

Expand Down
126 changes: 89 additions & 37 deletions libraries/common-arm/src/sd_manager.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use core::{fmt::Debug, marker::PhantomData};
use defmt::info;
use defmt::{info, error};
use embedded_hal as hal;
use embedded_sdmmc as sd;
use hal::spi::FullDuplex;

use messages::ErrorContext;
use crate::herror;

/// Time source for `[SdInterface]`. It doesn't return any useful information for now, and will
/// always return an arbitrary time.
pub struct TimeSink {
Expand Down Expand Up @@ -39,8 +42,8 @@
CS: hal::digital::v2::OutputPin,
{
pub sd_controller: sd::Controller<sd::SdMmcSpi<SPI, CS>, TimeSink>,
pub volume: sd::Volume,
pub root_directory: sd::Directory,
pub volume: Option<sd::Volume>,
pub root_directory: Option<sd::Directory>,
pub file: Option<sd::File>,
}

Expand All @@ -52,85 +55,134 @@
{
pub fn new(spi: SPI, cs: CS) -> Self {
let time_sink: TimeSink = TimeSink::new(); // Need to give this a DateTime object for actual timing.
let mut sd_cont = sd::Controller::new(sd::SdMmcSpi::new(spi, cs), time_sink);
match sd_cont.device().init() {
Ok(_) => match sd_cont.device().card_size_bytes() {
let mut sd_controller = sd::Controller::new(sd::SdMmcSpi::new(spi, cs), time_sink);
match sd_controller.device().init() {
Ok(_) => match sd_controller.device().card_size_bytes() {
Ok(size) => info!("Card is {} bytes", size),
Err(_) => panic!("Cannot get card size"),
Err(_) => error!("Cannot get card size"),
},
Err(_) => {
panic!("Cannot get SD card.");
herror!(Error, ErrorContext::SDCardNotConnected);
}
}
};

let mut volume = match sd_cont.get_volume(sd::VolumeIdx(0)) {
Ok(volume) => volume,
let mut volume = match sd_controller.get_volume(sd::VolumeIdx(0)) {
Ok(volume) => Some(volume),
Err(_) => {
panic!("Cannot get volume 0");
error!("Cannot get volume 0");
None
}
};

let root_directory = match sd_cont.open_root_dir(&volume) {
Ok(root_directory) => root_directory,
Err(_) => {
panic!("Cannot get root");
let root_directory = match &volume {
Some(volume) => match sd_controller.open_root_dir(volume) {
Ok(root_directory) => Some(root_directory),
Err(_) => {
error!("Cannot get root");
None
}
}
_ => None,
};
let file = sd_cont.open_file_in_dir(
&mut volume,
&root_directory,
"log.txt",
sd::Mode::ReadWriteCreateOrTruncate,
);
let file = match file {
Ok(file) => file,
Err(_) => {
panic!("Cannot create file.");
}

let file = match (&mut volume, &root_directory) {
(Some(volume), Some(root_directory)) => {
let _file = sd_controller.open_file_in_dir(
volume,
&root_directory,
"log.txt",
sd::Mode::ReadWriteCreateOrTruncate,
);
let _file = match _file {
Ok(__file) => Some(__file),
Err(_) => {
error!("Cannot create file.");
None
}
};
_file
},
_ => None
};

SdManager {
sd_controller: sd_cont,
sd_controller,
volume,
root_directory,
file: Some(file),
file,
}
}

pub fn write(
&mut self,
file: &mut sd::File,
buffer: &[u8],
) -> Result<usize, sd::Error<sd::SdMmcError>> {
self.sd_controller.write(&mut self.volume, file, buffer)
if !self.is_mounted() {
return Err(sd::Error::DeviceError(sd::SdMmcError::CardNotFound));
}
self.sd_controller.write(self.volume.as_mut().unwrap(), file, buffer)
}

pub fn write_str(
&mut self,
file: &mut sd::File,
msg: &str,
) -> Result<usize, sd::Error<sd::SdMmcError>> {
if !self.is_mounted() {
return Err(sd::Error::DeviceError(sd::SdMmcError::CardNotFound));
}
let buffer: &[u8] = msg.as_bytes();
self.sd_controller.write(&mut self.volume, file, buffer)
self.sd_controller.write(self.volume.as_mut().unwrap(), file, buffer)
}

pub fn open_file(&mut self, file_name: &str) -> Result<sd::File, sd::Error<sd::SdMmcError>> {
if !self.is_mounted() {
return Err(sd::Error::DeviceError(sd::SdMmcError::CardNotFound));
}
self.sd_controller.open_file_in_dir(
&mut self.volume,
&self.root_directory,
self.volume.as_mut().unwrap(),
self.root_directory.as_ref().unwrap(),
file_name,
sd::Mode::ReadWriteCreateOrTruncate,
)
}

pub fn close_current_file(&mut self) -> Result<(), sd::Error<sd::SdMmcError>> {
if !self.is_mounted() {
return Err(sd::Error::DeviceError(sd::SdMmcError::CardNotFound));
}
if let Some(file) = self.file.take() {
return self.close_file(file);
}
Ok(())
}

pub fn close_file(&mut self, file: sd::File) -> Result<(), sd::Error<sd::SdMmcError>> {
self.sd_controller.close_file(&self.volume, file)
if !self.is_mounted() {
return Err(sd::Error::DeviceError(sd::SdMmcError::CardNotFound));
}
self.sd_controller.close_file(self.volume.as_ref().unwrap(), file)
}
pub fn close(mut self) {
self.sd_controller
.close_dir(&self.volume, self.root_directory);

pub fn close(&mut self) -> Result<(), sd::Error<sd::SdMmcError>> {
if !self.is_mounted() {
return Err(sd::Error::DeviceError(sd::SdMmcError::CardNotFound));
}
self.sd_controller.close_dir(
self.volume.as_ref().unwrap(),
self.root_directory.unwrap()

Check failure on line 174 in libraries/common-arm/src/sd_manager.rs

View workflow job for this annotation

GitHub Actions / All

cannot move out of `self.root_directory` which is behind a mutable reference

Check failure on line 174 in libraries/common-arm/src/sd_manager.rs

View workflow job for this annotation

GitHub Actions / clippy

cannot move out of `self.root_directory` which is behind a mutable reference

error[E0507]: cannot move out of `self.root_directory` which is behind a mutable reference --> libraries/common-arm/src/sd_manager.rs:174:13 | 174 | self.root_directory.unwrap() | ^^^^^^^^^^^^^^^^^^^ -------- `self.root_directory` moved due to this method call | | | help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents | move occurs because `self.root_directory` has type `core::option::Option<embedded_sdmmc::Directory>`, which does not implement the `Copy` trait | note: `core::option::Option::<T>::unwrap` takes ownership of the receiver `self`, which moves `self.root_directory` --> /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/option.rs:928:25 help: you could `clone` the value and consume it, if the `embedded_sdmmc::Directory: core::clone::Clone` trait bound could be satisfied | 174 | <core::option::Option<embedded_sdmmc::Directory> as Clone>::clone(&self.root_directory).unwrap() | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +
);
Ok(())
}

pub fn is_mounted(&mut self) -> bool {
// Sd crate doesn't have a check method for SD card being still mounted
// Use `card_size_bytes()` as indicator if device is still connected or not
match self.sd_controller.device().card_size_bytes() {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NoahSprenger reading the card size is the best option I could find from the sd crate to check if the device is connected.

This method does get called for every health check there's likely a performance penalty.

Do we want another alternative for how to check sd is connected? Or maybe just instead of health checking we can only send an error message when the sd manager is called to read/write it.

Ok(size) => true,

Check warning on line 183 in libraries/common-arm/src/sd_manager.rs

View workflow job for this annotation

GitHub Actions / All

unused variable: `size`

Check warning on line 183 in libraries/common-arm/src/sd_manager.rs

View workflow job for this annotation

GitHub Actions / clippy

unused variable: `size`

warning: unused variable: `size` --> libraries/common-arm/src/sd_manager.rs:183:16 | 183 | Ok(size) => true, | ^^^^ help: if this is intentional, prefix it with an underscore: `_size` | = note: `#[warn(unused_variables)]` on by default
Err(_) => false,
}
}
}

Expand Down
1 change: 1 addition & 0 deletions libraries/messages/src/health.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ pub struct HealthStatus {
pub ext_v5: Option<u16>,
pub ext_3v3: Option<u16>,
pub failover_sense: Option<u16>,
pub sd_card: Option<bool>,
}

impl Health {
Expand Down
4 changes: 3 additions & 1 deletion libraries/messages/src/logging/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,14 @@ pub enum ErrorContext {
UnknownRadioMessage,
UnkownPostcardMessage,
NoRadioTransfer,
SDCardNotConnected,
}

display_context!(
[GroundStation, "Error sending ground station message"],
[UnkownCanMessage, "Unknown CAN message received"],
[UnknownRadioMessage, "Unknown radio message received"],
[NoRadioTransfer, "No radio transfer available"],
[UnkownPostcardMessage, "Unknown postcard message received"]
[UnkownPostcardMessage, "Unknown postcard message received"],
[SDCardNotConnected, "SD Card is not connected"]
);
Loading