use core::marker::PhantomData;
use embedded_hal::digital::{OutputPin, StatefulOutputPin};
use crate::polarity::{Polarity, PolarityMode};
pub struct Led<P, POL = crate::ActiveHigh> {
pin: P,
_polarity: PhantomData<POL>,
}
impl<P, POL> Led<P, POL> {
#[inline]
pub fn from_pin(pin: P) -> Self {
Self {
pin,
_polarity: PhantomData,
}
}
}
impl<P: OutputPin, POL: Polarity> Led<P, POL> {
pub fn new(mut pin: P) -> Result<Self, P::Error> {
pin.set_state(POL::physical_off())?;
Ok(Self {
pin,
_polarity: PhantomData,
})
}
#[inline]
pub fn on(&mut self) -> Result<(), P::Error> {
self.pin.set_state(POL::physical_on())
}
#[inline]
pub fn off(&mut self) -> Result<(), P::Error> {
self.pin.set_state(POL::physical_off())
}
#[inline]
pub fn set(&mut self, state: bool) -> Result<(), P::Error> {
if state { self.on() } else { self.off() }
}
}
impl<P: StatefulOutputPin, POL: Polarity> Led<P, POL> {
#[inline]
pub fn is_on(&mut self) -> Result<bool, P::Error> {
self.pin.is_set_high().map(|h| POL::is_logical_on(h))
}
#[inline]
pub fn is_off(&mut self) -> Result<bool, P::Error> {
self.is_on().map(|on| !on)
}
#[inline]
pub fn toggle(&mut self) -> Result<(), P::Error> {
self.pin.toggle()
}
}
impl<P, POL: Polarity> Led<P, POL> {
#[inline]
pub fn release(self) -> P {
self.pin
}
pub fn into_flex(self) -> FlexLed<P> {
FlexLed {
pin: self.pin,
polarity: POL::MODE,
}
}
}
impl<P, POL: Polarity> core::fmt::Debug for Led<P, POL> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Led")
.field("polarity", &core::any::type_name::<POL>())
.finish_non_exhaustive()
}
}
#[cfg(feature = "defmt")]
impl<P, POL: Polarity> defmt::Format for Led<P, POL> {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(
fmt,
"Led {{ polarity: {} }}",
defmt::Display2Format(core::any::type_name::<POL>())
)
}
}
pub struct FlexLed<P> {
pin: P,
polarity: PolarityMode,
}
impl<P> FlexLed<P> {
#[inline]
pub fn from_pin(pin: P, polarity: PolarityMode) -> Self {
Self { pin, polarity }
}
}
impl<P: OutputPin> FlexLed<P> {
pub fn new(mut pin: P, polarity: PolarityMode) -> Result<Self, P::Error> {
pin.set_state(polarity.physical_off())?;
Ok(Self { pin, polarity })
}
#[inline]
pub fn on(&mut self) -> Result<(), P::Error> {
self.pin.set_state(self.polarity.physical_on())
}
#[inline]
pub fn off(&mut self) -> Result<(), P::Error> {
self.pin.set_state(self.polarity.physical_off())
}
#[inline]
pub fn set(&mut self, state: bool) -> Result<(), P::Error> {
if state { self.on() } else { self.off() }
}
}
impl<P: StatefulOutputPin> FlexLed<P> {
#[inline]
pub fn is_on(&mut self) -> Result<bool, P::Error> {
self.pin
.is_set_high()
.map(|h| self.polarity.is_logical_on(h))
}
#[inline]
pub fn is_off(&mut self) -> Result<bool, P::Error> {
self.is_on().map(|on| !on)
}
#[inline]
pub fn toggle(&mut self) -> Result<(), P::Error> {
self.pin.toggle()
}
}
impl<P> FlexLed<P> {
#[inline]
pub fn polarity(&self) -> PolarityMode {
self.polarity
}
#[inline]
pub fn release(self) -> P {
self.pin
}
}
impl<P, POL: Polarity> From<Led<P, POL>> for FlexLed<P> {
fn from(led: Led<P, POL>) -> Self {
led.into_flex()
}
}
#[cfg(test)]
mod tests {
use super::*;
use embedded_hal_mock::eh1::digital::{
Mock as PinMock, State as EState, Transaction as PinTrans,
};
#[test]
fn new_active_high_sets_off() {
let e = [PinTrans::set(EState::Low)];
Led::<_, crate::ActiveHigh>::new(PinMock::new(&e))
.unwrap()
.release()
.done();
}
#[test]
fn new_active_low_sets_off() {
let e = [PinTrans::set(EState::High)];
Led::<_, crate::ActiveLow>::new(PinMock::new(&e))
.unwrap()
.release()
.done();
}
#[test]
fn from_pin_no_touch() {
Led::<_, crate::ActiveHigh>::from_pin(PinMock::new(&[]))
.release()
.done();
}
#[test]
fn active_high_on_off() {
let e = [
PinTrans::set(EState::Low),
PinTrans::set(EState::High),
PinTrans::set(EState::Low),
];
let mut led = Led::<_, crate::ActiveHigh>::new(PinMock::new(&e)).unwrap();
led.on().unwrap();
led.off().unwrap();
led.release().done();
}
#[test]
fn active_low_on_off() {
let e = [
PinTrans::set(EState::High),
PinTrans::set(EState::Low),
PinTrans::set(EState::High),
];
let mut led = Led::<_, crate::ActiveLow>::new(PinMock::new(&e)).unwrap();
led.on().unwrap();
led.off().unwrap();
led.release().done();
}
#[test]
fn set_state() {
let e = [
PinTrans::set(EState::Low),
PinTrans::set(EState::High),
PinTrans::set(EState::Low),
];
let mut led = Led::<_, crate::ActiveHigh>::new(PinMock::new(&e)).unwrap();
led.set(true).unwrap();
led.set(false).unwrap();
led.release().done();
}
#[test]
fn toggle() {
let e = [PinTrans::set(EState::Low), PinTrans::toggle()];
let mut led = Led::<_, crate::ActiveHigh>::new(PinMock::new(&e)).unwrap();
led.toggle().unwrap();
led.release().done();
}
#[test]
fn is_on_active_high() {
let e = [PinTrans::set(EState::Low), PinTrans::get_state(EState::Low)];
let mut led = Led::<_, crate::ActiveHigh>::new(PinMock::new(&e)).unwrap();
assert!(!led.is_on().unwrap());
led.release().done();
}
#[test]
fn is_on_active_low() {
let e = [
PinTrans::set(EState::High),
PinTrans::get_state(EState::High),
];
let mut led = Led::<_, crate::ActiveLow>::new(PinMock::new(&e)).unwrap();
assert!(!led.is_on().unwrap());
led.release().done();
}
#[test]
fn flex_new_active_low() {
let e = [PinTrans::set(EState::High), PinTrans::set(EState::Low)];
let mut led = FlexLed::new(PinMock::new(&e), PolarityMode::ActiveLow).unwrap();
led.on().unwrap();
led.release().done();
}
#[test]
fn flex_from_pin_no_touch() {
let led = FlexLed::from_pin(PinMock::new(&[]), PolarityMode::ActiveLow);
assert_eq!(led.polarity(), PolarityMode::ActiveLow);
led.release().done();
}
#[test]
fn flex_toggle() {
let e = [PinTrans::set(EState::Low), PinTrans::toggle()];
let mut led = FlexLed::new(PinMock::new(&e), PolarityMode::ActiveHigh).unwrap();
led.toggle().unwrap();
led.release().done();
}
#[test]
fn flex_is_on() {
let e = [PinTrans::set(EState::Low), PinTrans::get_state(EState::Low)];
let mut led = FlexLed::new(PinMock::new(&e), PolarityMode::ActiveHigh).unwrap();
assert!(!led.is_on().unwrap());
led.release().done();
}
#[test]
fn into_flex_preserves_polarity() {
let led = Led::<_, crate::ActiveLow>::from_pin(PinMock::new(&[]));
let flex: FlexLed<_> = led.into_flex();
assert_eq!(flex.polarity(), PolarityMode::ActiveLow);
flex.release().done();
}
#[test]
fn from_led_to_flex() {
let led = Led::<_, crate::ActiveLow>::from_pin(PinMock::new(&[]));
let flex = FlexLed::from(led);
assert_eq!(flex.polarity(), PolarityMode::ActiveLow);
flex.release().done();
}
}