tasks: implement a task for writing measurement straems to the SD card
This commit is contained in:
@@ -75,7 +75,7 @@ async fn main(spawner: Spawner) {
|
|||||||
static MOTION_BUS: StaticCell<Channel<CriticalSectionRawMutex,Measurement,5> > = StaticCell::new();
|
static MOTION_BUS: StaticCell<Channel<CriticalSectionRawMutex,Measurement,5> > = StaticCell::new();
|
||||||
let motion_bus = MOTION_BUS.init_with(|| { Channel::new() });
|
let motion_bus = MOTION_BUS.init_with(|| { Channel::new() });
|
||||||
|
|
||||||
static RECORDING_BUS: StaticCell<PubSubChannel<CriticalSectionRawMutex,Measurement,1, 1, 1> > = StaticCell::new();
|
static RECORDING_BUS: StaticCell<PubSubChannel<CriticalSectionRawMutex,Measurement,1, 2, 1> > = StaticCell::new();
|
||||||
let recording_bus = RECORDING_BUS.init_with(|| { PubSubChannel::new() });
|
let recording_bus = RECORDING_BUS.init_with(|| { PubSubChannel::new() });
|
||||||
|
|
||||||
info!("Setting up rendering pipeline");
|
info!("Setting up rendering pipeline");
|
||||||
@@ -114,6 +114,16 @@ async fn main(spawner: Spawner) {
|
|||||||
let mut io = esp_hal::gpio::Io::new(peripherals.IO_MUX);
|
let mut io = esp_hal::gpio::Io::new(peripherals.IO_MUX);
|
||||||
io.set_interrupt_handler(gpio_interrupt_handler);
|
io.set_interrupt_handler(gpio_interrupt_handler);
|
||||||
|
|
||||||
|
spawner.must_spawn(renderbug_bike::tasks::sd_card::sdcard_task(
|
||||||
|
peripherals.SPI3.degrade(),
|
||||||
|
peripherals.GPIO41.degrade(),
|
||||||
|
peripherals.GPIO2.degrade(),
|
||||||
|
peripherals.GPIO1.degrade(),
|
||||||
|
Output::new(peripherals.GPIO40.degrade(), esp_hal::gpio::Level::High, OutputConfig::default()),
|
||||||
|
sd_detect_interrupt,
|
||||||
|
recording_bus.dyn_subscriber().unwrap()
|
||||||
|
));
|
||||||
|
|
||||||
#[cfg(feature="i2c")]
|
#[cfg(feature="i2c")]
|
||||||
{
|
{
|
||||||
use embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice;
|
use embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice;
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ pub mod oled_render;
|
|||||||
|
|
||||||
pub mod usb_power;
|
pub mod usb_power;
|
||||||
|
|
||||||
|
pub mod sd_card;
|
||||||
|
|
||||||
// Prediction engines
|
// Prediction engines
|
||||||
pub mod motion;
|
pub mod motion;
|
||||||
|
|
||||||
|
|||||||
142
src/tasks/sd_card.rs
Normal file
142
src/tasks/sd_card.rs
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
use core::cell::RefCell;
|
||||||
|
|
||||||
|
use embassy_sync::pubsub::DynSubscriber;
|
||||||
|
use embedded_hal_bus::spi::RefCellDevice;
|
||||||
|
use embedded_sdmmc::{Mode, SdCard, TimeSource, VolumeIdx, VolumeManager};
|
||||||
|
use esp_hal::{delay::Delay, gpio::{AnyPin, Level, Output}, spi::{master::{AnySpi, Config, Spi}}, time::Rate};
|
||||||
|
use rmp::encode::{RmpWrite, RmpWriteErr};
|
||||||
|
|
||||||
|
use crate::{events::Measurement, gpio_interrupt::PinInterrupt, simdata::IMUReading, storage::SimDataRecorder};
|
||||||
|
use log::*;
|
||||||
|
|
||||||
|
struct SdTimeSource;
|
||||||
|
|
||||||
|
// TODSO: We need a whole time keeping stack that can sync from GPS or NTP
|
||||||
|
impl TimeSource for SdTimeSource {
|
||||||
|
fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {
|
||||||
|
embedded_sdmmc::Timestamp::from_fat(0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::single_match)]
|
||||||
|
#[embassy_executor::task]
|
||||||
|
pub async fn sdcard_task(
|
||||||
|
spi: AnySpi<'static>,
|
||||||
|
sck: AnyPin<'static>,
|
||||||
|
mosi: AnyPin<'static>,
|
||||||
|
miso: AnyPin<'static>,
|
||||||
|
cs: Output<'static>,
|
||||||
|
sd_detect_interrupt: PinInterrupt<'static>,
|
||||||
|
mut recording_stream: DynSubscriber<'static, Measurement>
|
||||||
|
) {
|
||||||
|
|
||||||
|
let sd_spi = Spi::new(spi, Config::default().with_frequency(Rate::from_khz(400)).with_mode(esp_hal::spi::Mode::_0))
|
||||||
|
.unwrap()
|
||||||
|
.with_sck(sck)
|
||||||
|
.with_mosi(mosi)
|
||||||
|
.with_miso(miso)
|
||||||
|
.into_async();
|
||||||
|
|
||||||
|
let sd_ref = RefCell::new(sd_spi);
|
||||||
|
let sd_device = RefCellDevice::new(&sd_ref, cs, Delay::new()).unwrap();
|
||||||
|
let mut sd_card = SdCard::new(sd_device, Delay::new());
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match sd_detect_interrupt.wait_for_interrupt().await {
|
||||||
|
Level::High => {
|
||||||
|
info!("SD card inserted!");
|
||||||
|
sd_detect_interrupt.listen();
|
||||||
|
},
|
||||||
|
Level::Low => {
|
||||||
|
info!("SD card removed!");
|
||||||
|
sd_detect_interrupt.listen();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sd_card.mark_card_uninit();
|
||||||
|
|
||||||
|
|
||||||
|
match sd_card.get_card_type() {
|
||||||
|
Some(card_type) => info!("SD card detected: {:?}", card_type),
|
||||||
|
None => {
|
||||||
|
error!("Failed to determine SD card type!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let volumes = VolumeManager::new(sd_card, SdTimeSource);
|
||||||
|
let vol = volumes.open_volume(VolumeIdx(0)).unwrap();
|
||||||
|
let rootfs = vol.open_root_dir().unwrap();
|
||||||
|
|
||||||
|
let stream = rootfs.open_file_in_dir("renderbug-sensors.rmp",Mode::ReadWriteCreateOrTruncate).unwrap();
|
||||||
|
let rmp_stream = EmbeddedRmp(stream);
|
||||||
|
|
||||||
|
let mut recorder = SimDataRecorder::new(EmbeddedRmp(rmp_stream));
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// FIXME: THis chunk could really go into an impl From<Measurement> for EventRecord
|
||||||
|
match recording_stream.next_message_pure().await {
|
||||||
|
Measurement::IMU { accel, gyro } => {
|
||||||
|
let reading = IMUReading {
|
||||||
|
accel_x: accel.x as f64,
|
||||||
|
accel_y: accel.y as f64,
|
||||||
|
accel_z: accel.z as f64,
|
||||||
|
gyro_x: gyro.x as f64,
|
||||||
|
gyro_y: gyro.y as f64,
|
||||||
|
gyro_z: gyro.z as f64
|
||||||
|
};
|
||||||
|
if recorder.write_next(reading).is_err() {
|
||||||
|
error!("Failed to write IMU reading to SD card");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
info!("Wrote IMU to SD card");
|
||||||
|
},
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(recorder);
|
||||||
|
drop(rootfs);
|
||||||
|
drop(vol);
|
||||||
|
|
||||||
|
sd_card = volumes.free().0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EmbeddedRmp<W: embedded_io::Write>(W);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct EmbeddedRmpError<T>(T);
|
||||||
|
|
||||||
|
impl<T: core::fmt::Debug + 'static> RmpWriteErr for EmbeddedRmpError<T> {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> core::fmt::Display for EmbeddedRmpError<T> where T: core::fmt::Debug {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: embedded_io::Write> RmpWrite for EmbeddedRmp<W> where W::Error: core::fmt::Debug + 'static {
|
||||||
|
type Error = EmbeddedRmpError<W::Error>;
|
||||||
|
|
||||||
|
fn write_bytes(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
|
||||||
|
self.0.write(buf).map_err(|e| { EmbeddedRmpError(e)})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: embedded_io::Write> embedded_io::ErrorType for EmbeddedRmp<W> {
|
||||||
|
type Error = W::Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: embedded_io::Write> embedded_io::Write for EmbeddedRmp<W> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||||
|
self.0.write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.0.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user