feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
+27
@@ -0,0 +1,27 @@
|
||||
//! Digital I/O
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
|
||||
// Deprecated / infallible traits
|
||||
#[deprecated(
|
||||
since = "0.2.2",
|
||||
note = "Deprecated because the methods cannot return errors. \
|
||||
Users should use the traits in digital::v2."
|
||||
)]
|
||||
pub mod v1;
|
||||
|
||||
// New / fallible traits
|
||||
pub mod v2;
|
||||
|
||||
// v2 -> v1 compatibility wrappers
|
||||
// These require explicit casts from v2 -> v1
|
||||
pub mod v1_compat;
|
||||
|
||||
// v1 -> v2 compatibility shims
|
||||
// These are implicit over v1 implementations
|
||||
pub mod v2_compat;
|
||||
|
||||
// Re-export old traits so this isn't a breaking change
|
||||
#[allow(deprecated)]
|
||||
pub use self::v1::*;
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
//! Digital I/O
|
||||
//!
|
||||
//! The traits in this module are now deprecated. Please use the new versions included
|
||||
//! in `digital::v2`.
|
||||
|
||||
#![allow(deprecated)]
|
||||
|
||||
/// Single digital push-pull output pin
|
||||
///
|
||||
/// *This version of the trait is now deprecated. Please use the new `OutputPin` trait in
|
||||
/// `digital::v2::OutputPin`*.
|
||||
|
||||
pub trait OutputPin {
|
||||
/// Drives the pin low
|
||||
///
|
||||
/// *NOTE* the actual electrical state of the pin may not actually be low, e.g. due to external
|
||||
/// electrical sources
|
||||
fn set_low(&mut self);
|
||||
|
||||
/// Drives the pin high
|
||||
///
|
||||
/// *NOTE* the actual electrical state of the pin may not actually be high, e.g. due to external
|
||||
/// electrical sources
|
||||
fn set_high(&mut self);
|
||||
}
|
||||
|
||||
/// Push-pull output pin that can read its output state
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
///
|
||||
/// *This version of the trait is now deprecated. Please use the new `StatefulOutputPin` trait in
|
||||
/// `digital::v2::StatefulOutputPin`*.
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait StatefulOutputPin {
|
||||
/// Is the pin in drive high mode?
|
||||
///
|
||||
/// *NOTE* this does *not* read the electrical state of the pin
|
||||
fn is_set_high(&self) -> bool;
|
||||
|
||||
/// Is the pin in drive low mode?
|
||||
///
|
||||
/// *NOTE* this does *not* read the electrical state of the pin
|
||||
fn is_set_low(&self) -> bool;
|
||||
}
|
||||
|
||||
/// Output pin that can be toggled
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
///
|
||||
/// *This version of the trait is now deprecated. Please use the new `ToggleableOutputPin`
|
||||
/// trait in `digital::v2::ToggleableOutputPin`*.
|
||||
///
|
||||
/// See [toggleable](toggleable) to use a software implementation if
|
||||
/// both [OutputPin](trait.OutputPin.html) and
|
||||
/// [StatefulOutputPin](trait.StatefulOutputPin.html) are
|
||||
/// implemented. Otherwise, implement this using hardware mechanisms.
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait ToggleableOutputPin {
|
||||
/// Toggle pin output.
|
||||
fn toggle(&mut self);
|
||||
}
|
||||
|
||||
/// If you can read **and** write the output state, a pin is
|
||||
/// toggleable by software.
|
||||
///
|
||||
/// *This version of the module is now deprecated. Please use the new `toggleable` module in
|
||||
/// `digital::v2::toggleable`*.
|
||||
///
|
||||
/// ```
|
||||
/// use embedded_hal::digital::{OutputPin, StatefulOutputPin, ToggleableOutputPin};
|
||||
/// use embedded_hal::digital::toggleable;
|
||||
///
|
||||
/// /// A virtual output pin that exists purely in software
|
||||
/// struct MyPin {
|
||||
/// state: bool
|
||||
/// }
|
||||
///
|
||||
/// impl OutputPin for MyPin {
|
||||
/// fn set_low(&mut self) {
|
||||
/// self.state = false;
|
||||
/// }
|
||||
/// fn set_high(&mut self) {
|
||||
/// self.state = true;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl StatefulOutputPin for MyPin {
|
||||
/// fn is_set_low(&self) -> bool {
|
||||
/// !self.state
|
||||
/// }
|
||||
/// fn is_set_high(&self) -> bool {
|
||||
/// self.state
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// /// Opt-in to the software implementation.
|
||||
/// impl toggleable::Default for MyPin {}
|
||||
///
|
||||
/// let mut pin = MyPin { state: false };
|
||||
/// pin.toggle();
|
||||
/// assert!(pin.is_set_high());
|
||||
/// pin.toggle();
|
||||
/// assert!(pin.is_set_low());
|
||||
/// ```
|
||||
#[cfg(feature = "unproven")]
|
||||
pub mod toggleable {
|
||||
#[allow(deprecated)]
|
||||
use super::{OutputPin, StatefulOutputPin, ToggleableOutputPin};
|
||||
|
||||
/// Software-driven `toggle()` implementation.
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
#[allow(deprecated)]
|
||||
pub trait Default: OutputPin + StatefulOutputPin {}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<P> ToggleableOutputPin for P
|
||||
where
|
||||
P: Default,
|
||||
{
|
||||
/// Toggle pin output
|
||||
fn toggle(&mut self) {
|
||||
if self.is_set_low() {
|
||||
self.set_high();
|
||||
} else {
|
||||
self.set_low();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Single digital input pin
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
///
|
||||
/// *This version of the trait is now deprecated. Please use the new `InputPin` trait in
|
||||
/// `digital::v2::InputPin`*.
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait InputPin {
|
||||
/// Is the input pin high?
|
||||
fn is_high(&self) -> bool;
|
||||
|
||||
/// Is the input pin low?
|
||||
fn is_low(&self) -> bool;
|
||||
}
|
||||
+298
@@ -0,0 +1,298 @@
|
||||
//! v1 compatibility wrappers
|
||||
//!
|
||||
//! This module provides wrappers to support use of v2 implementations with
|
||||
//! v1 consumers. v2 traits must be explicitly cast to the v1 version using
|
||||
//! `.into()`, and will panic on internal errors
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate embedded_hal;
|
||||
//! use embedded_hal::digital::{v1, v2, v1_compat::OldOutputPin};
|
||||
//!
|
||||
//! struct NewOutputPinImpl {}
|
||||
//!
|
||||
//! impl v2::OutputPin for NewOutputPinImpl {
|
||||
//! type Error = ();
|
||||
//! fn set_low(&mut self) -> Result<(), Self::Error> { Ok(()) }
|
||||
//! fn set_high(&mut self) -> Result<(), Self::Error>{ Ok(()) }
|
||||
//! }
|
||||
//!
|
||||
//! struct OldOutputPinConsumer<T: v1::OutputPin> {
|
||||
//! _pin: T,
|
||||
//! }
|
||||
//!
|
||||
//! impl <T>OldOutputPinConsumer<T>
|
||||
//! where T: v1::OutputPin {
|
||||
//! pub fn new(pin: T) -> OldOutputPinConsumer<T> {
|
||||
//! OldOutputPinConsumer{ _pin: pin }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let pin = NewOutputPinImpl{};
|
||||
//! let _consumer: OldOutputPinConsumer<OldOutputPin<_>> = OldOutputPinConsumer::new(pin.into());
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
|
||||
#[allow(deprecated)]
|
||||
use super::v1;
|
||||
use super::v2;
|
||||
|
||||
/// Wrapper to allow fallible `v2::OutputPin` traits to be converted to `v1::OutputPin` traits
|
||||
pub struct OldOutputPin<T> {
|
||||
pin: T,
|
||||
}
|
||||
|
||||
impl<T, E> OldOutputPin<T>
|
||||
where
|
||||
T: v2::OutputPin<Error = E>,
|
||||
E: core::fmt::Debug,
|
||||
{
|
||||
/// Create a new OldOutputPin wrapper around a `v2::OutputPin`
|
||||
pub fn new(pin: T) -> Self {
|
||||
Self { pin }
|
||||
}
|
||||
|
||||
/// Fetch a reference to the inner `v2::OutputPin` impl
|
||||
#[cfg(test)]
|
||||
fn inner(&self) -> &T {
|
||||
&self.pin
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> From<T> for OldOutputPin<T>
|
||||
where
|
||||
T: v2::OutputPin<Error = E>,
|
||||
E: core::fmt::Debug,
|
||||
{
|
||||
fn from(pin: T) -> Self {
|
||||
OldOutputPin { pin }
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `v1::OutputPin` trait for fallible `v2::OutputPin` output pins
|
||||
/// where errors will panic.
|
||||
#[allow(deprecated)]
|
||||
impl<T, E> v1::OutputPin for OldOutputPin<T>
|
||||
where
|
||||
T: v2::OutputPin<Error = E>,
|
||||
E: core::fmt::Debug,
|
||||
{
|
||||
fn set_low(&mut self) {
|
||||
self.pin.set_low().unwrap()
|
||||
}
|
||||
|
||||
fn set_high(&mut self) {
|
||||
self.pin.set_high().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `v1::StatefulOutputPin` trait for `v2::StatefulOutputPin` fallible pins
|
||||
/// where errors will panic.
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
impl<T, E> v1::StatefulOutputPin for OldOutputPin<T>
|
||||
where
|
||||
T: v2::StatefulOutputPin<Error = E>,
|
||||
E: core::fmt::Debug,
|
||||
{
|
||||
fn is_set_low(&self) -> bool {
|
||||
self.pin.is_set_low().unwrap()
|
||||
}
|
||||
|
||||
fn is_set_high(&self) -> bool {
|
||||
self.pin.is_set_high().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper to allow fallible `v2::InputPin` traits to be converted to `v1::InputPin` traits
|
||||
/// where errors will panic.
|
||||
#[cfg(feature = "unproven")]
|
||||
pub struct OldInputPin<T> {
|
||||
pin: T,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
impl<T, E> OldInputPin<T>
|
||||
where
|
||||
T: v2::InputPin<Error = E>,
|
||||
E: core::fmt::Debug,
|
||||
{
|
||||
/// Create an `OldInputPin` wrapper around a `v2::InputPin`.
|
||||
pub fn new(pin: T) -> Self {
|
||||
Self { pin }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
impl<T, E> From<T> for OldInputPin<T>
|
||||
where
|
||||
T: v2::InputPin<Error = E>,
|
||||
E: core::fmt::Debug,
|
||||
{
|
||||
fn from(pin: T) -> Self {
|
||||
OldInputPin { pin }
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `v1::InputPin` trait for `v2::InputPin` fallible pins
|
||||
/// where errors will panic.
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
impl<T, E> v1::InputPin for OldInputPin<T>
|
||||
where
|
||||
T: v2::InputPin<Error = E>,
|
||||
E: core::fmt::Debug,
|
||||
{
|
||||
fn is_low(&self) -> bool {
|
||||
self.pin.is_low().unwrap()
|
||||
}
|
||||
|
||||
fn is_high(&self) -> bool {
|
||||
self.pin.is_high().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(deprecated)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[allow(deprecated)]
|
||||
use crate::digital::v1;
|
||||
use crate::digital::v2;
|
||||
|
||||
use crate::digital::v1::OutputPin;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct NewOutputPinImpl {
|
||||
state: bool,
|
||||
res: Result<(), ()>,
|
||||
}
|
||||
|
||||
impl v2::OutputPin for NewOutputPinImpl {
|
||||
type Error = ();
|
||||
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
self.state = false;
|
||||
self.res
|
||||
}
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
self.state = true;
|
||||
self.res
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
struct OldOutputPinConsumer<T: v1::OutputPin> {
|
||||
_pin: T,
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<T> OldOutputPinConsumer<T>
|
||||
where
|
||||
T: v1::OutputPin,
|
||||
{
|
||||
pub fn new(pin: T) -> OldOutputPinConsumer<T> {
|
||||
OldOutputPinConsumer { _pin: pin }
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v1_v2_output_explicit() {
|
||||
let i = NewOutputPinImpl {
|
||||
state: false,
|
||||
res: Ok(()),
|
||||
};
|
||||
let _c: OldOutputPinConsumer<OldOutputPin<_>> = OldOutputPinConsumer::new(i.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v1_v2_output_state() {
|
||||
let mut o: OldOutputPin<_> = NewOutputPinImpl {
|
||||
state: false,
|
||||
res: Ok(()),
|
||||
}
|
||||
.into();
|
||||
|
||||
o.set_high();
|
||||
assert_eq!(o.inner().state, true);
|
||||
|
||||
o.set_low();
|
||||
assert_eq!(o.inner().state, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn v1_v2_output_panic() {
|
||||
let mut o: OldOutputPin<_> = NewOutputPinImpl {
|
||||
state: false,
|
||||
res: Err(()),
|
||||
}
|
||||
.into();
|
||||
|
||||
o.set_high();
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
use crate::digital::v1::InputPin;
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
struct NewInputPinImpl {
|
||||
state: Result<bool, ()>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
impl v2::InputPin for NewInputPinImpl {
|
||||
type Error = ();
|
||||
|
||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||
self.state.map(|v| v == false)
|
||||
}
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
self.state.map(|v| v == true)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
struct OldInputPinConsumer<T: v1::InputPin> {
|
||||
_pin: T,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
impl<T> OldInputPinConsumer<T>
|
||||
where
|
||||
T: v1::InputPin,
|
||||
{
|
||||
pub fn new(pin: T) -> OldInputPinConsumer<T> {
|
||||
OldInputPinConsumer { _pin: pin }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[test]
|
||||
fn v1_v2_input_explicit() {
|
||||
let i = NewInputPinImpl { state: Ok(false) };
|
||||
let _c: OldInputPinConsumer<OldInputPin<_>> = OldInputPinConsumer::new(i.into());
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[test]
|
||||
fn v1_v2_input_state() {
|
||||
let i: OldInputPin<_> = NewInputPinImpl { state: Ok(false) }.into();
|
||||
|
||||
assert_eq!(i.is_low(), true);
|
||||
assert_eq!(i.is_high(), false);
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn v1_v2_input_panic() {
|
||||
let i: OldInputPin<_> = NewInputPinImpl { state: Err(()) }.into();
|
||||
|
||||
i.is_low();
|
||||
}
|
||||
}
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
//! Digital I/O
|
||||
//!
|
||||
//! Version 2 / fallible traits. Infallible implementations should set Error to `!`.
|
||||
|
||||
use core::{convert::From, ops::Not};
|
||||
|
||||
/// Digital output pin state
|
||||
///
|
||||
/// Conversion from `bool` and logical negation are also implemented
|
||||
/// for this type.
|
||||
/// ```rust
|
||||
/// # use embedded_hal::digital::v2::PinState;
|
||||
/// let state = PinState::from(false);
|
||||
/// assert_eq!(state, PinState::Low);
|
||||
/// assert_eq!(!state, PinState::High);
|
||||
/// ```
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum PinState {
|
||||
/// Low pin state
|
||||
Low,
|
||||
/// High pin state
|
||||
High,
|
||||
}
|
||||
|
||||
impl From<bool> for PinState {
|
||||
fn from(value: bool) -> Self {
|
||||
match value {
|
||||
false => PinState::Low,
|
||||
true => PinState::High,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Not for PinState {
|
||||
type Output = PinState;
|
||||
|
||||
fn not(self) -> Self::Output {
|
||||
match self {
|
||||
PinState::High => PinState::Low,
|
||||
PinState::Low => PinState::High,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Single digital push-pull output pin
|
||||
pub trait OutputPin {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Drives the pin low
|
||||
///
|
||||
/// *NOTE* the actual electrical state of the pin may not actually be low, e.g. due to external
|
||||
/// electrical sources
|
||||
fn set_low(&mut self) -> Result<(), Self::Error>;
|
||||
|
||||
/// Drives the pin high
|
||||
///
|
||||
/// *NOTE* the actual electrical state of the pin may not actually be high, e.g. due to external
|
||||
/// electrical sources
|
||||
fn set_high(&mut self) -> Result<(), Self::Error>;
|
||||
|
||||
/// Drives the pin high or low depending on the provided value
|
||||
///
|
||||
/// *NOTE* the actual electrical state of the pin may not actually be high or low, e.g. due to external
|
||||
/// electrical sources
|
||||
fn set_state(&mut self, state: PinState) -> Result<(), Self::Error> {
|
||||
match state {
|
||||
PinState::Low => self.set_low(),
|
||||
PinState::High => self.set_high(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Push-pull output pin that can read its output state
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait StatefulOutputPin: OutputPin {
|
||||
/// Is the pin in drive high mode?
|
||||
///
|
||||
/// *NOTE* this does *not* read the electrical state of the pin
|
||||
fn is_set_high(&self) -> Result<bool, Self::Error>;
|
||||
|
||||
/// Is the pin in drive low mode?
|
||||
///
|
||||
/// *NOTE* this does *not* read the electrical state of the pin
|
||||
fn is_set_low(&self) -> Result<bool, Self::Error>;
|
||||
}
|
||||
|
||||
/// Output pin that can be toggled
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
///
|
||||
/// See [toggleable](toggleable) to use a software implementation if
|
||||
/// both [OutputPin](trait.OutputPin.html) and
|
||||
/// [StatefulOutputPin](trait.StatefulOutputPin.html) are
|
||||
/// implemented. Otherwise, implement this using hardware mechanisms.
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait ToggleableOutputPin {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Toggle pin output.
|
||||
fn toggle(&mut self) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// If you can read **and** write the output state, a pin is
|
||||
/// toggleable by software.
|
||||
///
|
||||
/// ```
|
||||
/// use embedded_hal::digital::v2::{OutputPin, StatefulOutputPin, ToggleableOutputPin};
|
||||
/// use embedded_hal::digital::v2::toggleable;
|
||||
///
|
||||
/// /// A virtual output pin that exists purely in software
|
||||
/// struct MyPin {
|
||||
/// state: bool
|
||||
/// }
|
||||
///
|
||||
/// impl OutputPin for MyPin {
|
||||
/// type Error = void::Void;
|
||||
///
|
||||
/// fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
/// self.state = false;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
/// self.state = true;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl StatefulOutputPin for MyPin {
|
||||
/// fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||
/// Ok(!self.state)
|
||||
/// }
|
||||
/// fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||
/// Ok(self.state)
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// /// Opt-in to the software implementation.
|
||||
/// impl toggleable::Default for MyPin {}
|
||||
///
|
||||
/// let mut pin = MyPin { state: false };
|
||||
/// pin.toggle().unwrap();
|
||||
/// assert!(pin.is_set_high().unwrap());
|
||||
/// pin.toggle().unwrap();
|
||||
/// assert!(pin.is_set_low().unwrap());
|
||||
/// ```
|
||||
#[cfg(feature = "unproven")]
|
||||
pub mod toggleable {
|
||||
use super::{OutputPin, StatefulOutputPin, ToggleableOutputPin};
|
||||
|
||||
/// Software-driven `toggle()` implementation.
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
pub trait Default: OutputPin + StatefulOutputPin {}
|
||||
|
||||
impl<P> ToggleableOutputPin for P
|
||||
where
|
||||
P: Default,
|
||||
{
|
||||
type Error = P::Error;
|
||||
|
||||
/// Toggle pin output
|
||||
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||
if self.is_set_low()? {
|
||||
self.set_high()
|
||||
} else {
|
||||
self.set_low()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Single digital input pin
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait InputPin {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Is the input pin high?
|
||||
fn is_high(&self) -> Result<bool, Self::Error>;
|
||||
|
||||
/// Is the input pin low?
|
||||
fn is_low(&self) -> Result<bool, Self::Error>;
|
||||
}
|
||||
|
||||
/// Single pin that can switch from input to output mode, and vice-versa.
|
||||
///
|
||||
/// Example use (assumes the `Error` type is the same for the `IoPin`,
|
||||
/// `InputPin`, and `OutputPin`):
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait IoPin<TInput, TOutput>
|
||||
where
|
||||
TInput: InputPin + IoPin<TInput, TOutput>,
|
||||
TOutput: OutputPin + IoPin<TInput, TOutput>,
|
||||
{
|
||||
/// Error type.
|
||||
type Error;
|
||||
|
||||
/// Tries to convert this pin to input mode.
|
||||
///
|
||||
/// If the pin is already in input mode, this method should succeed.
|
||||
fn into_input_pin(self) -> Result<TInput, Self::Error>;
|
||||
|
||||
/// Tries to convert this pin to output mode with the given initial state.
|
||||
///
|
||||
/// If the pin is already in the requested state, this method should
|
||||
/// succeed.
|
||||
fn into_output_pin(self, state: PinState) -> Result<TOutput, Self::Error>;
|
||||
}
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
//! v2 compatibility shims
|
||||
//!
|
||||
//! This module adds implicit forward support to v1 digital traits,
|
||||
//! allowing v1 implementations to be directly used with v2 consumers.
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate embedded_hal;
|
||||
//! use embedded_hal::digital::{v1, v2};
|
||||
//!
|
||||
//! struct OldOutputPinImpl { }
|
||||
//!
|
||||
//! impl v1::OutputPin for OldOutputPinImpl {
|
||||
//! fn set_low(&mut self) { }
|
||||
//! fn set_high(&mut self) { }
|
||||
//! }
|
||||
//!
|
||||
//! struct NewOutputPinConsumer<T: v2::OutputPin> {
|
||||
//! _pin: T,
|
||||
//! }
|
||||
//!
|
||||
//! impl <T>NewOutputPinConsumer<T>
|
||||
//! where T: v2::OutputPin {
|
||||
//! pub fn new(pin: T) -> NewOutputPinConsumer<T> {
|
||||
//! NewOutputPinConsumer{ _pin: pin }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let pin = OldOutputPinImpl{};
|
||||
//! let _consumer = NewOutputPinConsumer::new(pin);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
|
||||
#[allow(deprecated)]
|
||||
use super::v1;
|
||||
use super::v2;
|
||||
|
||||
/// Implementation of fallible `v2::OutputPin` for `v1::OutputPin` traits
|
||||
#[allow(deprecated)]
|
||||
impl<T> v2::OutputPin for T
|
||||
where
|
||||
T: v1::OutputPin,
|
||||
{
|
||||
// TODO: update to ! when never_type is stabilized
|
||||
type Error = ();
|
||||
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(self.set_low())
|
||||
}
|
||||
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(self.set_high())
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of fallible `v2::StatefulOutputPin` for `v1::StatefulOutputPin` digital traits
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
impl<T> v2::StatefulOutputPin for T
|
||||
where
|
||||
T: v1::StatefulOutputPin + v1::OutputPin,
|
||||
{
|
||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_set_low())
|
||||
}
|
||||
|
||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_set_high())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
impl<T> v2::toggleable::Default for T where T: v1::toggleable::Default {}
|
||||
|
||||
/// Implementation of fallible `v2::InputPin` for `v1::InputPin` digital traits
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
impl<T> v2::InputPin for T
|
||||
where
|
||||
T: v1::InputPin,
|
||||
{
|
||||
// TODO: update to ! when never_type is stabilized
|
||||
type Error = ();
|
||||
|
||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_low())
|
||||
}
|
||||
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_high())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
#[allow(deprecated)]
|
||||
use crate::digital::v1;
|
||||
use crate::digital::v2;
|
||||
|
||||
#[allow(deprecated)]
|
||||
struct OldOutputPinImpl {
|
||||
state: bool,
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl v1::OutputPin for OldOutputPinImpl {
|
||||
fn set_low(&mut self) {
|
||||
self.state = false;
|
||||
}
|
||||
fn set_high(&mut self) {
|
||||
self.state = true;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[cfg(feature = "unproven")]
|
||||
impl v1::StatefulOutputPin for OldOutputPinImpl {
|
||||
fn is_set_low(&self) -> bool {
|
||||
self.state == false
|
||||
}
|
||||
|
||||
fn is_set_high(&self) -> bool {
|
||||
self.state == true
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[cfg(feature = "unproven")]
|
||||
impl v1::toggleable::Default for OldOutputPinImpl {}
|
||||
|
||||
struct NewOutputPinConsumer<T: v2::OutputPin> {
|
||||
_pin: T,
|
||||
}
|
||||
|
||||
impl<T> NewOutputPinConsumer<T>
|
||||
where
|
||||
T: v2::OutputPin,
|
||||
{
|
||||
pub fn new(pin: T) -> NewOutputPinConsumer<T> {
|
||||
NewOutputPinConsumer { _pin: pin }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
struct NewToggleablePinConsumer<T: v2::ToggleableOutputPin> {
|
||||
_pin: T,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
impl<T> NewToggleablePinConsumer<T>
|
||||
where
|
||||
T: v2::ToggleableOutputPin,
|
||||
{
|
||||
pub fn new(pin: T) -> NewToggleablePinConsumer<T> {
|
||||
NewToggleablePinConsumer { _pin: pin }
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unproven")]
|
||||
fn v2_v1_toggleable_implicit() {
|
||||
let i = OldOutputPinImpl { state: false };
|
||||
let _c = NewToggleablePinConsumer::new(i);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v2_v1_output_implicit() {
|
||||
let i = OldOutputPinImpl { state: false };
|
||||
let _c = NewOutputPinConsumer::new(i);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v2_v1_output_state() {
|
||||
let mut o = OldOutputPinImpl { state: false };
|
||||
|
||||
v2::OutputPin::set_high(&mut o).unwrap();
|
||||
assert_eq!(o.state, true);
|
||||
|
||||
v2::OutputPin::set_low(&mut o).unwrap();
|
||||
assert_eq!(o.state, false);
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
struct OldInputPinImpl {
|
||||
state: bool,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
impl v1::InputPin for OldInputPinImpl {
|
||||
fn is_low(&self) -> bool {
|
||||
!self.state
|
||||
}
|
||||
fn is_high(&self) -> bool {
|
||||
self.state
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
struct NewInputPinConsumer<T: v2::InputPin> {
|
||||
_pin: T,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
impl<T> NewInputPinConsumer<T>
|
||||
where
|
||||
T: v2::InputPin,
|
||||
{
|
||||
pub fn new(pin: T) -> NewInputPinConsumer<T> {
|
||||
NewInputPinConsumer { _pin: pin }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[test]
|
||||
#[cfg(feature = "unproven")]
|
||||
fn v2_v1_input_implicit() {
|
||||
let i = OldInputPinImpl { state: false };
|
||||
let _c = NewInputPinConsumer::new(i);
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[test]
|
||||
fn v2_v1_input_state() {
|
||||
let mut i = OldInputPinImpl { state: false };
|
||||
|
||||
assert_eq!(v2::InputPin::is_high(&mut i).unwrap(), false);
|
||||
assert_eq!(v2::InputPin::is_low(&mut i).unwrap(), true);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user