use alloc::boxed::Box;
use core::marker::PhantomData;
use embassy_rp::{
Peri,
gpio::{AnyPin, Output, Level},
};
mod private {
pub trait Sealed {}
}
pub trait LedRole: private::Sealed + 'static + Send {}
macro_rules! role {
($($n:ident),* $(,)?) => {
$(
pub struct $n;
impl private::Sealed for $n {}
impl LedRole for $n {}
)*
};
}
role!(Generic);
pub trait Led<R: LedRole>: Send {
fn on(&mut self);
fn off(&mut self);
fn set(&mut self, on: bool);
fn toggle(&mut self);
fn is_on(&self) -> bool;
}
pub struct SingleLed<R: LedRole> {
pin: Output<'static>,
active_low: bool,
_role: PhantomData<R>,
}
impl<R: LedRole> SingleLed<R> {
pub fn new(pin: Peri<'static, AnyPin>, active_low: bool) -> Self {
let initial_level = if active_low { Level::High } else { Level::Low };
Self {
pin: Output::new(pin, initial_level),
active_low,
_role: PhantomData,
}
}
}
impl<R: LedRole> Led<R> for SingleLed<R> {
fn on(&mut self) {
let level = if self.active_low { Level::Low } else { Level::High };
self.pin.set_level(level);
}
fn off(&mut self) {
let level = if self.active_low { Level::High } else { Level::Low };
self.pin.set_level(level);
}
fn set(&mut self, on: bool) {
if on {
self.on();
} else {
self.off();
}
}
fn toggle(&mut self) {
self.pin.toggle();
}
fn is_on(&self) -> bool {
let is_high = self.pin.is_set_high();
if self.active_low {
!is_high
} else {
is_high
}
}
}
pub fn pin_led<R: LedRole>(
pin: Peri<'static, AnyPin>,
active_low: bool,
) -> Box<dyn Led<R>> {
Box::new(SingleLed::<R>::new(pin, active_low))
}