Skip to content

Commit

Permalink
Adding support for a best effort interface wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
ryan-summers committed Nov 20, 2023
1 parent 2c391b6 commit 3ab4fff
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 31 deletions.
66 changes: 66 additions & 0 deletions serial-settings/src/interface.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/// Wrapper type for a "best effort" serial interface.
///
/// # Note
/// Overflows of the output are silently ignored.
pub struct BestEffortInterface<T>(T);

impl<T> BestEffortInterface<T>
where
T: embedded_io::Write
+ embedded_io::WriteReady
+ embedded_io::Read
+ embedded_io::ReadReady,
{
/// Construct an interface where overflows and errors when writing on the output are silently
/// ignored.
pub fn new(interface: T) -> Self {
Self(interface)
}

/// Get access to the inner (wrapped) interface
pub fn inner(&self) -> &T {
&self.0
}

/// Get mutable access to the inner (wrapped) interface
pub fn inner_mut(&mut self) -> &mut T {
&mut self.0
}
}

impl<T> core::fmt::Write for BestEffortInterface<T>
where
T: embedded_io::Write + embedded_io::WriteReady,
{
fn write_str(&mut self, s: &str) -> core::fmt::Result {
if let Ok(true) = self.0.write_ready() {
self.0.write(s.as_bytes()).ok();
}
Ok(())
}
}

impl<T> embedded_io::ErrorType for BestEffortInterface<T>
where
T: embedded_io::ErrorType,
{
type Error = <T as embedded_io::ErrorType>::Error;
}

impl<T> embedded_io::Read for BestEffortInterface<T>
where
T: embedded_io::Read,
{
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
self.0.read(buf)
}
}

impl<T> embedded_io::ReadReady for BestEffortInterface<T>
where
T: embedded_io::ReadReady,
{
fn read_ready(&mut self) -> Result<bool, Self::Error> {
self.0.read_ready()
}
}
35 changes: 10 additions & 25 deletions serial-settings/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,13 @@
#![no_std]

use core::fmt::Write;
use embedded_io::{Read, ReadReady, Write as EioWrite, WriteReady};
use embedded_io::{Read, ReadReady};
use miniconf::{JsonCoreSlash, TreeKey};

mod interface;

pub use interface::BestEffortInterface;

/// Specifies the API required for objects that are used as settings with the serial terminal
/// interface.
pub trait Settings: for<'a> JsonCoreSlash<'a> + Clone {
Expand All @@ -63,8 +67,7 @@ pub trait Platform: Sized {
/// This type specifies the interface to the user, for example, a USB CDC-ACM serial port.
type Interface: embedded_io::Read
+ embedded_io::ReadReady
+ embedded_io::Write
+ embedded_io::WriteReady;
+ core::fmt::Write;

/// Specifies the settings that are used on the device.
type Settings: Settings;
Expand Down Expand Up @@ -128,7 +131,7 @@ impl<'a, P: Platform> Context<'a, P> {
continue;
}
Ok(len) => write!(
Writer(&mut context.platform.interface_mut()),
&mut context.platform.interface_mut(),
"{path}: {}",
core::str::from_utf8(&context.buffer[..len])
.unwrap()
Expand All @@ -142,7 +145,7 @@ impl<'a, P: Platform> Context<'a, P> {
"[default serialization error: {e}]"
),
Ok(len) => writeln!(
Writer(&mut context.platform.interface_mut()),
&mut context.platform.interface_mut(),
" [default: {}]",
core::str::from_utf8(&context.buffer[..len])
.unwrap()
Expand Down Expand Up @@ -185,7 +188,7 @@ impl<'a, P: Platform> Context<'a, P> {
}
Ok(len) => {
writeln!(
Writer(&mut context.platform.interface_mut()),
&mut context.platform.interface_mut(),
"{key}: {}",
core::str::from_utf8(&context.buffer[..len]).unwrap()
)
Expand Down Expand Up @@ -295,26 +298,8 @@ impl<'a, P: Platform> Context<'a, P> {
}

impl<'a, P: Platform> core::fmt::Write for Context<'a, P> {
/// Write data to the serial terminal.
///
/// # Note
/// The terminal uses an internal buffer. Overflows of the output buffer are silently ignored.
fn write_str(&mut self, s: &str) -> core::fmt::Result {
let mut writer = Writer(self.platform.interface_mut());
writer.write_str(s)
}
}

struct Writer<'a, T: WriteReady + EioWrite>(&'a mut T);

impl<'a, T: embedded_io::WriteReady + embedded_io::Write> core::fmt::Write
for Writer<'a, T>
{
fn write_str(&mut self, s: &str) -> core::fmt::Result {
if let Ok(true) = self.0.write_ready() {
self.0.write(s.as_bytes()).ok();
}
Ok(())
self.platform.interface_mut().write_str(s)
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/bin/dual-iir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ mod app {
fn usb(mut c: usb::Context) {
// Handle the USB serial terminal.
c.shared.usb.lock(|usb| {
usb.poll(&mut [c.local.usb_terminal.interface_mut()]);
usb.poll(&mut [c.local.usb_terminal.interface_mut().inner_mut()]);
});

c.local.usb_terminal.process().unwrap();
Expand Down
2 changes: 1 addition & 1 deletion src/bin/lockin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ mod app {
fn usb(mut c: usb::Context) {
// Handle the USB serial terminal.
c.shared.usb.lock(|usb| {
usb.poll(&mut [c.local.usb_terminal.interface_mut()]);
usb.poll(&mut [c.local.usb_terminal.interface_mut().inner_mut()]);
});

c.local.usb_terminal.process().unwrap();
Expand Down
9 changes: 6 additions & 3 deletions src/hardware/flash.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use core::fmt::Write;
use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
use stm32h7xx_hal::flash::LockedFlashBank;
use usbd_serial::embedded_io::Write as EioWrite;

#[derive(Clone, serde::Serialize, serde::Deserialize, miniconf::Tree)]
pub struct Settings {
Expand Down Expand Up @@ -96,15 +95,19 @@ impl<F> From<postcard::Error> for Error<F> {

pub struct SerialSettingsPlatform {
/// The interface to read/write data to/from serially (via text) to the user.
pub interface: usbd_serial::SerialPort<'static, super::UsbBus>,
pub interface: serial_settings::BestEffortInterface<
usbd_serial::SerialPort<'static, super::UsbBus>,
>,
/// The Settings structure.
pub settings: Settings,
/// The storage mechanism used to persist settings to between boots.
pub storage: Flash,
}

impl serial_settings::Platform for SerialSettingsPlatform {
type Interface = usbd_serial::SerialPort<'static, super::UsbBus>;
type Interface = serial_settings::BestEffortInterface<
usbd_serial::SerialPort<'static, super::UsbBus>,
>;
type Settings = Settings;
type Error =
Error<<Flash as embedded_storage::nor_flash::ErrorType>::Error>;
Expand Down
4 changes: 3 additions & 1 deletion src/hardware/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1088,7 +1088,9 @@ pub fn setup(

serial_settings::Runner::new(
super::flash::SerialSettingsPlatform {
interface: usb_serial,
interface: serial_settings::BestEffortInterface::new(
usb_serial,
),
storage,
settings,
},
Expand Down

0 comments on commit 3ab4fff

Please sign in to comment.