From 2de2898ba4cc635b5cb7aa1372b5856824e1e797 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 12 Mar 2026 15:54:51 +0100 Subject: [PATCH] re-work ACS --- satrs-example/models/src/lib.rs | 7 ++ satrs-example/models/src/mgm.rs | 7 ++ satrs-example/src/acs/mgm.rs | 175 +++++++++++++------------- satrs-example/src/acs/mgm_assembly.rs | 42 +++++-- satrs-example/src/lib.rs | 61 ++++++++- satrs-example/src/main.rs | 42 +++---- satrs-example/src/spi.rs | 6 - 7 files changed, 216 insertions(+), 124 deletions(-) delete mode 100644 satrs-example/src/spi.rs diff --git a/satrs-example/models/src/lib.rs b/satrs-example/models/src/lib.rs index 3206a3a..02707f1 100644 --- a/satrs-example/models/src/lib.rs +++ b/satrs-example/models/src/lib.rs @@ -152,11 +152,18 @@ pub trait Message { fn message_type(&self) -> MessageType; } +/// Generic device mode which covers the requirements of most devices. +/// +/// The states are related both to the physical and the logical state of the device. Some +/// device handlers control the power supply of their own device and an off state might also +/// mean that the device is physically off. #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Copy, Clone)] pub enum DeviceMode { Off = 0, On = 1, + /// Normal operation mode where periodic polling might be done as well. Normal = 2, + Unknown = 3, } #[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] diff --git a/satrs-example/models/src/mgm.rs b/satrs-example/models/src/mgm.rs index 3bd88fb..ad1adfc 100644 --- a/satrs-example/models/src/mgm.rs +++ b/satrs-example/models/src/mgm.rs @@ -51,10 +51,16 @@ pub mod response { MgmData(MgmData), } + #[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] + pub enum ModeFailure { + Timeout, + } + #[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] pub enum Response { Ok, Hk(HkResponse), + ModeFailure(ModeFailure), } impl Response { @@ -62,6 +68,7 @@ pub mod response { match self { Response::Ok => crate::MessageType::Verification, Response::Hk(_hk_response) => crate::MessageType::Hk, + Response::ModeFailure(_mode_failure) => crate::MessageType::Mode, } } } diff --git a/satrs-example/src/acs/mgm.rs b/satrs-example/src/acs/mgm.rs index a1e6cab..2d4fb3b 100644 --- a/satrs-example/src/acs/mgm.rs +++ b/satrs-example/src/acs/mgm.rs @@ -1,25 +1,23 @@ use models::ccsds::{CcsdsTcPacketOwned, CcsdsTmPacketOwned}; +use models::mgm::response::ModeFailure; use models::mgm::MgmData; use models::pcdu::SwitchId; use models::{mgm, ComponentId, DeviceMode, HkRequestType}; use satrs::spacepackets::CcsdsPacketIdAndPsc; -use satrs_example::{HkHelperSingleSet, TimestampHelper}; +use satrs_example::{HkHelperSingleSet, ModeHelper, TimestampHelper, TransitionState}; use satrs_minisim::acs::lis3mdl::{ MgmLis3MdlReply, MgmLis3RawValues, FIELD_LSB_PER_GAUSS_4_SENS, GAUSS_TO_MICROTESLA_FACTOR, }; use satrs_minisim::acs::MgmRequestLis3Mdl; use satrs_minisim::{SerializableSimMsgPayload, SimReply, SimRequest}; -use std::fmt::Debug; -use std::sync::mpsc::{self}; +use std::sync::mpsc; use std::sync::{Arc, Mutex}; use std::time::Duration; use satrs::request::MessageMetadata; -use crate::acs::mgm_assembly; use crate::ccsds::pack_ccsds_tm_packet_for_now; use crate::eps::PowerSwitchHelper; -use crate::spi::SpiInterface; pub const NR_OF_DATA_AND_CFG_REGISTERS: usize = 14; @@ -28,12 +26,38 @@ pub const X_LOWBYTE_IDX: usize = 9; pub const Y_LOWBYTE_IDX: usize = 11; pub const Z_LOWBYTE_IDX: usize = 13; -#[derive(Default, Debug, PartialEq, Eq)] -pub enum TransitionState { - #[default] - Idle, - PowerSwitching, - Done, +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum MgmId { + _0, + _1, +} + +impl MgmId { + pub const fn str(&self) -> &str { + match self { + MgmId::_0 => "MGM 0", + MgmId::_1 => "MGM 1", + } + } + + #[inline] + pub const fn component_id(&self) -> ComponentId { + match self { + MgmId::_0 => ComponentId::AcsMgm0, + MgmId::_1 => ComponentId::AcsMgm1, + } + } +} + +pub enum ModeRequest { + SetMode(DeviceMode), + ReadMode, +} + +pub enum ModeReport { + Mode(DeviceMode), + /// Setting a mode timed out. + SetModeTimeout, } #[derive(Default)] @@ -41,14 +65,11 @@ pub struct SpiDummyInterface { pub dummy_values: MgmLis3RawValues, } -impl SpiInterface for SpiDummyInterface { - type Error = (); - - fn transfer(&mut self, _tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error> { +impl SpiDummyInterface { + fn transfer(&mut self, _tx: &[u8], rx: &mut [u8]) { rx[X_LOWBYTE_IDX..X_LOWBYTE_IDX + 2].copy_from_slice(&self.dummy_values.x.to_le_bytes()); rx[Y_LOWBYTE_IDX..Y_LOWBYTE_IDX + 2].copy_from_slice(&self.dummy_values.y.to_be_bytes()); rx[Z_LOWBYTE_IDX..Z_LOWBYTE_IDX + 2].copy_from_slice(&self.dummy_values.z.to_be_bytes()); - Ok(()) } } @@ -57,11 +78,9 @@ pub struct SpiSimInterface { pub sim_reply_rx: mpsc::Receiver, } -impl SpiInterface for SpiSimInterface { - type Error = (); - +impl SpiSimInterface { // Right now, we only support requesting sensor data and not configuration of the sensor. - fn transfer(&mut self, _tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error> { + fn transfer(&mut self, _tx: &[u8], rx: &mut [u8]) { let mgm_sensor_request = MgmRequestLis3Mdl::RequestSensorData; if let Err(e) = self .sim_request_tx @@ -84,22 +103,19 @@ impl SpiInterface for SpiSimInterface { log::warn!("MGM LIS3 SIM reply timeout: {e}"); } } - Ok(()) } } -pub enum SpiSimInterfaceWrapper { +pub enum SpiCommunication { Dummy(SpiDummyInterface), Sim(SpiSimInterface), } -impl SpiInterface for SpiSimInterfaceWrapper { - type Error = (); - - fn transfer(&mut self, tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error> { +impl SpiCommunication { + fn transfer(&mut self, tx: &[u8], rx: &mut [u8]) { match self { - SpiSimInterfaceWrapper::Dummy(dummy) => dummy.transfer(tx, rx), - SpiSimInterfaceWrapper::Sim(sim_if) => sim_if.transfer(tx, rx), + SpiCommunication::Dummy(dummy) => dummy.transfer(tx, rx), + SpiCommunication::Sim(sim_if) => sim_if.transfer(tx, rx), } } } @@ -110,67 +126,45 @@ pub struct BufWrapper { rx_buf: [u8; 32], } -pub struct ModeHelpers { - current: DeviceMode, - target: Option, - tc_id: Option, - transition_state: TransitionState, -} - -impl Default for ModeHelpers { - fn default() -> Self { - Self { - current: DeviceMode::Off, - target: Default::default(), - tc_id: Default::default(), - transition_state: Default::default(), - } - } -} - /// Helper component for communication with a parent component, which is usually as assembly. pub struct ModeLeafHelper { - pub request_rx: mpsc::Receiver, - pub report_tx: mpsc::SyncSender, + pub request_rx: mpsc::Receiver, + pub report_tx: mpsc::SyncSender, } /// Example MGM device handler strongly based on the LIS3MDL MEMS device. -pub struct MgmHandlerLis3Mdl { - id: ComponentId, - dev_str: &'static str, +pub struct MgmHandlerLis3Mdl { + id: MgmId, tc_rx: mpsc::Receiver, tm_tx: mpsc::SyncSender, switch_helper: PowerSwitchHelper, - pub com_interface: ComInterface, + pub com_interface: SpiCommunication, shared_mgm_set: Arc>, buffers: BufWrapper, stamp_helper: TimestampHelper, hk_helper: HkHelperSingleSet, - mode_helpers: ModeHelpers, + mode_helpers: ModeHelper, mode_leaf_helper: ModeLeafHelper, } -impl MgmHandlerLis3Mdl { - #[allow(clippy::too_many_arguments)] +impl MgmHandlerLis3Mdl { pub fn new( - id: ComponentId, - dev_str: &'static str, + id: MgmId, tc_rx: mpsc::Receiver, tm_tx: mpsc::SyncSender, switch_helper: PowerSwitchHelper, - com_interface: ComInterface, + com_interface: SpiCommunication, shared_mgm_set: Arc>, mode_leaf_helper: ModeLeafHelper, ) -> Self { Self { id, - dev_str, tc_rx, tm_tx, switch_helper, com_interface, shared_mgm_set, - mode_helpers: ModeHelpers::default(), + mode_helpers: ModeHelper::new(DeviceMode::Off, Duration::from_millis(200)), buffers: BufWrapper::default(), stamp_helper: TimestampHelper::default(), hk_helper: HkHelperSingleSet::new(false, Duration::from_millis(200)), @@ -186,8 +180,8 @@ impl MgmHandlerLis3Mdl { #[inline] pub fn switch_id(&self) -> SwitchId { match self.id { - ComponentId::AcsMgm0 => SwitchId::Mgm0, - ComponentId::AcsMgm1 => SwitchId::Mgm1, + MgmId::_0 => SwitchId::Mgm0, + MgmId::_1 => SwitchId::Mgm1, _ => panic!("unexpected component id"), } } @@ -207,7 +201,7 @@ impl MgmHandlerLis3Mdl { // Poll sensor before checking and generating HK. if self.mode() == DeviceMode::Normal { - log::trace!("polling LIS3MDL sensor {}", self.dev_str); + log::trace!("polling LIS3MDL sensor {}", self.id.str()); self.poll_sensor(); } @@ -261,10 +255,8 @@ impl MgmHandlerLis3Mdl { loop { match self.mode_leaf_helper.request_rx.try_recv() { Ok(request) => match request { - mgm_assembly::ModeRequest::SetMode(device_mode) => { - self.start_transition(device_mode, false) - } - mgm_assembly::ModeRequest::ReadMode => self.report_mode_to_parent(), + ModeRequest::SetMode(device_mode) => self.start_transition(device_mode, false), + ModeRequest::ReadMode => self.report_mode_to_parent(), }, Err(e) => match e { std::sync::mpsc::TryRecvError::Empty => break, @@ -281,7 +273,7 @@ impl MgmHandlerLis3Mdl { tc_id: Option, response: mgm::response::Response, ) { - match pack_ccsds_tm_packet_for_now(self.id, tc_id, &response) { + match pack_ccsds_tm_packet_for_now(self.id.component_id(), tc_id, &response) { Ok(packet) => { if let Err(e) = self.tm_tx.send(packet) { log::warn!("failed to send TM packet: {}", e); @@ -358,12 +350,11 @@ impl MgmHandlerLis3Mdl { } fn start_transition(&mut self, target_mode: DeviceMode, _forced: bool) { - log::info!("{}: transitioning to mode {:?}", self.dev_str, target_mode); + log::info!("{}: transitioning to mode {:?}", self.id.str(), target_mode); if target_mode == DeviceMode::Off { self.shared_mgm_set.lock().unwrap().valid = false; } - self.mode_helpers.transition_state = TransitionState::Idle; - self.mode_helpers.target = Some(target_mode); + self.mode_helpers.start(target_mode); } pub fn handle_mode_transition(&mut self) { @@ -373,7 +364,6 @@ impl MgmHandlerLis3Mdl { let target_mode = self.mode_helpers.target.unwrap(); if target_mode == DeviceMode::On || target_mode == DeviceMode::Normal { if self.mode_helpers.transition_state == TransitionState::Idle { - // TODO: Switch ID for MGM1.. let result = self .switch_helper .send_switch_on_cmd(MessageMetadata::new(0, self.id as u32), self.switch_id()); @@ -383,24 +373,38 @@ impl MgmHandlerLis3Mdl { } self.mode_helpers.transition_state = TransitionState::PowerSwitching; } - if self.mode_helpers.transition_state == TransitionState::PowerSwitching - && self.switch_helper.is_switch_on(self.switch_id()) - { - self.mode_helpers.transition_state = TransitionState::Done; + if self.mode_helpers.transition_state == TransitionState::PowerSwitching { + if self.switch_helper.is_switch_on(self.switch_id()) { + self.mode_helpers.transition_state = TransitionState::Done; + } else if self.mode_helpers.timed_out() { + self.handle_mode_transition_failure(); + } } if self.mode_helpers.transition_state == TransitionState::Done { - self.mode_helpers.current = self.mode_helpers.target.unwrap(); self.handle_mode_reached(); - self.mode_helpers.transition_state = TransitionState::Idle; } } } + fn handle_mode_transition_failure(&mut self) { + self.mode_helpers.finish(false); + if let Some(requestor) = self.mode_helpers.tc_id { + self.send_telemetry( + Some(requestor), + mgm::response::Response::ModeFailure(ModeFailure::Timeout), + ); + } + self.mode_leaf_helper + .report_tx + .send(ModeReport::SetModeTimeout) + .unwrap(); + } + fn handle_mode_reached(&mut self) { - self.mode_helpers.target = None; + self.mode_helpers.finish(true); log::info!( "{} announcing mode: {:?}", - self.dev_str, + self.id.str(), self.mode_helpers.current ); if let Some(requestor) = self.mode_helpers.tc_id { @@ -413,7 +417,7 @@ impl MgmHandlerLis3Mdl { fn report_mode_to_parent(&self) { self.mode_leaf_helper .report_tx - .send(mgm_assembly::ModeReport::Mode(self.mode_helpers.current)) + .send(ModeReport::Mode(self.mode_helpers.current)) .unwrap(); } @@ -474,7 +478,7 @@ mod tests { pub next_mgm_data: MgmLis3RawValues, } - impl SpiInterface for TestSpiInterface { + impl SpiCommunication for TestSpiInterface { type Error = (); fn transfer(&mut self, _tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error> { @@ -491,8 +495,8 @@ mod tests { #[allow(dead_code)] pub struct MgmTestbench { - pub assembly_mode_request_tx: mpsc::SyncSender, - pub mode_report_rx: mpsc::Receiver, + pub assembly_mode_request_tx: mpsc::SyncSender, + pub mode_report_rx: mpsc::Receiver, pub shared_switch_set: SharedSwitchSet, pub tc_tx: mpsc::SyncSender, pub tm_rx: mpsc::Receiver, @@ -517,8 +521,7 @@ mod tests { let switch_map = SwitchSet::new(switch_map); let shared_switch_set = SharedSwitchSet::new(Mutex::new(switch_map)); let handler = MgmHandlerLis3Mdl::new( - ComponentId::AcsMgm0, - "TEST_MGM", + MgmId::_0, tc_rx, tm_tx, PowerSwitchHelper::new(switcher_tx, shared_switch_set.clone()), diff --git a/satrs-example/src/acs/mgm_assembly.rs b/satrs-example/src/acs/mgm_assembly.rs index 672f742..77cd5c6 100644 --- a/satrs-example/src/acs/mgm_assembly.rs +++ b/satrs-example/src/acs/mgm_assembly.rs @@ -2,9 +2,10 @@ // TODO: Remove dead_code lint as soon as assembly is done. #![allow(dead_code)] -use std::sync::mpsc; +use std::{sync::mpsc, time::Duration}; use models::DeviceMode; +use satrs_example::ModeHelper; pub enum ModeRequest { SetMode(DeviceMode), @@ -13,30 +14,51 @@ pub enum ModeRequest { pub enum ModeReport { Mode(DeviceMode), + /// Setting a mode timed out. + SetModeTimeout, + /// An assembly child lost the mode. + ChildLostMode, +} + +pub struct ParentQueueHelper { + pub request_rx: mpsc::Receiver, + pub report_tx: mpsc::SyncSender, } /// Helper component for communication with a parent component, which is usually as assembly. -pub struct QueueHelper { - pub request_tx_queues: [mpsc::SyncSender; 2], - pub report_rx_queues: [mpsc::Receiver; 2], +pub struct ChildrenQueueHelper { + pub request_tx_queues: [mpsc::SyncSender; 2], + pub report_rx_queues: [mpsc::Receiver; 2], } +/// MGM assembly component. pub struct Assembly { - pub(crate) helper: QueueHelper, + mode_helper: ModeHelper, + parent_queues: ParentQueueHelper, + pub(crate) children_queues: ChildrenQueueHelper, } impl Assembly { - pub fn periodic_operation(&mut self) { - self.handle_mode_queue(); + pub fn new(parent_queues: ParentQueueHelper, children_queues: ChildrenQueueHelper) -> Self { + Self { + mode_helper: ModeHelper::new(DeviceMode::Unknown, Duration::from_millis(200)), + parent_queues, + children_queues, + } } - pub fn handle_mode_queue(&mut self) { - for rx in &mut self.helper.report_rx_queues { + pub fn periodic_operation(&mut self) { + self.handle_children_mode_queues(); + } + + pub fn handle_children_mode_queues(&mut self) { + for rx in &mut self.children_queues.report_rx_queues { loop { match rx.try_recv() { // TODO: Do something with the report. Ok(report) => match report { - ModeReport::Mode(_device_mode) => (), + super::mgm::ModeReport::Mode(_device_mode) => todo!(), + super::mgm::ModeReport::SetModeTimeout => todo!(), }, Err(e) => match e { mpsc::TryRecvError::Empty => break, diff --git a/satrs-example/src/lib.rs b/satrs-example/src/lib.rs index 6967898..06aef60 100644 --- a/satrs-example/src/lib.rs +++ b/satrs-example/src/lib.rs @@ -3,7 +3,7 @@ extern crate alloc; use std::time::{Duration, Instant}; pub use models::ComponentId; -use satrs::spacepackets::time::cds::CdsTime; +use satrs::spacepackets::{time::cds::CdsTime, CcsdsPacketIdAndPsc}; pub mod config; @@ -90,3 +90,62 @@ impl HkHelperSingleSet { false } } + +#[derive(Default, Debug, PartialEq, Eq)] +pub enum TransitionState { + #[default] + Idle, + PowerSwitching, + Done, +} + +#[derive(Debug)] +pub struct ModeHelper { + pub current: Mode, + pub target: Option, + pub tc_id: Option, + pub transition_start: Option, + pub timeout: Duration, + pub transition_state: TransitionState, +} + +impl ModeHelper { + pub fn new(init_mode: Mode, timeout: Duration) -> Self { + Self { + current: init_mode, + target: Default::default(), + tc_id: Default::default(), + transition_start: None, + timeout, + transition_state: Default::default(), + } + } + + pub fn start(&mut self, target: Mode) { + self.target = Some(target); + self.transition_start = Some(Instant::now()); + self.transition_state = TransitionState::Idle; + } + + pub fn timed_out(&self) -> bool { + if self.target.is_none() { + return false; + } + if let Some(transition_start) = self.transition_start { + return Instant::now() - transition_start >= self.timeout; + } + false + } + + pub fn finish(&mut self, success: bool) { + if self.target.is_none() { + return; + } + if success { + self.current = self.target.take().unwrap(); + } else { + self.target = None; + } + self.transition_start = None; + } +} diff --git a/satrs-example/src/main.rs b/satrs-example/src/main.rs index 94be517..bedec10 100644 --- a/satrs-example/src/main.rs +++ b/satrs-example/src/main.rs @@ -33,12 +33,7 @@ use tmtc::sender::TmTcSender; use tmtc::{tc_source::TcSourceTask, tm_sink::TmSink}; use crate::{ - acs::{ - mgm::{ - self, MgmHandlerLis3Mdl, SpiDummyInterface, SpiSimInterface, SpiSimInterfaceWrapper, - }, - mgm_assembly, - }, + acs::{mgm, mgm_assembly}, control::Controller, eps::pcdu::SwitchSet, event_manager::EventManager, @@ -53,7 +48,6 @@ mod eps; mod event_manager; mod interface; mod logger; -mod spi; mod tmtc; fn main() { @@ -75,7 +69,11 @@ fn main() { let (pcdu_handler_tc_tx, pcdu_handler_tc_rx) = mpsc::sync_channel(30); let (controller_tc_tx, controller_tc_rx) = mpsc::sync_channel(10); - // These message handles need to go into the MGM assembly. + // These message handles need to go into the MGM assembly and ACS subsystem. + let (_mgm_assembly_request_tx, mgm_assembly_request_rx) = mpsc::sync_channel(5); + let (mgm_assembly_report_tx, _mgm_assembly_report_rx) = mpsc::sync_channel(5); + + // These message handles need to go into the MGM assembly and MGM devices. let (mgm_0_mode_request_tx, mgm_0_mode_request_rx) = mpsc::sync_channel(5); let (mgm_1_mode_request_tx, mgm_1_mode_request_rx) = mpsc::sync_channel(5); let (mgm_0_mode_report_tx, mgm_0_mode_report_rx) = mpsc::sync_channel(5); @@ -147,24 +145,23 @@ fn main() { sim_client .add_reply_recipient(satrs_minisim::SimComponent::Mgm1Lis3Mdl, mgm_1_sim_reply_tx); ( - SpiSimInterfaceWrapper::Sim(SpiSimInterface { + mgm::SpiInterface::Sim(mgm::SpiSimInterface { sim_request_tx: sim_request_tx.clone(), sim_reply_rx: mgm_0_sim_reply_rx, }), - SpiSimInterfaceWrapper::Sim(SpiSimInterface { + mgm::SpiInterface::Sim(mgm::SpiSimInterface { sim_request_tx: sim_request_tx.clone(), sim_reply_rx: mgm_1_sim_reply_rx, }), ) } else { ( - SpiSimInterfaceWrapper::Dummy(SpiDummyInterface::default()), - SpiSimInterfaceWrapper::Dummy(SpiDummyInterface::default()), + mgm::SpiInterface::Dummy(mgm::SpiDummyInterface::default()), + mgm::SpiInterface::Dummy(mgm::SpiDummyInterface::default()), ) }; - let mut mgm_0_handler = MgmHandlerLis3Mdl::new( - ComponentId::AcsMgm0, - "MGM_0", + let mut mgm_0_handler = mgm::MgmHandlerLis3Mdl::new( + mgm::MgmId::_0, mgm_0_handler_tc_rx, tm_sink_tx.clone(), switch_helper.clone(), @@ -175,9 +172,8 @@ fn main() { report_tx: mgm_0_mode_report_tx, }, ); - let mut mgm_1_handler = MgmHandlerLis3Mdl::new( - ComponentId::AcsMgm1, - "MGM_1", + let mut mgm_1_handler = mgm::MgmHandlerLis3Mdl::new( + mgm::MgmId::_1, mgm_1_handler_tc_rx, tm_sink_tx.clone(), switch_helper.clone(), @@ -188,12 +184,16 @@ fn main() { report_tx: mgm_1_mode_report_tx, }, ); - let mut mgm_assembly = mgm_assembly::Assembly { - helper: mgm_assembly::QueueHelper { + let mut mgm_assembly = mgm_assembly::Assembly::new( + mgm_assembly::ParentQueueHelper { + request_rx: mgm_assembly_request_rx, + report_tx: mgm_assembly_report_tx, + }, + mgm_assembly::ChildrenQueueHelper { request_tx_queues: [mgm_0_mode_request_tx, mgm_1_mode_request_tx], report_rx_queues: [mgm_0_mode_report_rx, mgm_1_mode_report_rx], }, - }; + ); let pcdu_serial_interface = if let Some(sim_client) = opt_sim_client.as_mut() { sim_client.add_reply_recipient(satrs_minisim::SimComponent::Pcdu, pcdu_sim_reply_tx); diff --git a/satrs-example/src/spi.rs b/satrs-example/src/spi.rs deleted file mode 100644 index 165e6da..0000000 --- a/satrs-example/src/spi.rs +++ /dev/null @@ -1,6 +0,0 @@ -use core::fmt::Debug; - -pub trait SpiInterface { - type Error: Debug; - fn transfer(&mut self, tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error>; -}